ALSA: oxygen: Xonar DG(X): modify playback output select
[platform/adaptation/renesas_rcar/renesas_kernel.git] / sound / pci / oxygen / xonar_dg.c
1 /*
2  * card driver 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 /*
20  * Xonar DG/DGX
21  * ------------
22  *
23  * CS4245 and CS4361 both will mute all outputs if any clock ratio
24  * is invalid.
25  *
26  * CMI8788:
27  *
28  *   SPI 0 -> CS4245
29  *
30  *   Playback:
31  *   I²S 1 -> CS4245
32  *   I²S 2 -> CS4361 (center/LFE)
33  *   I²S 3 -> CS4361 (surround)
34  *   I²S 4 -> CS4361 (front)
35  *   Capture:
36  *   I²S ADC 1 <- CS4245
37  *
38  *   GPIO 3 <- ?
39  *   GPIO 4 <- headphone detect
40  *   GPIO 5 -> enable ADC analog circuit for the left channel
41  *   GPIO 6 -> enable ADC analog circuit for the right channel
42  *   GPIO 7 -> switch green rear output jack between CS4245 and and the first
43  *             channel of CS4361 (mechanical relay)
44  *   GPIO 8 -> enable output to speakers
45  *
46  * CS4245:
47  *
48  *   input 0 <- mic
49  *   input 1 <- aux
50  *   input 2 <- front mic
51  *   input 4 <- line
52  *   DAC out -> headphones
53  *   aux out -> front panel headphones
54  */
55
56 #include <linux/pci.h>
57 #include <linux/delay.h>
58 #include <sound/control.h>
59 #include <sound/core.h>
60 #include <sound/info.h>
61 #include <sound/pcm.h>
62 #include <sound/tlv.h>
63 #include "oxygen.h"
64 #include "xonar_dg.h"
65 #include "cs4245.h"
66
67 int cs4245_write_spi(struct oxygen *chip, u8 reg)
68 {
69         struct dg *data = chip->model_data;
70         unsigned int packet;
71
72         packet = reg << 8;
73         packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
74         packet |= data->cs4245_shadow[reg];
75
76         return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
77                                 OXYGEN_SPI_DATA_LENGTH_3 |
78                                 OXYGEN_SPI_CLOCK_1280 |
79                                 (0 << OXYGEN_SPI_CODEC_SHIFT) |
80                                 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
81                                 packet);
82 }
83
84 int cs4245_read_spi(struct oxygen *chip, u8 addr)
85 {
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);
106
107         return 0;
108 }
109
110 int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
111 {
112         struct dg *data = chip->model_data;
113         unsigned char addr;
114         int ret;
115
116         for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
117                 ret = (op == CS4245_SAVE_TO_SHADOW ?
118                         cs4245_read_spi(chip, addr) :
119                         cs4245_write_spi(chip, addr));
120                 if (ret < 0)
121                         return ret;
122         }
123         return 0;
124 }
125
126 void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
127 {
128         struct dg *data = chip->model_data;
129
130         oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
131                          OXYGEN_SPI_DATA_LENGTH_3 |
132                          OXYGEN_SPI_CLOCK_1280 |
133                          (0 << OXYGEN_SPI_CODEC_SHIFT) |
134                          OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
135                          CS4245_SPI_ADDRESS_S |
136                          CS4245_SPI_WRITE_S |
137                          (reg << 8) | value);
138         data->cs4245_shadow[reg] = value;
139 }
140
141 void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
142 {
143         struct dg *data = chip->model_data;
144
145         if (value != data->cs4245_shadow[reg])
146                 cs4245_write(chip, reg, value);
147 }
148
149 static void cs4245_init(struct oxygen *chip)
150 {
151         struct dg *data = chip->model_data;
152
153         /* save the initial state: codec version, registers */
154         cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
155
156         /*
157          * Power up the CODEC internals, enable soft ramp & zero cross, work in
158          * async. mode, enable aux output from DAC. Invert DAC output as in the
159          * Windows driver.
160          */
161         data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
162         data->cs4245_shadow[CS4245_SIGNAL_SEL] =
163                 CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
164         data->cs4245_shadow[CS4245_DAC_CTRL_1] =
165                 CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
166         data->cs4245_shadow[CS4245_DAC_CTRL_2] =
167                 CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
168         data->cs4245_shadow[CS4245_ADC_CTRL] =
169                 CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
170         data->cs4245_shadow[CS4245_ANALOG_IN] =
171                 CS4245_PGA_SOFT | CS4245_PGA_ZERO;
172         data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
173         data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
174         data->cs4245_shadow[CS4245_DAC_A_CTRL] = 4;
175         data->cs4245_shadow[CS4245_DAC_B_CTRL] = 4;
176
177         cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
178         snd_component_add(chip->card, "CS4245");
179 }
180
181 void dg_init(struct oxygen *chip)
182 {
183         struct dg *data = chip->model_data;
184
185         data->output_sel = 0;
186         data->input_sel = 3;
187         data->hp_vol_att = 2 * 16;
188
189         cs4245_init(chip);
190         oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
191                        GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
192         oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
193         msleep(2500); /* anti-pop delay */
194         oxygen_write16(chip, OXYGEN_GPIO_DATA,
195                        GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
196 }
197
198 void dg_cleanup(struct oxygen *chip)
199 {
200         oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
201 }
202
203 void dg_suspend(struct oxygen *chip)
204 {
205         dg_cleanup(chip);
206 }
207
208 void dg_resume(struct oxygen *chip)
209 {
210         cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
211         msleep(2500);
212         oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
213 }
214
215 void set_cs4245_dac_params(struct oxygen *chip,
216                                   struct snd_pcm_hw_params *params)
217 {
218         struct dg *data = chip->model_data;
219         unsigned char dac_ctrl;
220         unsigned char mclk_freq;
221
222         dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
223         mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
224         if (params_rate(params) <= 50000) {
225                 dac_ctrl |= CS4245_DAC_FM_SINGLE;
226                 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
227         } else if (params_rate(params) <= 100000) {
228                 dac_ctrl |= CS4245_DAC_FM_DOUBLE;
229                 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
230         } else {
231                 dac_ctrl |= CS4245_DAC_FM_QUAD;
232                 mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
233         }
234         data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
235         data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
236         cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
237         cs4245_write_spi(chip, CS4245_MCLK_FREQ);
238 }
239
240 void set_cs4245_adc_params(struct oxygen *chip,
241                                   struct snd_pcm_hw_params *params)
242 {
243         struct dg *data = chip->model_data;
244         unsigned char adc_ctrl;
245         unsigned char mclk_freq;
246
247         adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
248         mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
249         if (params_rate(params) <= 50000) {
250                 adc_ctrl |= CS4245_ADC_FM_SINGLE;
251                 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
252         } else if (params_rate(params) <= 100000) {
253                 adc_ctrl |= CS4245_ADC_FM_DOUBLE;
254                 mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
255         } else {
256                 adc_ctrl |= CS4245_ADC_FM_QUAD;
257                 mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
258         }
259         data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
260         data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
261         cs4245_write_spi(chip, CS4245_ADC_CTRL);
262         cs4245_write_spi(chip, CS4245_MCLK_FREQ);
263 }
264
265 unsigned int adjust_dg_dac_routing(struct oxygen *chip,
266                                           unsigned int play_routing)
267 {
268         struct dg *data = chip->model_data;
269         unsigned int routing = 0;
270
271         switch (data->output_sel) {
272         case PLAYBACK_DST_HP:
273         case PLAYBACK_DST_HP_FP:
274                 oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
275                         OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
276                         OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
277                 break;
278         case PLAYBACK_DST_MULTICH:
279                 routing = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
280                           (2 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
281                           (1 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
282                           (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
283                 oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
284                         OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
285                 break;
286         }
287         return routing;
288 }
289
290 void dump_cs4245_registers(struct oxygen *chip,
291                                   struct snd_info_buffer *buffer)
292 {
293         struct dg *data = chip->model_data;
294         unsigned int addr;
295
296         snd_iprintf(buffer, "\nCS4245:");
297         cs4245_read_spi(chip, CS4245_INT_STATUS);
298         for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
299                 snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
300         snd_iprintf(buffer, "\n");
301 }