aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-01-30 06:12:27 -0500
committerTakashi Iwai <tiwai@suse.de>2014-01-30 06:12:27 -0500
commit77d531bfdaec60001812047737c092727f076318 (patch)
treee6cf09201af15aafd2dc4e53d76835ec69183fa7 /sound/pci
parentcd262518a3ae4465e8e51c29641d98c4ed0651a1 (diff)
parent3f49a66f6ceff1c87b49858644771c17763902ab (diff)
Merge branch 'xonar-dg' of git://git.alsa-project.org/alsa-kprivate into for-next
This completes the hardware support for the Asus Xonar DG/DGX cards, and makes them actually usable. This is v4 of Roman's patch set with some small formatting changes.
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/oxygen/Makefile2
-rw-r--r--sound/pci/oxygen/cs4245.h7
-rw-r--r--sound/pci/oxygen/oxygen.h2
-rw-r--r--sound/pci/oxygen/oxygen_io.c25
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c1
-rw-r--r--sound/pci/oxygen/oxygen_regs.h1
-rw-r--r--sound/pci/oxygen/xonar_dg.c651
-rw-r--r--sound/pci/oxygen/xonar_dg.h48
-rw-r--r--sound/pci/oxygen/xonar_dg_mixer.c477
9 files changed, 713 insertions, 501 deletions
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
index 0f8726551fde..8f4c409f7e45 100644
--- a/sound/pci/oxygen/Makefile
+++ b/sound/pci/oxygen/Makefile
@@ -1,5 +1,5 @@
1snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o 1snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
2snd-oxygen-objs := oxygen.o xonar_dg.o 2snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o
3snd-virtuoso-objs := virtuoso.o xonar_lib.o \ 3snd-virtuoso-objs := virtuoso.o xonar_lib.o \
4 xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o 4 xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
5 5
diff --git a/sound/pci/oxygen/cs4245.h b/sound/pci/oxygen/cs4245.h
index 5e0197e07dd1..99098657695a 100644
--- a/sound/pci/oxygen/cs4245.h
+++ b/sound/pci/oxygen/cs4245.h
@@ -102,6 +102,9 @@
102#define CS4245_ADC_OVFL 0x02 102#define CS4245_ADC_OVFL 0x02
103#define CS4245_ADC_UNDRFL 0x01 103#define CS4245_ADC_UNDRFL 0x01
104 104
105#define CS4245_SPI_ADDRESS_S (0x9e << 16)
106#define CS4245_SPI_WRITE_S (0 << 16)
105 107
106#define CS4245_SPI_ADDRESS (0x9e << 16) 108#define CS4245_SPI_ADDRESS 0x9e
107#define CS4245_SPI_WRITE (0 << 16) 109#define CS4245_SPI_WRITE 0
110#define CS4245_SPI_READ 1
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index 09a24b24958b..c10ab077afd8 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -198,7 +198,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
198void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, 198void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
199 unsigned int index, u16 data, u16 mask); 199 unsigned int index, u16 data, u16 mask);
200 200
201void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); 201int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
202void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data); 202void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
203 203
204void oxygen_reset_uart(struct oxygen *chip); 204void oxygen_reset_uart(struct oxygen *chip);
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 521eae458348..3274907189fe 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -194,23 +194,36 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
194} 194}
195EXPORT_SYMBOL(oxygen_write_ac97_masked); 195EXPORT_SYMBOL(oxygen_write_ac97_masked);
196 196
197void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) 197static int oxygen_wait_spi(struct oxygen *chip)
198{ 198{
199 unsigned int count; 199 unsigned int count;
200 200
201 /* should not need more than 30.72 us (24 * 1.28 us) */ 201 /*
202 count = 10; 202 * Higher timeout to be sure: 200 us;
203 while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) 203 * actual transaction should not need more than 40 us.
204 && count > 0) { 204 */
205 for (count = 50; count > 0; count--) {
205 udelay(4); 206 udelay(4);
206 --count; 207 if ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) &
208 OXYGEN_SPI_BUSY) == 0)
209 return 0;
207 } 210 }
211 snd_printk(KERN_ERR "oxygen: SPI wait timeout\n");
212 return -EIO;
213}
208 214
215int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
216{
217 /*
218 * We need to wait AFTER initiating the SPI transaction,
219 * otherwise read operations will not work.
220 */
209 oxygen_write8(chip, OXYGEN_SPI_DATA1, data); 221 oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
210 oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); 222 oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
211 if (control & OXYGEN_SPI_DATA_LENGTH_3) 223 if (control & OXYGEN_SPI_DATA_LENGTH_3)
212 oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); 224 oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
213 oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); 225 oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
226 return oxygen_wait_spi(chip);
214} 227}
215EXPORT_SYMBOL(oxygen_write_spi); 228EXPORT_SYMBOL(oxygen_write_spi);
216 229
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index c0dbb52d45be..5988e044c519 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -190,6 +190,7 @@ void oxygen_update_dac_routing(struct oxygen *chip)
190 if (chip->model.update_center_lfe_mix) 190 if (chip->model.update_center_lfe_mix)
191 chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2); 191 chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2);
192} 192}
193EXPORT_SYMBOL(oxygen_update_dac_routing);
193 194
194static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 195static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
195{ 196{
diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h
index 63dc7a0ab555..8c191badaae8 100644
--- a/sound/pci/oxygen/oxygen_regs.h
+++ b/sound/pci/oxygen/oxygen_regs.h
@@ -318,6 +318,7 @@
318#define OXYGEN_PLAY_MUTE23 0x0002 318#define OXYGEN_PLAY_MUTE23 0x0002
319#define OXYGEN_PLAY_MUTE45 0x0004 319#define OXYGEN_PLAY_MUTE45 0x0004
320#define OXYGEN_PLAY_MUTE67 0x0008 320#define OXYGEN_PLAY_MUTE67 0x0008
321#define OXYGEN_PLAY_MUTE_MASK 0x000f
321#define OXYGEN_PLAY_MULTICH_MASK 0x0010 322#define OXYGEN_PLAY_MULTICH_MASK 0x0010
322#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000 323#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000
323#define OXYGEN_PLAY_MULTICH_AC97 0x0010 324#define OXYGEN_PLAY_MULTICH_AC97 0x0010
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c
index 77acd790ea47..ed6f199f8a38 100644
--- a/sound/pci/oxygen/xonar_dg.c
+++ b/sound/pci/oxygen/xonar_dg.c
@@ -2,7 +2,7 @@
2 * card driver for the Xonar DG/DGX 2 * card driver for the Xonar DG/DGX
3 * 3 *
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 * 5 * Copyright (c) Roman Volkov <v1ron@mail.ru>
6 * 6 *
7 * This driver is free software; you can redistribute it and/or modify 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. 8 * it under the terms of the GNU General Public License, version 2.
@@ -20,27 +20,35 @@
20 * Xonar DG/DGX 20 * Xonar DG/DGX
21 * ------------ 21 * ------------
22 * 22 *
23 * CS4245 and CS4361 both will mute all outputs if any clock ratio
24 * is invalid.
25 *
23 * CMI8788: 26 * CMI8788:
24 * 27 *
25 * SPI 0 -> CS4245 28 * SPI 0 -> CS4245
26 * 29 *
30 * Playback:
27 * I²S 1 -> CS4245 31 * I²S 1 -> CS4245
28 * I²S 2 -> CS4361 (center/LFE) 32 * I²S 2 -> CS4361 (center/LFE)
29 * I²S 3 -> CS4361 (surround) 33 * I²S 3 -> CS4361 (surround)
30 * I²S 4 -> CS4361 (front) 34 * I²S 4 -> CS4361 (front)
35 * Capture:
36 * I²S ADC 1 <- CS4245
31 * 37 *
32 * GPIO 3 <- ? 38 * GPIO 3 <- ?
33 * GPIO 4 <- headphone detect 39 * GPIO 4 <- headphone detect
34 * GPIO 5 -> route input jack to line-in (0) or mic-in (1) 40 * GPIO 5 -> enable ADC analog circuit for the left channel
35 * GPIO 6 -> route input jack to line-in (0) or mic-in (1) 41 * GPIO 6 -> enable ADC analog circuit for the right channel
36 * GPIO 7 -> enable rear headphone amp 42 * GPIO 7 -> switch green rear output jack between CS4245 and and the first
43 * channel of CS4361 (mechanical relay)
37 * GPIO 8 -> enable output to speakers 44 * GPIO 8 -> enable output to speakers
38 * 45 *
39 * CS4245: 46 * CS4245:
40 * 47 *
48 * input 0 <- mic
41 * input 1 <- aux 49 * input 1 <- aux
42 * input 2 <- front mic 50 * input 2 <- front mic
43 * input 4 <- line/mic 51 * input 4 <- line
44 * DAC out -> headphones 52 * DAC out -> headphones
45 * aux out -> front panel headphones 53 * aux out -> front panel headphones
46 */ 54 */
@@ -56,553 +64,214 @@
56#include "xonar_dg.h" 64#include "xonar_dg.h"
57#include "cs4245.h" 65#include "cs4245.h"
58 66
59#define GPIO_MAGIC 0x0008 67int cs4245_write_spi(struct oxygen *chip, u8 reg)
60#define GPIO_HP_DETECT 0x0010
61#define GPIO_INPUT_ROUTE 0x0060
62#define GPIO_HP_REAR 0x0080
63#define GPIO_OUTPUT_ENABLE 0x0100
64
65struct dg {
66 unsigned int output_sel;
67 s8 input_vol[4][2];
68 unsigned int input_sel;
69 u8 hp_vol_att;
70 u8 cs4245_regs[0x11];
71};
72
73static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
74{ 68{
75 struct dg *data = chip->model_data; 69 struct dg *data = chip->model_data;
70 unsigned int packet;
76 71
77 oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | 72 packet = reg << 8;
78 OXYGEN_SPI_DATA_LENGTH_3 | 73 packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
79 OXYGEN_SPI_CLOCK_1280 | 74 packet |= data->cs4245_shadow[reg];
80 (0 << OXYGEN_SPI_CODEC_SHIFT) | 75
81 OXYGEN_SPI_CEN_LATCH_CLOCK_HI, 76 return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
82 CS4245_SPI_ADDRESS | 77 OXYGEN_SPI_DATA_LENGTH_3 |
83 CS4245_SPI_WRITE | 78 OXYGEN_SPI_CLOCK_1280 |
84 (reg << 8) | value); 79 (0 << OXYGEN_SPI_CODEC_SHIFT) |
85 data->cs4245_regs[reg] = value; 80 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
81 packet);
86} 82}
87 83
88static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) 84int cs4245_read_spi(struct oxygen *chip, u8 addr)
89{ 85{
90 struct dg *data = chip->model_data; 86 struct dg *data = chip->model_data;
87 int ret;
88
89 ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
90 OXYGEN_SPI_DATA_LENGTH_2 |
91 OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
92 OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
93 ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
94 if (ret < 0)
95 return ret;
96
97 ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
98 OXYGEN_SPI_DATA_LENGTH_2 |
99 OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
100 OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
101 (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
102 if (ret < 0)
103 return ret;
104
105 data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
91 106
92 if (value != data->cs4245_regs[reg]) 107 return 0;
93 cs4245_write(chip, reg, value);
94} 108}
95 109
96static void cs4245_registers_init(struct oxygen *chip) 110int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
97{ 111{
98 struct dg *data = chip->model_data; 112 struct dg *data = chip->model_data;
99 113 unsigned char addr;
100 cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN); 114 int ret;
101 cs4245_write(chip, CS4245_DAC_CTRL_1, 115
102 data->cs4245_regs[CS4245_DAC_CTRL_1]); 116 for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
103 cs4245_write(chip, CS4245_ADC_CTRL, 117 ret = (op == CS4245_SAVE_TO_SHADOW ?
104 data->cs4245_regs[CS4245_ADC_CTRL]); 118 cs4245_read_spi(chip, addr) :
105 cs4245_write(chip, CS4245_SIGNAL_SEL, 119 cs4245_write_spi(chip, addr));
106 data->cs4245_regs[CS4245_SIGNAL_SEL]); 120 if (ret < 0)
107 cs4245_write(chip, CS4245_PGA_B_CTRL, 121 return ret;
108 data->cs4245_regs[CS4245_PGA_B_CTRL]); 122 }
109 cs4245_write(chip, CS4245_PGA_A_CTRL, 123 return 0;
110 data->cs4245_regs[CS4245_PGA_A_CTRL]);
111 cs4245_write(chip, CS4245_ANALOG_IN,
112 data->cs4245_regs[CS4245_ANALOG_IN]);
113 cs4245_write(chip, CS4245_DAC_A_CTRL,
114 data->cs4245_regs[CS4245_DAC_A_CTRL]);
115 cs4245_write(chip, CS4245_DAC_B_CTRL,
116 data->cs4245_regs[CS4245_DAC_B_CTRL]);
117 cs4245_write(chip, CS4245_DAC_CTRL_2,
118 CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC);
119 cs4245_write(chip, CS4245_INT_MASK, 0);
120 cs4245_write(chip, CS4245_POWER_CTRL, 0);
121} 124}
122 125
123static void cs4245_init(struct oxygen *chip) 126static void cs4245_init(struct oxygen *chip)
124{ 127{
125 struct dg *data = chip->model_data; 128 struct dg *data = chip->model_data;
126 129
127 data->cs4245_regs[CS4245_DAC_CTRL_1] = 130 /* save the initial state: codec version, registers */
131 cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
132
133 /*
134 * Power up the CODEC internals, enable soft ramp & zero cross, work in
135 * async. mode, enable aux output from DAC. Invert DAC output as in the
136 * Windows driver.
137 */
138 data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
139 data->cs4245_shadow[CS4245_SIGNAL_SEL] =
140 CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
141 data->cs4245_shadow[CS4245_DAC_CTRL_1] =
128 CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST; 142 CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
129 data->cs4245_regs[CS4245_ADC_CTRL] = 143 data->cs4245_shadow[CS4245_DAC_CTRL_2] =
144 CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
145 data->cs4245_shadow[CS4245_ADC_CTRL] =
130 CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; 146 CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
131 data->cs4245_regs[CS4245_SIGNAL_SEL] = 147 data->cs4245_shadow[CS4245_ANALOG_IN] =
132 CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH; 148 CS4245_PGA_SOFT | CS4245_PGA_ZERO;
133 data->cs4245_regs[CS4245_PGA_B_CTRL] = 0; 149 data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
134 data->cs4245_regs[CS4245_PGA_A_CTRL] = 0; 150 data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
135 data->cs4245_regs[CS4245_ANALOG_IN] = 151 data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
136 CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4; 152 data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
137 data->cs4245_regs[CS4245_DAC_A_CTRL] = 0; 153
138 data->cs4245_regs[CS4245_DAC_B_CTRL] = 0; 154 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
139 cs4245_registers_init(chip);
140 snd_component_add(chip->card, "CS4245"); 155 snd_component_add(chip->card, "CS4245");
141} 156}
142 157
143static void dg_output_enable(struct oxygen *chip) 158void dg_init(struct oxygen *chip)
144{
145 msleep(2500);
146 oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
147}
148
149static void dg_init(struct oxygen *chip)
150{ 159{
151 struct dg *data = chip->model_data; 160 struct dg *data = chip->model_data;
152 161
153 data->output_sel = 0; 162 data->output_sel = PLAYBACK_DST_HP_FP;
154 data->input_sel = 3; 163 data->input_sel = CAPTURE_SRC_MIC;
155 data->hp_vol_att = 2 * 16;
156 164
157 cs4245_init(chip); 165 cs4245_init(chip);
158 166 oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
159 oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, 167 GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
160 GPIO_MAGIC | GPIO_HP_DETECT); 168 /* anti-pop delay, wait some time before enabling the output */
161 oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 169 msleep(2500);
162 GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE); 170 oxygen_write16(chip, OXYGEN_GPIO_DATA,
163 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, 171 GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
164 GPIO_INPUT_ROUTE | GPIO_HP_REAR);
165 dg_output_enable(chip);
166} 172}
167 173
168static void dg_cleanup(struct oxygen *chip) 174void dg_cleanup(struct oxygen *chip)
169{ 175{
170 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); 176 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
171} 177}
172 178
173static void dg_suspend(struct oxygen *chip) 179void dg_suspend(struct oxygen *chip)
174{ 180{
175 dg_cleanup(chip); 181 dg_cleanup(chip);
176} 182}
177 183
178static void dg_resume(struct oxygen *chip) 184void dg_resume(struct oxygen *chip)
179{ 185{
180 cs4245_registers_init(chip); 186 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
181 dg_output_enable(chip); 187 msleep(2500);
182} 188 oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
183
184static void set_cs4245_dac_params(struct oxygen *chip,
185 struct snd_pcm_hw_params *params)
186{
187 struct dg *data = chip->model_data;
188 u8 value;
189
190 value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
191 if (params_rate(params) <= 50000)
192 value |= CS4245_DAC_FM_SINGLE;
193 else if (params_rate(params) <= 100000)
194 value |= CS4245_DAC_FM_DOUBLE;
195 else
196 value |= CS4245_DAC_FM_QUAD;
197 cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value);
198} 189}
199 190
200static void set_cs4245_adc_params(struct oxygen *chip, 191void set_cs4245_dac_params(struct oxygen *chip,
201 struct snd_pcm_hw_params *params) 192 struct snd_pcm_hw_params *params)
202{ 193{
203 struct dg *data = chip->model_data; 194 struct dg *data = chip->model_data;
204 u8 value; 195 unsigned char dac_ctrl;
205 196 unsigned char mclk_freq;
206 value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; 197
207 if (params_rate(params) <= 50000) 198 dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
208 value |= CS4245_ADC_FM_SINGLE; 199 mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
209 else if (params_rate(params) <= 100000) 200 if (params_rate(params) <= 50000) {
210 value |= CS4245_ADC_FM_DOUBLE; 201 dac_ctrl |= CS4245_DAC_FM_SINGLE;
211 else 202 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
212 value |= CS4245_ADC_FM_QUAD; 203 } else if (params_rate(params) <= 100000) {
213 cs4245_write_cached(chip, CS4245_ADC_CTRL, value); 204 dac_ctrl |= CS4245_DAC_FM_DOUBLE;
214} 205 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
215 206 } else {
216static inline unsigned int shift_bits(unsigned int value, 207 dac_ctrl |= CS4245_DAC_FM_QUAD;
217 unsigned int shift_from, 208 mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
218 unsigned int shift_to,
219 unsigned int mask)
220{
221 if (shift_from < shift_to)
222 return (value << (shift_to - shift_from)) & mask;
223 else
224 return (value >> (shift_from - shift_to)) & mask;
225}
226
227static unsigned int adjust_dg_dac_routing(struct oxygen *chip,
228 unsigned int play_routing)
229{
230 return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
231 shift_bits(play_routing,
232 OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
233 OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
234 OXYGEN_PLAY_DAC1_SOURCE_MASK) |
235 shift_bits(play_routing,
236 OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
237 OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
238 OXYGEN_PLAY_DAC2_SOURCE_MASK) |
239 shift_bits(play_routing,
240 OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
241 OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
242 OXYGEN_PLAY_DAC3_SOURCE_MASK);
243}
244
245static int output_switch_info(struct snd_kcontrol *ctl,
246 struct snd_ctl_elem_info *info)
247{
248 static const char *const names[3] = {
249 "Speakers", "Headphones", "FP Headphones"
250 };
251
252 return snd_ctl_enum_info(info, 1, 3, names);
253}
254
255static int output_switch_get(struct snd_kcontrol *ctl,
256 struct snd_ctl_elem_value *value)
257{
258 struct oxygen *chip = ctl->private_data;
259 struct dg *data = chip->model_data;
260
261 mutex_lock(&chip->mutex);
262 value->value.enumerated.item[0] = data->output_sel;
263 mutex_unlock(&chip->mutex);
264 return 0;
265}
266
267static int output_switch_put(struct snd_kcontrol *ctl,
268 struct snd_ctl_elem_value *value)
269{
270 struct oxygen *chip = ctl->private_data;
271 struct dg *data = chip->model_data;
272 u8 reg;
273 int changed;
274
275 if (value->value.enumerated.item[0] > 2)
276 return -EINVAL;
277
278 mutex_lock(&chip->mutex);
279 changed = value->value.enumerated.item[0] != data->output_sel;
280 if (changed) {
281 data->output_sel = value->value.enumerated.item[0];
282
283 reg = data->cs4245_regs[CS4245_SIGNAL_SEL] &
284 ~CS4245_A_OUT_SEL_MASK;
285 reg |= data->output_sel == 2 ?
286 CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
287 cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
288
289 cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
290 data->output_sel ? data->hp_vol_att : 0);
291 cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
292 data->output_sel ? data->hp_vol_att : 0);
293
294 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
295 data->output_sel == 1 ? GPIO_HP_REAR : 0,
296 GPIO_HP_REAR);
297 }
298 mutex_unlock(&chip->mutex);
299 return changed;
300}
301
302static int hp_volume_offset_info(struct snd_kcontrol *ctl,
303 struct snd_ctl_elem_info *info)
304{
305 static const char *const names[3] = {
306 "< 64 ohms", "64-150 ohms", "150-300 ohms"
307 };
308
309 return snd_ctl_enum_info(info, 1, 3, names);
310}
311
312static int hp_volume_offset_get(struct snd_kcontrol *ctl,
313 struct snd_ctl_elem_value *value)
314{
315 struct oxygen *chip = ctl->private_data;
316 struct dg *data = chip->model_data;
317
318 mutex_lock(&chip->mutex);
319 if (data->hp_vol_att > 2 * 7)
320 value->value.enumerated.item[0] = 0;
321 else if (data->hp_vol_att > 0)
322 value->value.enumerated.item[0] = 1;
323 else
324 value->value.enumerated.item[0] = 2;
325 mutex_unlock(&chip->mutex);
326 return 0;
327}
328
329static int hp_volume_offset_put(struct snd_kcontrol *ctl,
330 struct snd_ctl_elem_value *value)
331{
332 static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
333 struct oxygen *chip = ctl->private_data;
334 struct dg *data = chip->model_data;
335 s8 att;
336 int changed;
337
338 if (value->value.enumerated.item[0] > 2)
339 return -EINVAL;
340 att = atts[value->value.enumerated.item[0]];
341 mutex_lock(&chip->mutex);
342 changed = att != data->hp_vol_att;
343 if (changed) {
344 data->hp_vol_att = att;
345 if (data->output_sel) {
346 cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
347 cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
348 }
349 } 209 }
350 mutex_unlock(&chip->mutex); 210 data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
351 return changed; 211 data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
352} 212 cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
353 213 cs4245_write_spi(chip, CS4245_MCLK_FREQ);
354static int input_vol_info(struct snd_kcontrol *ctl,
355 struct snd_ctl_elem_info *info)
356{
357 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
358 info->count = 2;
359 info->value.integer.min = 2 * -12;
360 info->value.integer.max = 2 * 12;
361 return 0;
362}
363
364static int input_vol_get(struct snd_kcontrol *ctl,
365 struct snd_ctl_elem_value *value)
366{
367 struct oxygen *chip = ctl->private_data;
368 struct dg *data = chip->model_data;
369 unsigned int idx = ctl->private_value;
370
371 mutex_lock(&chip->mutex);
372 value->value.integer.value[0] = data->input_vol[idx][0];
373 value->value.integer.value[1] = data->input_vol[idx][1];
374 mutex_unlock(&chip->mutex);
375 return 0;
376}
377
378static int input_vol_put(struct snd_kcontrol *ctl,
379 struct snd_ctl_elem_value *value)
380{
381 struct oxygen *chip = ctl->private_data;
382 struct dg *data = chip->model_data;
383 unsigned int idx = ctl->private_value;
384 int changed = 0;
385
386 if (value->value.integer.value[0] < 2 * -12 ||
387 value->value.integer.value[0] > 2 * 12 ||
388 value->value.integer.value[1] < 2 * -12 ||
389 value->value.integer.value[1] > 2 * 12)
390 return -EINVAL;
391 mutex_lock(&chip->mutex);
392 changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
393 data->input_vol[idx][1] != value->value.integer.value[1];
394 if (changed) {
395 data->input_vol[idx][0] = value->value.integer.value[0];
396 data->input_vol[idx][1] = value->value.integer.value[1];
397 if (idx == data->input_sel) {
398 cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
399 data->input_vol[idx][0]);
400 cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
401 data->input_vol[idx][1]);
402 }
403 }
404 mutex_unlock(&chip->mutex);
405 return changed;
406}
407
408static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
409
410static int input_sel_info(struct snd_kcontrol *ctl,
411 struct snd_ctl_elem_info *info)
412{
413 static const char *const names[4] = {
414 "Mic", "Aux", "Front Mic", "Line"
415 };
416
417 return snd_ctl_enum_info(info, 1, 4, names);
418}
419
420static int input_sel_get(struct snd_kcontrol *ctl,
421 struct snd_ctl_elem_value *value)
422{
423 struct oxygen *chip = ctl->private_data;
424 struct dg *data = chip->model_data;
425
426 mutex_lock(&chip->mutex);
427 value->value.enumerated.item[0] = data->input_sel;
428 mutex_unlock(&chip->mutex);
429 return 0;
430} 214}
431 215
432static int input_sel_put(struct snd_kcontrol *ctl, 216void set_cs4245_adc_params(struct oxygen *chip,
433 struct snd_ctl_elem_value *value) 217 struct snd_pcm_hw_params *params)
434{ 218{
435 static const u8 sel_values[4] = {
436 CS4245_SEL_MIC,
437 CS4245_SEL_INPUT_1,
438 CS4245_SEL_INPUT_2,
439 CS4245_SEL_INPUT_4
440 };
441 struct oxygen *chip = ctl->private_data;
442 struct dg *data = chip->model_data; 219 struct dg *data = chip->model_data;
443 int changed; 220 unsigned char adc_ctrl;
444 221 unsigned char mclk_freq;
445 if (value->value.enumerated.item[0] > 3) 222
446 return -EINVAL; 223 adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
447 224 mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
448 mutex_lock(&chip->mutex); 225 if (params_rate(params) <= 50000) {
449 changed = value->value.enumerated.item[0] != data->input_sel; 226 adc_ctrl |= CS4245_ADC_FM_SINGLE;
450 if (changed) { 227 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
451 data->input_sel = value->value.enumerated.item[0]; 228 } else if (params_rate(params) <= 100000) {
452 229 adc_ctrl |= CS4245_ADC_FM_DOUBLE;
453 cs4245_write(chip, CS4245_ANALOG_IN, 230 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
454 (data->cs4245_regs[CS4245_ANALOG_IN] & 231 } else {
455 ~CS4245_SEL_MASK) | 232 adc_ctrl |= CS4245_ADC_FM_QUAD;
456 sel_values[data->input_sel]); 233 mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
457
458 cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
459 data->input_vol[data->input_sel][0]);
460 cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
461 data->input_vol[data->input_sel][1]);
462
463 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
464 data->input_sel ? 0 : GPIO_INPUT_ROUTE,
465 GPIO_INPUT_ROUTE);
466 } 234 }
467 mutex_unlock(&chip->mutex); 235 data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
468 return changed; 236 data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
469} 237 cs4245_write_spi(chip, CS4245_ADC_CTRL);
470 238 cs4245_write_spi(chip, CS4245_MCLK_FREQ);
471static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
472{
473 static const char *const names[2] = { "Active", "Frozen" };
474
475 return snd_ctl_enum_info(info, 1, 2, names);
476} 239}
477 240
478static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 241unsigned int adjust_dg_dac_routing(struct oxygen *chip,
479{ 242 unsigned int play_routing)
480 struct oxygen *chip = ctl->private_data;
481 struct dg *data = chip->model_data;
482
483 value->value.enumerated.item[0] =
484 !!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
485 return 0;
486}
487
488static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
489{ 243{
490 struct oxygen *chip = ctl->private_data;
491 struct dg *data = chip->model_data; 244 struct dg *data = chip->model_data;
492 u8 reg; 245 unsigned int routing = 0;
493 int changed; 246
494 247 switch (data->output_sel) {
495 mutex_lock(&chip->mutex); 248 case PLAYBACK_DST_HP:
496 reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; 249 case PLAYBACK_DST_HP_FP:
497 if (value->value.enumerated.item[0]) 250 oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
498 reg |= CS4245_HPF_FREEZE; 251 OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
499 changed = reg != data->cs4245_regs[CS4245_ADC_CTRL]; 252 OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
500 if (changed) 253 break;
501 cs4245_write(chip, CS4245_ADC_CTRL, reg); 254 case PLAYBACK_DST_MULTICH:
502 mutex_unlock(&chip->mutex); 255 routing = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
503 return changed; 256 (2 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
504} 257 (1 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
505 258 (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
506#define INPUT_VOLUME(xname, index) { \ 259 oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
507 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 260 OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
508 .name = xname, \ 261 break;
509 .info = input_vol_info, \
510 .get = input_vol_get, \
511 .put = input_vol_put, \
512 .tlv = { .p = cs4245_pga_db_scale }, \
513 .private_value = index, \
514}
515static const struct snd_kcontrol_new dg_controls[] = {
516 {
517 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
518 .name = "Analog Output Playback Enum",
519 .info = output_switch_info,
520 .get = output_switch_get,
521 .put = output_switch_put,
522 },
523 {
524 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
525 .name = "Headphones Impedance Playback Enum",
526 .info = hp_volume_offset_info,
527 .get = hp_volume_offset_get,
528 .put = hp_volume_offset_put,
529 },
530 INPUT_VOLUME("Mic Capture Volume", 0),
531 INPUT_VOLUME("Aux Capture Volume", 1),
532 INPUT_VOLUME("Front Mic Capture Volume", 2),
533 INPUT_VOLUME("Line Capture Volume", 3),
534 {
535 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
536 .name = "Capture Source",
537 .info = input_sel_info,
538 .get = input_sel_get,
539 .put = input_sel_put,
540 },
541 {
542 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
543 .name = "ADC High-pass Filter Capture Enum",
544 .info = hpf_info,
545 .get = hpf_get,
546 .put = hpf_put,
547 },
548};
549
550static int dg_control_filter(struct snd_kcontrol_new *template)
551{
552 if (!strncmp(template->name, "Master Playback ", 16))
553 return 1;
554 return 0;
555}
556
557static int dg_mixer_init(struct oxygen *chip)
558{
559 unsigned int i;
560 int err;
561
562 for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
563 err = snd_ctl_add(chip->card,
564 snd_ctl_new1(&dg_controls[i], chip));
565 if (err < 0)
566 return err;
567 } 262 }
568 return 0; 263 return routing;
569} 264}
570 265
571static void dump_cs4245_registers(struct oxygen *chip, 266void dump_cs4245_registers(struct oxygen *chip,
572 struct snd_info_buffer *buffer) 267 struct snd_info_buffer *buffer)
573{ 268{
574 struct dg *data = chip->model_data; 269 struct dg *data = chip->model_data;
575 unsigned int i; 270 unsigned int addr;
576 271
577 snd_iprintf(buffer, "\nCS4245:"); 272 snd_iprintf(buffer, "\nCS4245:");
578 for (i = 1; i <= 0x10; ++i) 273 cs4245_read_spi(chip, CS4245_INT_STATUS);
579 snd_iprintf(buffer, " %02x", data->cs4245_regs[i]); 274 for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
275 snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
580 snd_iprintf(buffer, "\n"); 276 snd_iprintf(buffer, "\n");
581} 277}
582
583struct oxygen_model model_xonar_dg = {
584 .longname = "C-Media Oxygen HD Audio",
585 .chip = "CMI8786",
586 .init = dg_init,
587 .control_filter = dg_control_filter,
588 .mixer_init = dg_mixer_init,
589 .cleanup = dg_cleanup,
590 .suspend = dg_suspend,
591 .resume = dg_resume,
592 .set_dac_params = set_cs4245_dac_params,
593 .set_adc_params = set_cs4245_adc_params,
594 .adjust_dac_routing = adjust_dg_dac_routing,
595 .dump_registers = dump_cs4245_registers,
596 .model_data_size = sizeof(struct dg),
597 .device_config = PLAYBACK_0_TO_I2S |
598 PLAYBACK_1_TO_SPDIF |
599 CAPTURE_0_FROM_I2S_2 |
600 CAPTURE_1_FROM_SPDIF,
601 .dac_channels_pcm = 6,
602 .dac_channels_mixer = 0,
603 .function_flags = OXYGEN_FUNCTION_SPI,
604 .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
605 .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
606 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
607 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
608};
diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h
index 5688d78609a9..d461df357aa1 100644
--- a/sound/pci/oxygen/xonar_dg.h
+++ b/sound/pci/oxygen/xonar_dg.h
@@ -3,6 +3,54 @@
3 3
4#include "oxygen.h" 4#include "oxygen.h"
5 5
6#define GPIO_MAGIC 0x0008
7#define GPIO_HP_DETECT 0x0010
8#define GPIO_INPUT_ROUTE 0x0060
9#define GPIO_HP_REAR 0x0080
10#define GPIO_OUTPUT_ENABLE 0x0100
11
12#define CAPTURE_SRC_MIC 0
13#define CAPTURE_SRC_FP_MIC 1
14#define CAPTURE_SRC_LINE 2
15#define CAPTURE_SRC_AUX 3
16
17#define PLAYBACK_DST_HP 0
18#define PLAYBACK_DST_HP_FP 1
19#define PLAYBACK_DST_MULTICH 2
20
21enum cs4245_shadow_operation {
22 CS4245_SAVE_TO_SHADOW,
23 CS4245_LOAD_FROM_SHADOW
24};
25
26struct dg {
27 /* shadow copy of the CS4245 register space */
28 unsigned char cs4245_shadow[17];
29 /* output select: headphone/speakers */
30 unsigned char output_sel;
31 /* volumes for all capture sources */
32 char input_vol[4][2];
33 /* input select: mic/fp mic/line/aux */
34 unsigned char input_sel;
35};
36
37/* Xonar DG control routines */
38int cs4245_write_spi(struct oxygen *chip, u8 reg);
39int cs4245_read_spi(struct oxygen *chip, u8 reg);
40int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op);
41void dg_init(struct oxygen *chip);
42void set_cs4245_dac_params(struct oxygen *chip,
43 struct snd_pcm_hw_params *params);
44void set_cs4245_adc_params(struct oxygen *chip,
45 struct snd_pcm_hw_params *params);
46unsigned int adjust_dg_dac_routing(struct oxygen *chip,
47 unsigned int play_routing);
48void dump_cs4245_registers(struct oxygen *chip,
49 struct snd_info_buffer *buffer);
50void dg_suspend(struct oxygen *chip);
51void dg_resume(struct oxygen *chip);
52void dg_cleanup(struct oxygen *chip);
53
6extern struct oxygen_model model_xonar_dg; 54extern struct oxygen_model model_xonar_dg;
7 55
8#endif 56#endif
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c
new file mode 100644
index 000000000000..b885dac28a09
--- /dev/null
+++ b/sound/pci/oxygen/xonar_dg_mixer.c
@@ -0,0 +1,477 @@
1/*
2 * Mixer controls for the Xonar DG/DGX
3 *
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 * Copyright (c) Roman Volkov <v1ron@mail.ru>
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#include <linux/pci.h>
20#include <linux/delay.h>
21#include <sound/control.h>
22#include <sound/core.h>
23#include <sound/info.h>
24#include <sound/pcm.h>
25#include <sound/tlv.h>
26#include "oxygen.h"
27#include "xonar_dg.h"
28#include "cs4245.h"
29
30/* analog output select */
31
32static int output_select_apply(struct oxygen *chip)
33{
34 struct dg *data = chip->model_data;
35
36 data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK;
37 if (data->output_sel == PLAYBACK_DST_HP) {
38 /* mute FP (aux output) amplifier, switch rear jack to CS4245 */
39 oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
40 } else if (data->output_sel == PLAYBACK_DST_HP_FP) {
41 /*
42 * Unmute FP amplifier, switch rear jack to CS4361;
43 * I2S channels 2,3,4 should be inactive.
44 */
45 oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
46 data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC;
47 } else {
48 /*
49 * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp.,
50 * and change playback routing.
51 */
52 oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
53 }
54 return cs4245_write_spi(chip, CS4245_SIGNAL_SEL);
55}
56
57static int output_select_info(struct snd_kcontrol *ctl,
58 struct snd_ctl_elem_info *info)
59{
60 static const char *const names[3] = {
61 "Stereo Headphones",
62 "Stereo Headphones FP",
63 "Multichannel",
64 };
65
66 return snd_ctl_enum_info(info, 1, 3, names);
67}
68
69static int output_select_get(struct snd_kcontrol *ctl,
70 struct snd_ctl_elem_value *value)
71{
72 struct oxygen *chip = ctl->private_data;
73 struct dg *data = chip->model_data;
74
75 mutex_lock(&chip->mutex);
76 value->value.enumerated.item[0] = data->output_sel;
77 mutex_unlock(&chip->mutex);
78 return 0;
79}
80
81static int output_select_put(struct snd_kcontrol *ctl,
82 struct snd_ctl_elem_value *value)
83{
84 struct oxygen *chip = ctl->private_data;
85 struct dg *data = chip->model_data;
86 unsigned int new = value->value.enumerated.item[0];
87 int changed = 0;
88 int ret;
89
90 mutex_lock(&chip->mutex);
91 if (data->output_sel != new) {
92 data->output_sel = new;
93 ret = output_select_apply(chip);
94 changed = ret >= 0 ? 1 : ret;
95 oxygen_update_dac_routing(chip);
96 }
97 mutex_unlock(&chip->mutex);
98
99 return changed;
100}
101
102/* CS4245 Headphone Channels A&B Volume Control */
103
104static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
105 struct snd_ctl_elem_info *info)
106{
107 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
108 info->count = 2;
109 info->value.integer.min = 0;
110 info->value.integer.max = 255;
111 return 0;
112}
113
114static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
115 struct snd_ctl_elem_value *val)
116{
117 struct oxygen *chip = ctl->private_data;
118 struct dg *data = chip->model_data;
119 unsigned int tmp;
120
121 mutex_lock(&chip->mutex);
122 tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
123 val->value.integer.value[0] = tmp;
124 tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
125 val->value.integer.value[1] = tmp;
126 mutex_unlock(&chip->mutex);
127 return 0;
128}
129
130static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
131 struct snd_ctl_elem_value *val)
132{
133 struct oxygen *chip = ctl->private_data;
134 struct dg *data = chip->model_data;
135 int ret;
136 int changed = 0;
137 long new1 = val->value.integer.value[0];
138 long new2 = val->value.integer.value[1];
139
140 if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
141 return -EINVAL;
142
143 mutex_lock(&chip->mutex);
144 if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
145 (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
146 data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
147 data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
148 ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
149 if (ret >= 0)
150 ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
151 changed = ret >= 0 ? 1 : ret;
152 }
153 mutex_unlock(&chip->mutex);
154
155 return changed;
156}
157
158/* Headphone Mute */
159
160static int hp_mute_get(struct snd_kcontrol *ctl,
161 struct snd_ctl_elem_value *val)
162{
163 struct oxygen *chip = ctl->private_data;
164 struct dg *data = chip->model_data;
165
166 mutex_lock(&chip->mutex);
167 val->value.integer.value[0] =
168 !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
169 mutex_unlock(&chip->mutex);
170 return 0;
171}
172
173static int hp_mute_put(struct snd_kcontrol *ctl,
174 struct snd_ctl_elem_value *val)
175{
176 struct oxygen *chip = ctl->private_data;
177 struct dg *data = chip->model_data;
178 int ret;
179 int changed;
180
181 if (val->value.integer.value[0] > 1)
182 return -EINVAL;
183 mutex_lock(&chip->mutex);
184 data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
185 data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
186 (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
187 ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
188 changed = ret >= 0 ? 1 : ret;
189 mutex_unlock(&chip->mutex);
190 return changed;
191}
192
193/* capture volume for all sources */
194
195static int input_volume_apply(struct oxygen *chip, char left, char right)
196{
197 struct dg *data = chip->model_data;
198 int ret;
199
200 data->cs4245_shadow[CS4245_PGA_A_CTRL] = left;
201 data->cs4245_shadow[CS4245_PGA_B_CTRL] = right;
202 ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL);
203 if (ret < 0)
204 return ret;
205 return cs4245_write_spi(chip, CS4245_PGA_B_CTRL);
206}
207
208static int input_vol_info(struct snd_kcontrol *ctl,
209 struct snd_ctl_elem_info *info)
210{
211 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
212 info->count = 2;
213 info->value.integer.min = 2 * -12;
214 info->value.integer.max = 2 * 12;
215 return 0;
216}
217
218static int input_vol_get(struct snd_kcontrol *ctl,
219 struct snd_ctl_elem_value *value)
220{
221 struct oxygen *chip = ctl->private_data;
222 struct dg *data = chip->model_data;
223 unsigned int idx = ctl->private_value;
224
225 mutex_lock(&chip->mutex);
226 value->value.integer.value[0] = data->input_vol[idx][0];
227 value->value.integer.value[1] = data->input_vol[idx][1];
228 mutex_unlock(&chip->mutex);
229 return 0;
230}
231
232static int input_vol_put(struct snd_kcontrol *ctl,
233 struct snd_ctl_elem_value *value)
234{
235 struct oxygen *chip = ctl->private_data;
236 struct dg *data = chip->model_data;
237 unsigned int idx = ctl->private_value;
238 int changed = 0;
239 int ret = 0;
240
241 if (value->value.integer.value[0] < 2 * -12 ||
242 value->value.integer.value[0] > 2 * 12 ||
243 value->value.integer.value[1] < 2 * -12 ||
244 value->value.integer.value[1] > 2 * 12)
245 return -EINVAL;
246 mutex_lock(&chip->mutex);
247 changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
248 data->input_vol[idx][1] != value->value.integer.value[1];
249 if (changed) {
250 data->input_vol[idx][0] = value->value.integer.value[0];
251 data->input_vol[idx][1] = value->value.integer.value[1];
252 if (idx == data->input_sel) {
253 ret = input_volume_apply(chip,
254 data->input_vol[idx][0],
255 data->input_vol[idx][1]);
256 }
257 changed = ret >= 0 ? 1 : ret;
258 }
259 mutex_unlock(&chip->mutex);
260 return changed;
261}
262
263/* Capture Source */
264
265static int input_source_apply(struct oxygen *chip)
266{
267 struct dg *data = chip->model_data;
268
269 data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK;
270 if (data->input_sel == CAPTURE_SRC_FP_MIC)
271 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2;
272 else if (data->input_sel == CAPTURE_SRC_LINE)
273 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4;
274 else if (data->input_sel != CAPTURE_SRC_MIC)
275 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1;
276 return cs4245_write_spi(chip, CS4245_ANALOG_IN);
277}
278
279static int input_sel_info(struct snd_kcontrol *ctl,
280 struct snd_ctl_elem_info *info)
281{
282 static const char *const names[4] = {
283 "Mic", "Front Mic", "Line", "Aux"
284 };
285
286 return snd_ctl_enum_info(info, 1, 4, names);
287}
288
289static int input_sel_get(struct snd_kcontrol *ctl,
290 struct snd_ctl_elem_value *value)
291{
292 struct oxygen *chip = ctl->private_data;
293 struct dg *data = chip->model_data;
294
295 mutex_lock(&chip->mutex);
296 value->value.enumerated.item[0] = data->input_sel;
297 mutex_unlock(&chip->mutex);
298 return 0;
299}
300
301static int input_sel_put(struct snd_kcontrol *ctl,
302 struct snd_ctl_elem_value *value)
303{
304 struct oxygen *chip = ctl->private_data;
305 struct dg *data = chip->model_data;
306 int changed;
307 int ret;
308
309 if (value->value.enumerated.item[0] > 3)
310 return -EINVAL;
311
312 mutex_lock(&chip->mutex);
313 changed = value->value.enumerated.item[0] != data->input_sel;
314 if (changed) {
315 data->input_sel = value->value.enumerated.item[0];
316
317 ret = input_source_apply(chip);
318 if (ret >= 0)
319 ret = input_volume_apply(chip,
320 data->input_vol[data->input_sel][0],
321 data->input_vol[data->input_sel][1]);
322 changed = ret >= 0 ? 1 : ret;
323 }
324 mutex_unlock(&chip->mutex);
325 return changed;
326}
327
328/* ADC high-pass filter */
329
330static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
331{
332 static const char *const names[2] = { "Active", "Frozen" };
333
334 return snd_ctl_enum_info(info, 1, 2, names);
335}
336
337static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
338{
339 struct oxygen *chip = ctl->private_data;
340 struct dg *data = chip->model_data;
341
342 value->value.enumerated.item[0] =
343 !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
344 return 0;
345}
346
347static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
348{
349 struct oxygen *chip = ctl->private_data;
350 struct dg *data = chip->model_data;
351 u8 reg;
352 int changed;
353
354 mutex_lock(&chip->mutex);
355 reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
356 if (value->value.enumerated.item[0])
357 reg |= CS4245_HPF_FREEZE;
358 changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
359 if (changed) {
360 data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
361 cs4245_write_spi(chip, CS4245_ADC_CTRL);
362 }
363 mutex_unlock(&chip->mutex);
364 return changed;
365}
366
367#define INPUT_VOLUME(xname, index) { \
368 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
369 .name = xname, \
370 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
371 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
372 .info = input_vol_info, \
373 .get = input_vol_get, \
374 .put = input_vol_put, \
375 .tlv = { .p = pga_db_scale }, \
376 .private_value = index, \
377}
378static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
379static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
380static const struct snd_kcontrol_new dg_controls[] = {
381 {
382 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
383 .name = "Analog Output Playback Enum",
384 .info = output_select_info,
385 .get = output_select_get,
386 .put = output_select_put,
387 },
388 {
389 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
390 .name = "Headphone Playback Volume",
391 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
392 SNDRV_CTL_ELEM_ACCESS_TLV_READ,
393 .info = hp_stereo_volume_info,
394 .get = hp_stereo_volume_get,
395 .put = hp_stereo_volume_put,
396 .tlv = { .p = hp_db_scale, },
397 },
398 {
399 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
400 .name = "Headphone Playback Switch",
401 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
402 .info = snd_ctl_boolean_mono_info,
403 .get = hp_mute_get,
404 .put = hp_mute_put,
405 },
406 INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC),
407 INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC),
408 INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE),
409 INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX),
410 {
411 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
412 .name = "Capture Source",
413 .info = input_sel_info,
414 .get = input_sel_get,
415 .put = input_sel_put,
416 },
417 {
418 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
419 .name = "ADC High-pass Filter Capture Enum",
420 .info = hpf_info,
421 .get = hpf_get,
422 .put = hpf_put,
423 },
424};
425
426static int dg_control_filter(struct snd_kcontrol_new *template)
427{
428 if (!strncmp(template->name, "Master Playback ", 16))
429 return 1;
430 return 0;
431}
432
433static int dg_mixer_init(struct oxygen *chip)
434{
435 unsigned int i;
436 int err;
437
438 output_select_apply(chip);
439 input_source_apply(chip);
440 oxygen_update_dac_routing(chip);
441
442 for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
443 err = snd_ctl_add(chip->card,
444 snd_ctl_new1(&dg_controls[i], chip));
445 if (err < 0)
446 return err;
447 }
448
449 return 0;
450}
451
452struct oxygen_model model_xonar_dg = {
453 .longname = "C-Media Oxygen HD Audio",
454 .chip = "CMI8786",
455 .init = dg_init,
456 .control_filter = dg_control_filter,
457 .mixer_init = dg_mixer_init,
458 .cleanup = dg_cleanup,
459 .suspend = dg_suspend,
460 .resume = dg_resume,
461 .set_dac_params = set_cs4245_dac_params,
462 .set_adc_params = set_cs4245_adc_params,
463 .adjust_dac_routing = adjust_dg_dac_routing,
464 .dump_registers = dump_cs4245_registers,
465 .model_data_size = sizeof(struct dg),
466 .device_config = PLAYBACK_0_TO_I2S |
467 PLAYBACK_1_TO_SPDIF |
468 CAPTURE_0_FROM_I2S_1 |
469 CAPTURE_1_FROM_SPDIF,
470 .dac_channels_pcm = 6,
471 .dac_channels_mixer = 0,
472 .function_flags = OXYGEN_FUNCTION_SPI,
473 .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
474 .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
475 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
476 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
477};