[alsa-devel] [PATCH v4 01/19] ALSA: Oxygen: Add the separate SPI waiting function
The oxygen_wait_spi() function now performs waiting when the SPI bus completes a transaction. Introduce the timeout error checking and increase timeout to 200 from 40.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/oxygen_io.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 521eae4..471e64a 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -194,6 +194,21 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, } EXPORT_SYMBOL(oxygen_write_ac97_masked);
+static int oxygen_wait_spi(struct oxygen *chip) +{ + /* Higher timeout to be sure: 200 us, + Actual transaction should not need more than 40 us */ + unsigned int count; + for (count = 50; count > 0; count--) { + udelay(4); + if ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & + OXYGEN_SPI_BUSY) == 0) + return 0; + } + snd_printk(KERN_ERR "Oxygen: SPI wait timeout\n"); + return -EIO; +} + void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) { unsigned int count;
Modify the oxygen_write_spi() function to use the newly introduced oxygen_wait_spi() function. Change return value from void to int, so it can return error codes. Older drivers just ignore that return value, new drivers can check this value. We need to wait AFTER initiating the SPI transaction, otherwise read operation will not work.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/oxygen.h | 2 +- sound/pci/oxygen/oxygen_io.c | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-)
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 09a24b2..c10ab07 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, void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, unsigned int index, u16 data, u16 mask);
-void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); +int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
void oxygen_reset_uart(struct oxygen *chip); diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 471e64a..04f529e 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -209,23 +209,16 @@ static int oxygen_wait_spi(struct oxygen *chip) return -EIO; }
-void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) +int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) { - unsigned int count; - - /* should not need more than 30.72 us (24 * 1.28 us) */ - count = 10; - while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) - && count > 0) { - udelay(4); - --count; - } - + /* We need to wait AFTER initiating the SPI transaction, + otherwise read operations will not work */ oxygen_write8(chip, OXYGEN_SPI_DATA1, data); oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); if (control & OXYGEN_SPI_DATA_LENGTH_3) oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); + return oxygen_wait_spi(chip); } EXPORT_SYMBOL(oxygen_write_spi);
The Xonar DG/DGX driver needs this mask to mute unnecessary channels.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/oxygen_regs.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h index 63dc7a0..8c191ba 100644 --- a/sound/pci/oxygen/oxygen_regs.h +++ b/sound/pci/oxygen/oxygen_regs.h @@ -318,6 +318,7 @@ #define OXYGEN_PLAY_MUTE23 0x0002 #define OXYGEN_PLAY_MUTE45 0x0004 #define OXYGEN_PLAY_MUTE67 0x0008 +#define OXYGEN_PLAY_MUTE_MASK 0x000f #define OXYGEN_PLAY_MULTICH_MASK 0x0010 #define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000 #define OXYGEN_PLAY_MULTICH_AC97 0x0010
When the user switches the output from stereo to multichannel or vice versa, the driver needs to update the channel routing. Instead of creating additional subroutines, I better export existing oxygen_update_dac_routing symbol from the oxygen mixer and call this function. It calls model.adjust_dac_routing() and my function does the work.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/oxygen_mixer.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index c0dbb52..5988e04 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) if (chip->model.update_center_lfe_mix) chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2); } +EXPORT_SYMBOL(oxygen_update_dac_routing);
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) {
Add some additional information in comments and my copyright.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 77acd79..b806cc1 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -1,8 +1,8 @@ /* - * card driver for the Xonar DG/DGX + * Card driver for the Xonar DG/DGX * * Copyright (c) Clemens Ladisch clemens@ladisch.de - * + * Copyright (c) Roman Volkov v1ron@mail.ru * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2. @@ -20,27 +20,35 @@ * Xonar DG/DGX * ------------ * + * CS4245 and CS4361 both will mute all outputs if any clock ratio + * is invalid. + * * CMI8788: * * SPI 0 -> CS4245 * + * Playback: * I²S 1 -> CS4245 * I²S 2 -> CS4361 (center/LFE) * I²S 3 -> CS4361 (surround) * I²S 4 -> CS4361 (front) + * Capture: + * I²S ADC 1 <- CS4245 * * GPIO 3 <- ? * GPIO 4 <- headphone detect - * GPIO 5 -> route input jack to line-in (0) or mic-in (1) - * GPIO 6 -> route input jack to line-in (0) or mic-in (1) - * GPIO 7 -> enable rear headphone amp + * GPIO 5 -> enable ADC analog circuit for the left channel + * GPIO 6 -> enable ADC analog circuit for the right channel + * GPIO 7 -> switch green rear output jack between CS4245 and + * and the first channel of CS4361 (mechanical relay) * GPIO 8 -> enable output to speakers * * CS4245: * + * input 0 <- mic * input 1 <- aux * input 2 <- front mic - * input 4 <- line/mic + * input 4 <- line * DAC out -> headphones * aux out -> front panel headphones */
Add additional constants to the xonar_dg.h file: capture and playback sources. Move GPIO_* constants and the dg struct to the header file from the xonar_dg.c file.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 14 -------------- sound/pci/oxygen/xonar_dg.h | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index b806cc1..2a0ad6e 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -64,20 +64,6 @@ #include "xonar_dg.h" #include "cs4245.h"
-#define GPIO_MAGIC 0x0008 -#define GPIO_HP_DETECT 0x0010 -#define GPIO_INPUT_ROUTE 0x0060 -#define GPIO_HP_REAR 0x0080 -#define GPIO_OUTPUT_ENABLE 0x0100 - -struct dg { - unsigned int output_sel; - s8 input_vol[4][2]; - unsigned int input_sel; - u8 hp_vol_att; - u8 cs4245_regs[0x11]; -}; - static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data; diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 5688d78..0812692 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -3,6 +3,29 @@
#include "oxygen.h"
+#define GPIO_MAGIC 0x0008 +#define GPIO_HP_DETECT 0x0010 +#define GPIO_INPUT_ROUTE 0x0060 +#define GPIO_HP_REAR 0x0080 +#define GPIO_OUTPUT_ENABLE 0x0100 + +#define CAPTURE_SRC_MIC 0 +#define CAPTURE_SRC_FP_MIC 1 +#define CAPTURE_SRC_LINE 2 +#define CAPTURE_SRC_AUX 3 + +#define PLAYBACK_DST_HP 0 +#define PLAYBACK_DST_HP_FP 1 +#define PLAYBACK_DST_MULTICH 2 + +struct dg { + unsigned int output_sel; + s8 input_vol[4][2]; + unsigned int input_sel; + u8 hp_vol_att; + u8 cs4245_regs[0x11]; +}; + extern struct oxygen_model model_xonar_dg;
#endif
Add the new SPI write and read functions. The SPI read function is used for creating initial registers dump and may be used for debugging purposes. SPI operations are cached, so there is a new function to manage the cache (shadow). I have to remove the shift from the CS4245_SPI_* constants, since when we are performing the reading, we need to shift by 8 instead of 16.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/cs4245.h | 7 +++-- sound/pci/oxygen/xonar_dg.c | 64 +++++++++++++++++++++++++++++++++++++++++-- sound/pci/oxygen/xonar_dg.h | 7 +++++ 3 files changed, 74 insertions(+), 4 deletions(-)
diff --git a/sound/pci/oxygen/cs4245.h b/sound/pci/oxygen/cs4245.h index 5e0197e..9909865 100644 --- a/sound/pci/oxygen/cs4245.h +++ b/sound/pci/oxygen/cs4245.h @@ -102,6 +102,9 @@ #define CS4245_ADC_OVFL 0x02 #define CS4245_ADC_UNDRFL 0x01
+#define CS4245_SPI_ADDRESS_S (0x9e << 16) +#define CS4245_SPI_WRITE_S (0 << 16)
-#define CS4245_SPI_ADDRESS (0x9e << 16) -#define CS4245_SPI_WRITE (0 << 16) +#define CS4245_SPI_ADDRESS 0x9e +#define CS4245_SPI_WRITE 0 +#define CS4245_SPI_READ 1 diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 2a0ad6e..28cfeeb 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -64,6 +64,66 @@ #include "xonar_dg.h" #include "cs4245.h"
+int cs4245_write_spi(struct oxygen *chip, u8 reg) +{ + struct dg *data = chip->model_data; + register unsigned int packet; + + packet = (u32)reg; + packet <<= 8; + packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16; + packet |= data->cs4245_shadow[reg]; + + return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_3 | + OXYGEN_SPI_CLOCK_1280 | + (0 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + packet); +} + +int cs4245_read_spi(struct oxygen *chip, u8 addr) +{ + struct dg *data = chip->model_data; + int ret; + + ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI | + OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), + ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr); + if (ret < 0) + return ret; + + ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI | + OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), + (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8); + if (ret < 0) + return ret; + + data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1); + + return 0; +} + +int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) +{ + struct dg *data = chip->model_data; + unsigned char addr; + int ret; + + for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) { + ret = (op == CS4245_SAVE_TO_SHADOW ? + cs4245_read_spi(chip, addr) : + cs4245_write_spi(chip, addr)); + if (ret < 0) + return ret; + } + return 0; +} + static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data; @@ -73,8 +133,8 @@ static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, - CS4245_SPI_ADDRESS | - CS4245_SPI_WRITE | + CS4245_SPI_ADDRESS_S | + CS4245_SPI_WRITE_S | (reg << 8) | value); data->cs4245_regs[reg] = value; } diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 0812692..f861a4c 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -18,7 +18,14 @@ #define PLAYBACK_DST_HP_FP 1 #define PLAYBACK_DST_MULTICH 2
+enum cs4245_shadow_operation { + CS4245_SAVE_TO_SHADOW, + CS4245_LOAD_FROM_SHADOW +}; + struct dg { + /* The shadow copy of the CS4245 register space */ + unsigned char cs4245_shadow[17]; unsigned int output_sel; s8 input_vol[4][2]; unsigned int input_sel;
On Fri, Jan 24, 2014 at 04:18:08PM +0400, Roman Volkov wrote:
Add the new SPI write and read functions. The SPI read function is used for creating initial registers dump and may be used for debugging purposes. SPI operations are cached, so there is a new function to manage the cache (shadow). I have to remove the shift from the CS4245_SPI_* constants, since when we are performing the reading, we need to shift by 8 instead of 16.
I can't help but think that this driver ought to be making more use of frameworks - it sounds like an ASoC system really (split CODEC and CPU) and all this register I/O stuff sounds like it could make use of regmap.
Mark Brown wrote:
On Fri, Jan 24, 2014 at 04:18:08PM +0400, Roman Volkov wrote:
Add the new SPI write and read functions. The SPI read function is used for creating initial registers dump and may be used for debugging purposes. SPI operations are cached, so there is a new function to manage the cache (shadow). I have to remove the shift from the CS4245_SPI_* constants, since when we are performing the reading, we need to shift by 8 instead of 16.
I can't help but think that this driver ought to be making more use of frameworks
Everbody else thinks the same: http://lkml.org/lkml/2012/3/13/215.
Unfortunately, thinking requires less effort than writing patches.
Regards, Clemens
Change CS4245 initialization: different sequence and GPIO values, according to datasheets and reverse-engineering information. Change Cleanup/resume/suspend functions, since they use initialization.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 114 +++++++++++++++++++------------------------ sound/pci/oxygen/xonar_dg.h | 1 - 2 files changed, 50 insertions(+), 65 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 28cfeeb..4ec06e6 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -136,68 +136,50 @@ static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) CS4245_SPI_ADDRESS_S | CS4245_SPI_WRITE_S | (reg << 8) | value); - data->cs4245_regs[reg] = value; + data->cs4245_shadow[reg] = value; }
static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data;
- if (value != data->cs4245_regs[reg]) + if (value != data->cs4245_shadow[reg]) cs4245_write(chip, reg, value); }
-static void cs4245_registers_init(struct oxygen *chip) -{ - struct dg *data = chip->model_data; - - cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN); - cs4245_write(chip, CS4245_DAC_CTRL_1, - data->cs4245_regs[CS4245_DAC_CTRL_1]); - cs4245_write(chip, CS4245_ADC_CTRL, - data->cs4245_regs[CS4245_ADC_CTRL]); - cs4245_write(chip, CS4245_SIGNAL_SEL, - data->cs4245_regs[CS4245_SIGNAL_SEL]); - cs4245_write(chip, CS4245_PGA_B_CTRL, - data->cs4245_regs[CS4245_PGA_B_CTRL]); - cs4245_write(chip, CS4245_PGA_A_CTRL, - data->cs4245_regs[CS4245_PGA_A_CTRL]); - cs4245_write(chip, CS4245_ANALOG_IN, - data->cs4245_regs[CS4245_ANALOG_IN]); - cs4245_write(chip, CS4245_DAC_A_CTRL, - data->cs4245_regs[CS4245_DAC_A_CTRL]); - cs4245_write(chip, CS4245_DAC_B_CTRL, - data->cs4245_regs[CS4245_DAC_B_CTRL]); - cs4245_write(chip, CS4245_DAC_CTRL_2, - CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC); - cs4245_write(chip, CS4245_INT_MASK, 0); - cs4245_write(chip, CS4245_POWER_CTRL, 0); -} - static void cs4245_init(struct oxygen *chip) { struct dg *data = chip->model_data;
- data->cs4245_regs[CS4245_DAC_CTRL_1] = + /* Save the initial state: codec version, registers */ + cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW); + + /* + * Power up the CODEC internals, enable soft ramp & zero cross, work + * in async. mode, + * Enable aux output from DAC. Invert DAC output as in the Windows + * driver + */ + data->cs4245_shadow[CS4245_POWER_CTRL] = 0; + data->cs4245_shadow[CS4245_SIGNAL_SEL] = CS4245_A_OUT_SEL_DAC | + CS4245_ASYNCH; + data->cs4245_shadow[CS4245_DAC_CTRL_1] = 0; + data->cs4245_shadow[CS4245_DAC_CTRL_2] = CS4245_DAC_SOFT | + CS4245_DAC_ZERO | CS4245_INVERT_DAC; + data->cs4245_shadow[CS4245_ADC_CTRL] = 0; + data->cs4245_shadow[CS4245_ANALOG_IN] = CS4245_PGA_SOFT | + CS4245_PGA_ZERO; + data->cs4245_shadow[CS4245_DAC_CTRL_1] = CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST; - data->cs4245_regs[CS4245_ADC_CTRL] = + data->cs4245_shadow[CS4245_ADC_CTRL] = CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; - data->cs4245_regs[CS4245_SIGNAL_SEL] = - CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH; - data->cs4245_regs[CS4245_PGA_B_CTRL] = 0; - data->cs4245_regs[CS4245_PGA_A_CTRL] = 0; - data->cs4245_regs[CS4245_ANALOG_IN] = - CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4; - data->cs4245_regs[CS4245_DAC_A_CTRL] = 0; - data->cs4245_regs[CS4245_DAC_B_CTRL] = 0; - cs4245_registers_init(chip); - snd_component_add(chip->card, "CS4245"); -} + data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0; + data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0; + data->cs4245_shadow[CS4245_DAC_A_CTRL] = 4; + data->cs4245_shadow[CS4245_DAC_B_CTRL] = 4;
-static void dg_output_enable(struct oxygen *chip) -{ - msleep(2500); - oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); + snd_component_add(chip->card, "CS4245"); }
static void dg_init(struct oxygen *chip) @@ -208,15 +190,18 @@ static void dg_init(struct oxygen *chip) data->input_sel = 3; data->hp_vol_att = 2 * 16;
- cs4245_init(chip); + /* + * The pin XGPIO1 as XGPIO1, gpios 8,7,6,5,1,0 as outputs. + */
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_MAGIC | GPIO_HP_DETECT); - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, - GPIO_INPUT_ROUTE | GPIO_HP_REAR); - dg_output_enable(chip); + cs4245_init(chip); + oxygen_write16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE | + GPIO_HP_REAR | GPIO_INPUT_ROUTE); + oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE); + /* Anti-pop delay */ + msleep(2500); + oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE | + GPIO_INPUT_ROUTE); }
static void dg_cleanup(struct oxygen *chip) @@ -231,8 +216,9 @@ static void dg_suspend(struct oxygen *chip)
static void dg_resume(struct oxygen *chip) { - cs4245_registers_init(chip); - dg_output_enable(chip); + cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); + msleep(2500); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); }
static void set_cs4245_dac_params(struct oxygen *chip, @@ -241,7 +227,7 @@ static void set_cs4245_dac_params(struct oxygen *chip, struct dg *data = chip->model_data; u8 value;
- value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; + value = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; if (params_rate(params) <= 50000) value |= CS4245_DAC_FM_SINGLE; else if (params_rate(params) <= 100000) @@ -257,7 +243,7 @@ static void set_cs4245_adc_params(struct oxygen *chip, struct dg *data = chip->model_data; u8 value;
- value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; + value = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; if (params_rate(params) <= 50000) value |= CS4245_ADC_FM_SINGLE; else if (params_rate(params) <= 100000) @@ -334,7 +320,7 @@ static int output_switch_put(struct snd_kcontrol *ctl, if (changed) { data->output_sel = value->value.enumerated.item[0];
- reg = data->cs4245_regs[CS4245_SIGNAL_SEL] & + reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & ~CS4245_A_OUT_SEL_MASK; reg |= data->output_sel == 2 ? CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; @@ -505,7 +491,7 @@ static int input_sel_put(struct snd_kcontrol *ctl, data->input_sel = value->value.enumerated.item[0];
cs4245_write(chip, CS4245_ANALOG_IN, - (data->cs4245_regs[CS4245_ANALOG_IN] & + (data->cs4245_shadow[CS4245_ANALOG_IN] & ~CS4245_SEL_MASK) | sel_values[data->input_sel]);
@@ -535,7 +521,7 @@ static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) struct dg *data = chip->model_data;
value->value.enumerated.item[0] = - !!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); + !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); return 0; }
@@ -547,10 +533,10 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) int changed;
mutex_lock(&chip->mutex); - reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; + reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; if (value->value.enumerated.item[0]) reg |= CS4245_HPF_FREEZE; - changed = reg != data->cs4245_regs[CS4245_ADC_CTRL]; + changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; if (changed) cs4245_write(chip, CS4245_ADC_CTRL, reg); mutex_unlock(&chip->mutex); @@ -630,7 +616,7 @@ static void dump_cs4245_registers(struct oxygen *chip,
snd_iprintf(buffer, "\nCS4245:"); for (i = 1; i <= 0x10; ++i) - snd_iprintf(buffer, " %02x", data->cs4245_regs[i]); + snd_iprintf(buffer, " %02x", data->cs4245_shadow[i]); snd_iprintf(buffer, "\n"); }
diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index f861a4c..08e598c 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -30,7 +30,6 @@ struct dg { s8 input_vol[4][2]; unsigned int input_sel; u8 hp_vol_att; - u8 cs4245_regs[0x11]; };
extern struct oxygen_model model_xonar_dg;
When selecting the audio sample rate for CS4245, the MCLK divider should also be changed.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 58 ++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 20 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 4ec06e6..d9a8a4c 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -225,32 +225,50 @@ static void set_cs4245_dac_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; - u8 value; - - value = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; - if (params_rate(params) <= 50000) - value |= CS4245_DAC_FM_SINGLE; - else if (params_rate(params) <= 100000) - value |= CS4245_DAC_FM_DOUBLE; - else - value |= CS4245_DAC_FM_QUAD; - cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value); + unsigned char dac_ctrl; + unsigned char mclk_freq; + + dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; + mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK; + if (params_rate(params) <= 50000) { + dac_ctrl |= CS4245_DAC_FM_SINGLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; + } else if (params_rate(params) <= 100000) { + dac_ctrl |= CS4245_DAC_FM_DOUBLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; + } else { + dac_ctrl |= CS4245_DAC_FM_QUAD; + mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT; + } + data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl; + data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; + cs4245_write_spi(chip, CS4245_DAC_CTRL_1); + cs4245_write_spi(chip, CS4245_MCLK_FREQ); }
static void set_cs4245_adc_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; - u8 value; - - value = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; - if (params_rate(params) <= 50000) - value |= CS4245_ADC_FM_SINGLE; - else if (params_rate(params) <= 100000) - value |= CS4245_ADC_FM_DOUBLE; - else - value |= CS4245_ADC_FM_QUAD; - cs4245_write_cached(chip, CS4245_ADC_CTRL, value); + unsigned char adc_ctrl; + unsigned char mclk_freq; + + adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; + mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK; + if (params_rate(params) <= 50000) { + adc_ctrl |= CS4245_ADC_FM_SINGLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; + } else if (params_rate(params) <= 100000) { + adc_ctrl |= CS4245_ADC_FM_DOUBLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; + } else { + adc_ctrl |= CS4245_ADC_FM_QUAD; + mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT; + } + data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl; + data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; + cs4245_write_spi(chip, CS4245_ADC_CTRL); + cs4245_write_spi(chip, CS4245_MCLK_FREQ); }
static inline unsigned int shift_bits(unsigned int value,
When selecting the audio output destinations (headphones, FP headphones, multichannel output), the channel routing should be changed depending on what destination selected. Also unnecessary I2S channels are digitally muted. This function called when the user selects the destination in the ALSA mixer.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 44 ++++++++++++++++++++----------------------- sound/pci/oxygen/xonar_dg.h | 2 ++ 2 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index d9a8a4c..a7dc39d 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -271,33 +271,29 @@ static void set_cs4245_adc_params(struct oxygen *chip, cs4245_write_spi(chip, CS4245_MCLK_FREQ); }
-static inline unsigned int shift_bits(unsigned int value, - unsigned int shift_from, - unsigned int shift_to, - unsigned int mask) -{ - if (shift_from < shift_to) - return (value << (shift_to - shift_from)) & mask; - else - return (value >> (shift_from - shift_to)) & mask; -} - static unsigned int adjust_dg_dac_routing(struct oxygen *chip, unsigned int play_routing) { - return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) | - shift_bits(play_routing, - OXYGEN_PLAY_DAC2_SOURCE_SHIFT, - OXYGEN_PLAY_DAC1_SOURCE_SHIFT, - OXYGEN_PLAY_DAC1_SOURCE_MASK) | - shift_bits(play_routing, - OXYGEN_PLAY_DAC1_SOURCE_SHIFT, - OXYGEN_PLAY_DAC2_SOURCE_SHIFT, - OXYGEN_PLAY_DAC2_SOURCE_MASK) | - shift_bits(play_routing, - OXYGEN_PLAY_DAC0_SOURCE_SHIFT, - OXYGEN_PLAY_DAC3_SOURCE_SHIFT, - OXYGEN_PLAY_DAC3_SOURCE_MASK); + struct dg *data = chip->model_data; + unsigned int routing = 0; + + switch (data->pcm_output) { + case PLAYBACK_DST_HP: + case PLAYBACK_DST_HP_FP: + oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, + OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 | + OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK); + break; + case PLAYBACK_DST_MULTICH: + routing = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); + oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, + OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK); + break; + } + return routing; }
static int output_switch_info(struct snd_kcontrol *ctl, diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 08e598c..578d7a2 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -26,6 +26,8 @@ enum cs4245_shadow_operation { struct dg { /* The shadow copy of the CS4245 register space */ unsigned char cs4245_shadow[17]; + /* Output select: headphone/speakers */ + unsigned char pcm_output; unsigned int output_sel; s8 input_vol[4][2]; unsigned int input_sel;
Change the function to read the data from the new shadow buffer.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index a7dc39d..b277f0d 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -626,11 +626,12 @@ static void dump_cs4245_registers(struct oxygen *chip, struct snd_info_buffer *buffer) { struct dg *data = chip->model_data; - unsigned int i; + unsigned int addr;
snd_iprintf(buffer, "\nCS4245:"); - for (i = 1; i <= 0x10; ++i) - snd_iprintf(buffer, " %02x", data->cs4245_shadow[i]); + cs4245_read_spi(chip, CS4245_INT_STATUS); + for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) + snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]); snd_iprintf(buffer, "\n"); }
Moving the mixer code away makes things easier. The mixer will control the driver, so the functions of the driver need to be non-static.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/Makefile | 2 +- sound/pci/oxygen/xonar_dg.c | 375 ++---------------------------------- sound/pci/oxygen/xonar_dg.h | 19 ++ sound/pci/oxygen/xonar_dg_mixer.c | 381 +++++++++++++++++++++++++++++++++++++ 4 files changed, 412 insertions(+), 365 deletions(-) create mode 100644 sound/pci/oxygen/xonar_dg_mixer.c
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 0f87265..8f4c409 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -1,5 +1,5 @@ snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o -snd-oxygen-objs := oxygen.o xonar_dg.o +snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o snd-virtuoso-objs := virtuoso.o xonar_lib.o \ xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index b277f0d..d0ca6c2 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -124,7 +124,7 @@ int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) return 0; }
-static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) +void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data;
@@ -139,7 +139,7 @@ static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) data->cs4245_shadow[reg] = value; }
-static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) +void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data;
@@ -147,7 +147,7 @@ static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) cs4245_write(chip, reg, value); }
-static void cs4245_init(struct oxygen *chip) +void cs4245_init(struct oxygen *chip) { struct dg *data = chip->model_data;
@@ -182,7 +182,7 @@ static void cs4245_init(struct oxygen *chip) snd_component_add(chip->card, "CS4245"); }
-static void dg_init(struct oxygen *chip) +void dg_init(struct oxygen *chip) { struct dg *data = chip->model_data;
@@ -204,24 +204,24 @@ static void dg_init(struct oxygen *chip) GPIO_INPUT_ROUTE); }
-static void dg_cleanup(struct oxygen *chip) +void dg_cleanup(struct oxygen *chip) { oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); }
-static void dg_suspend(struct oxygen *chip) +void dg_suspend(struct oxygen *chip) { dg_cleanup(chip); }
-static void dg_resume(struct oxygen *chip) +void dg_resume(struct oxygen *chip) { cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); msleep(2500); oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); }
-static void set_cs4245_dac_params(struct oxygen *chip, +void set_cs4245_dac_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; @@ -246,7 +246,7 @@ static void set_cs4245_dac_params(struct oxygen *chip, cs4245_write_spi(chip, CS4245_MCLK_FREQ); }
-static void set_cs4245_adc_params(struct oxygen *chip, +void set_cs4245_adc_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; @@ -271,7 +271,7 @@ static void set_cs4245_adc_params(struct oxygen *chip, cs4245_write_spi(chip, CS4245_MCLK_FREQ); }
-static unsigned int adjust_dg_dac_routing(struct oxygen *chip, +unsigned int adjust_dg_dac_routing(struct oxygen *chip, unsigned int play_routing) { struct dg *data = chip->model_data; @@ -296,333 +296,7 @@ static unsigned int adjust_dg_dac_routing(struct oxygen *chip, return routing; }
-static int output_switch_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[3] = { - "Speakers", "Headphones", "FP Headphones" - }; - - return snd_ctl_enum_info(info, 1, 3, names); -} - -static int output_switch_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - mutex_lock(&chip->mutex); - value->value.enumerated.item[0] = data->output_sel; - mutex_unlock(&chip->mutex); - return 0; -} - -static int output_switch_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - u8 reg; - int changed; - - if (value->value.enumerated.item[0] > 2) - return -EINVAL; - - mutex_lock(&chip->mutex); - changed = value->value.enumerated.item[0] != data->output_sel; - if (changed) { - data->output_sel = value->value.enumerated.item[0]; - - reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & - ~CS4245_A_OUT_SEL_MASK; - reg |= data->output_sel == 2 ? - CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; - cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); - - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, - data->output_sel ? data->hp_vol_att : 0); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, - data->output_sel ? data->hp_vol_att : 0); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->output_sel == 1 ? GPIO_HP_REAR : 0, - GPIO_HP_REAR); - } - mutex_unlock(&chip->mutex); - return changed; -} - -static int hp_volume_offset_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[3] = { - "< 64 ohms", "64-150 ohms", "150-300 ohms" - }; - - return snd_ctl_enum_info(info, 1, 3, names); -} - -static int hp_volume_offset_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - mutex_lock(&chip->mutex); - if (data->hp_vol_att > 2 * 7) - value->value.enumerated.item[0] = 0; - else if (data->hp_vol_att > 0) - value->value.enumerated.item[0] = 1; - else - value->value.enumerated.item[0] = 2; - mutex_unlock(&chip->mutex); - return 0; -} - -static int hp_volume_offset_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - s8 att; - int changed; - - if (value->value.enumerated.item[0] > 2) - return -EINVAL; - att = atts[value->value.enumerated.item[0]]; - mutex_lock(&chip->mutex); - changed = att != data->hp_vol_att; - if (changed) { - data->hp_vol_att = att; - if (data->output_sel) { - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); - } - } - mutex_unlock(&chip->mutex); - return changed; -} - -static int input_vol_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 2; - info->value.integer.min = 2 * -12; - info->value.integer.max = 2 * 12; - return 0; -} - -static int input_vol_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - unsigned int idx = ctl->private_value; - - mutex_lock(&chip->mutex); - value->value.integer.value[0] = data->input_vol[idx][0]; - value->value.integer.value[1] = data->input_vol[idx][1]; - mutex_unlock(&chip->mutex); - return 0; -} - -static int input_vol_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - unsigned int idx = ctl->private_value; - int changed = 0; - - if (value->value.integer.value[0] < 2 * -12 || - value->value.integer.value[0] > 2 * 12 || - value->value.integer.value[1] < 2 * -12 || - value->value.integer.value[1] > 2 * 12) - return -EINVAL; - mutex_lock(&chip->mutex); - changed = data->input_vol[idx][0] != value->value.integer.value[0] || - data->input_vol[idx][1] != value->value.integer.value[1]; - if (changed) { - data->input_vol[idx][0] = value->value.integer.value[0]; - data->input_vol[idx][1] = value->value.integer.value[1]; - if (idx == data->input_sel) { - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[idx][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[idx][1]); - } - } - mutex_unlock(&chip->mutex); - return changed; -} - -static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); - -static int input_sel_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[4] = { - "Mic", "Aux", "Front Mic", "Line" - }; - - return snd_ctl_enum_info(info, 1, 4, names); -} - -static int input_sel_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - mutex_lock(&chip->mutex); - value->value.enumerated.item[0] = data->input_sel; - mutex_unlock(&chip->mutex); - return 0; -} - -static int input_sel_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - static const u8 sel_values[4] = { - CS4245_SEL_MIC, - CS4245_SEL_INPUT_1, - CS4245_SEL_INPUT_2, - CS4245_SEL_INPUT_4 - }; - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - int changed; - - if (value->value.enumerated.item[0] > 3) - return -EINVAL; - - mutex_lock(&chip->mutex); - changed = value->value.enumerated.item[0] != data->input_sel; - if (changed) { - data->input_sel = value->value.enumerated.item[0]; - - cs4245_write(chip, CS4245_ANALOG_IN, - (data->cs4245_shadow[CS4245_ANALOG_IN] & - ~CS4245_SEL_MASK) | - sel_values[data->input_sel]); - - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[data->input_sel][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[data->input_sel][1]); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->input_sel ? 0 : GPIO_INPUT_ROUTE, - GPIO_INPUT_ROUTE); - } - mutex_unlock(&chip->mutex); - return changed; -} - -static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) -{ - static const char *const names[2] = { "Active", "Frozen" }; - - return snd_ctl_enum_info(info, 1, 2, names); -} - -static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - value->value.enumerated.item[0] = - !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); - return 0; -} - -static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - u8 reg; - int changed; - - mutex_lock(&chip->mutex); - reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; - if (value->value.enumerated.item[0]) - reg |= CS4245_HPF_FREEZE; - changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; - if (changed) - cs4245_write(chip, CS4245_ADC_CTRL, reg); - mutex_unlock(&chip->mutex); - return changed; -} - -#define INPUT_VOLUME(xname, index) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .info = input_vol_info, \ - .get = input_vol_get, \ - .put = input_vol_put, \ - .tlv = { .p = cs4245_pga_db_scale }, \ - .private_value = index, \ -} -static const struct snd_kcontrol_new dg_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Output Playback Enum", - .info = output_switch_info, - .get = output_switch_get, - .put = output_switch_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphones Impedance Playback Enum", - .info = hp_volume_offset_info, - .get = hp_volume_offset_get, - .put = hp_volume_offset_put, - }, - INPUT_VOLUME("Mic Capture Volume", 0), - INPUT_VOLUME("Aux Capture Volume", 1), - INPUT_VOLUME("Front Mic Capture Volume", 2), - INPUT_VOLUME("Line Capture Volume", 3), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = input_sel_info, - .get = input_sel_get, - .put = input_sel_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "ADC High-pass Filter Capture Enum", - .info = hpf_info, - .get = hpf_get, - .put = hpf_put, - }, -}; - -static int dg_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "Master Playback ", 16)) - return 1; - return 0; -} - -static int dg_mixer_init(struct oxygen *chip) -{ - unsigned int i; - int err; - - for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { - err = snd_ctl_add(chip->card, - snd_ctl_new1(&dg_controls[i], chip)); - if (err < 0) - return err; - } - return 0; -} - -static void dump_cs4245_registers(struct oxygen *chip, +void dump_cs4245_registers(struct oxygen *chip, struct snd_info_buffer *buffer) { struct dg *data = chip->model_data; @@ -634,30 +308,3 @@ static void dump_cs4245_registers(struct oxygen *chip, snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]); snd_iprintf(buffer, "\n"); } - -struct oxygen_model model_xonar_dg = { - .longname = "C-Media Oxygen HD Audio", - .chip = "CMI8786", - .init = dg_init, - .control_filter = dg_control_filter, - .mixer_init = dg_mixer_init, - .cleanup = dg_cleanup, - .suspend = dg_suspend, - .resume = dg_resume, - .set_dac_params = set_cs4245_dac_params, - .set_adc_params = set_cs4245_adc_params, - .adjust_dac_routing = adjust_dg_dac_routing, - .dump_registers = dump_cs4245_registers, - .model_data_size = sizeof(struct dg), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | - CAPTURE_1_FROM_SPDIF, - .dac_channels_pcm = 6, - .dac_channels_mixer = 0, - .function_flags = OXYGEN_FUNCTION_SPI, - .dac_mclks = OXYGEN_MCLKS(256, 128, 128), - .adc_mclks = OXYGEN_MCLKS(256, 128, 128), - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 578d7a2..35c8ec7 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -34,6 +34,25 @@ struct dg { u8 hp_vol_att; };
+/* Xonar DG control routines */ +int cs4245_write_spi(struct oxygen *chip, u8 reg); +int cs4245_read_spi(struct oxygen *chip, u8 reg); +int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op); +void dg_init(struct oxygen *chip); +void set_cs4245_dac_params(struct oxygen *chip, + struct snd_pcm_hw_params *params); +void set_cs4245_adc_params(struct oxygen *chip, + struct snd_pcm_hw_params *params); +unsigned int adjust_dg_dac_routing(struct oxygen *chip, + unsigned int play_routing); +void dump_cs4245_registers(struct oxygen *chip, + struct snd_info_buffer *buffer); +void dg_suspend(struct oxygen *chip); +void dg_resume(struct oxygen *chip); +void dg_cleanup(struct oxygen *chip); +void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value); +void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value); + extern struct oxygen_model model_xonar_dg;
#endif diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c new file mode 100644 index 0000000..a2cd0d3 --- /dev/null +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -0,0 +1,381 @@ +/* + * Mixer controls for the Xonar DG/DGX + * + * Copyright (c) Clemens Ladisch clemens@ladisch.de + * Copyright (c) Roman Volkov v1ron@mail.ru + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see http://www.gnu.org/licenses/. + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/pcm.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "xonar_dg.h" +#include "cs4245.h" + +static int output_switch_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Speakers", "Headphones", "FP Headphones" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int output_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + value->value.enumerated.item[0] = data->output_sel; + mutex_unlock(&chip->mutex); + return 0; +} + +static int output_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + u8 reg; + int changed; + + if (value->value.enumerated.item[0] > 2) + return -EINVAL; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != data->output_sel; + if (changed) { + data->output_sel = value->value.enumerated.item[0]; + + reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & + ~CS4245_A_OUT_SEL_MASK; + reg |= data->output_sel == 2 ? + CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; + cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); + + cs4245_write_cached(chip, CS4245_DAC_A_CTRL, + data->output_sel ? data->hp_vol_att : 0); + cs4245_write_cached(chip, CS4245_DAC_B_CTRL, + data->output_sel ? data->hp_vol_att : 0); + + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + data->output_sel == 1 ? GPIO_HP_REAR : 0, + GPIO_HP_REAR); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int hp_volume_offset_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "< 64 ohms", "64-150 ohms", "150-300 ohms" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int hp_volume_offset_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + if (data->hp_vol_att > 2 * 7) + value->value.enumerated.item[0] = 0; + else if (data->hp_vol_att > 0) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + mutex_unlock(&chip->mutex); + return 0; +} + +static int hp_volume_offset_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + s8 att; + int changed; + + if (value->value.enumerated.item[0] > 2) + return -EINVAL; + att = atts[value->value.enumerated.item[0]]; + mutex_lock(&chip->mutex); + changed = att != data->hp_vol_att; + if (changed) { + data->hp_vol_att = att; + if (data->output_sel) { + cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); + cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); + } + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int input_vol_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 2 * -12; + info->value.integer.max = 2 * 12; + return 0; +} + +static int input_vol_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + unsigned int idx = ctl->private_value; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = data->input_vol[idx][0]; + value->value.integer.value[1] = data->input_vol[idx][1]; + mutex_unlock(&chip->mutex); + return 0; +} + +static int input_vol_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + unsigned int idx = ctl->private_value; + int changed = 0; + + if (value->value.integer.value[0] < 2 * -12 || + value->value.integer.value[0] > 2 * 12 || + value->value.integer.value[1] < 2 * -12 || + value->value.integer.value[1] > 2 * 12) + return -EINVAL; + mutex_lock(&chip->mutex); + changed = data->input_vol[idx][0] != value->value.integer.value[0] || + data->input_vol[idx][1] != value->value.integer.value[1]; + if (changed) { + data->input_vol[idx][0] = value->value.integer.value[0]; + data->input_vol[idx][1] = value->value.integer.value[1]; + if (idx == data->input_sel) { + cs4245_write_cached(chip, CS4245_PGA_A_CTRL, + data->input_vol[idx][0]); + cs4245_write_cached(chip, CS4245_PGA_B_CTRL, + data->input_vol[idx][1]); + } + } + mutex_unlock(&chip->mutex); + return changed; +} + +static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); + +static int input_sel_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[4] = { + "Mic", "Aux", "Front Mic", "Line" + }; + + return snd_ctl_enum_info(info, 1, 4, names); +} + +static int input_sel_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + value->value.enumerated.item[0] = data->input_sel; + mutex_unlock(&chip->mutex); + return 0; +} + +static int input_sel_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + static const u8 sel_values[4] = { + CS4245_SEL_MIC, + CS4245_SEL_INPUT_1, + CS4245_SEL_INPUT_2, + CS4245_SEL_INPUT_4 + }; + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + int changed; + + if (value->value.enumerated.item[0] > 3) + return -EINVAL; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != data->input_sel; + if (changed) { + data->input_sel = value->value.enumerated.item[0]; + + cs4245_write(chip, CS4245_ANALOG_IN, + (data->cs4245_shadow[CS4245_ANALOG_IN] & + ~CS4245_SEL_MASK) | + sel_values[data->input_sel]); + + cs4245_write_cached(chip, CS4245_PGA_A_CTRL, + data->input_vol[data->input_sel][0]); + cs4245_write_cached(chip, CS4245_PGA_B_CTRL, + data->input_vol[data->input_sel][1]); + + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + data->input_sel ? 0 : GPIO_INPUT_ROUTE, + GPIO_INPUT_ROUTE); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { "Active", "Frozen" }; + + return snd_ctl_enum_info(info, 1, 2, names); +} + +static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + value->value.enumerated.item[0] = + !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); + return 0; +} + +static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + u8 reg; + int changed; + + mutex_lock(&chip->mutex); + reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; + if (value->value.enumerated.item[0]) + reg |= CS4245_HPF_FREEZE; + changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; + if (changed) + cs4245_write(chip, CS4245_ADC_CTRL, reg); + mutex_unlock(&chip->mutex); + return changed; +} + +#define INPUT_VOLUME(xname, index) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = input_vol_info, \ + .get = input_vol_get, \ + .put = input_vol_put, \ + .tlv = { .p = cs4245_pga_db_scale }, \ + .private_value = index, \ +} +static const struct snd_kcontrol_new dg_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output Playback Enum", + .info = output_switch_info, + .get = output_switch_get, + .put = output_switch_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphones Impedance Playback Enum", + .info = hp_volume_offset_info, + .get = hp_volume_offset_get, + .put = hp_volume_offset_put, + }, + INPUT_VOLUME("Mic Capture Volume", 0), + INPUT_VOLUME("Aux Capture Volume", 1), + INPUT_VOLUME("Front Mic Capture Volume", 2), + INPUT_VOLUME("Line Capture Volume", 3), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = input_sel_info, + .get = input_sel_get, + .put = input_sel_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC High-pass Filter Capture Enum", + .info = hpf_info, + .get = hpf_get, + .put = hpf_put, + }, +}; + +static int dg_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "Master Playback ", 16)) + return 1; + return 0; +} + +static int dg_mixer_init(struct oxygen *chip) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&dg_controls[i], chip)); + if (err < 0) + return err; + } + return 0; +} + +struct oxygen_model model_xonar_dg = { + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8786", + .init = dg_init, + .control_filter = dg_control_filter, + .mixer_init = dg_mixer_init, + .cleanup = dg_cleanup, + .suspend = dg_suspend, + .resume = dg_resume, + .set_dac_params = set_cs4245_dac_params, + .set_adc_params = set_cs4245_adc_params, + .adjust_dac_routing = adjust_dg_dac_routing, + .dump_registers = dump_cs4245_registers, + .model_data_size = sizeof(struct dg), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels_pcm = 6, + .dac_channels_mixer = 0, + .function_flags = OXYGEN_FUNCTION_SPI, + .dac_mclks = OXYGEN_MCLKS(256, 128, 128), + .adc_mclks = OXYGEN_MCLKS(256, 128, 128), + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +};
Actually CS4245 connected to the I2S channel 1 for capture, not channel 2. Otherwise capturing and playback does not work for CS4245.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg_mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index a2cd0d3..7e9fc4a 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -369,7 +369,7 @@ struct oxygen_model model_xonar_dg = { .model_data_size = sizeof(struct dg), .device_config = PLAYBACK_0_TO_I2S | PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | + CAPTURE_0_FROM_I2S_1 | CAPTURE_1_FROM_SPDIF, .dac_channels_pcm = 6, .dac_channels_mixer = 0,
Change the order of elements in the output select control. This will reduce the number of relay switches. Change 'put' function to call the oxygen_update_dac_routing() function. Otherwise multichannel playback does not work. Also there is a new function to apply settings, this prevents from duplicating the code.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 2 +- sound/pci/oxygen/xonar_dg.h | 3 +- sound/pci/oxygen/xonar_dg_mixer.c | 76 ++++++++++++++++++++++--------------- 3 files changed, 48 insertions(+), 33 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index d0ca6c2..f504f4f 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -277,7 +277,7 @@ unsigned int adjust_dg_dac_routing(struct oxygen *chip, struct dg *data = chip->model_data; unsigned int routing = 0;
- switch (data->pcm_output) { + switch (data->output_sel) { case PLAYBACK_DST_HP: case PLAYBACK_DST_HP_FP: oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 35c8ec7..c278809 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -27,8 +27,7 @@ struct dg { /* The shadow copy of the CS4245 register space */ unsigned char cs4245_shadow[17]; /* Output select: headphone/speakers */ - unsigned char pcm_output; - unsigned int output_sel; + unsigned char output_sel; s8 input_vol[4][2]; unsigned int input_sel; u8 hp_vol_att; diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 7e9fc4a..1cc82a7 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -27,17 +27,47 @@ #include "xonar_dg.h" #include "cs4245.h"
-static int output_switch_info(struct snd_kcontrol *ctl, +/* Analog output select */ + +static int output_select_apply(struct oxygen *chip) +{ + struct dg *data = chip->model_data; + + data->cs4245_shadow[CS4245_SIGNAL_SEL] &= + ~CS4245_A_OUT_SEL_MASK; + if (data->output_sel == PLAYBACK_DST_HP) { + /* Mute FP (aux output) amplifier, switch rear jack to CS4245 */ + oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); + } else if (data->output_sel == PLAYBACK_DST_HP_FP) { + /* + * Unmute FP amplifier, switch rear jack to CS4361, + * I2S channels 2,3,4 should be inactive + */ + oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); + data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC; + } else { + /* + * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp., + * and change playback routing + */ + oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); + } + return cs4245_write_spi(chip, CS4245_SIGNAL_SEL); +} + +static int output_select_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[3] = { - "Speakers", "Headphones", "FP Headphones" + "Stereo Headphones", + "Stereo Headphones FP", + "Multichannel", };
return snd_ctl_enum_info(info, 1, 3, names); }
-static int output_switch_get(struct snd_kcontrol *ctl, +static int output_select_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; @@ -49,38 +79,24 @@ static int output_switch_get(struct snd_kcontrol *ctl, return 0; }
-static int output_switch_put(struct snd_kcontrol *ctl, +static int output_select_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; - u8 reg; - int changed; - - if (value->value.enumerated.item[0] > 2) - return -EINVAL; + unsigned int new = value->value.enumerated.item[0]; + int changed = 0; + int ret;
mutex_lock(&chip->mutex); - changed = value->value.enumerated.item[0] != data->output_sel; - if (changed) { - data->output_sel = value->value.enumerated.item[0]; - - reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & - ~CS4245_A_OUT_SEL_MASK; - reg |= data->output_sel == 2 ? - CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; - cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); - - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, - data->output_sel ? data->hp_vol_att : 0); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, - data->output_sel ? data->hp_vol_att : 0); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->output_sel == 1 ? GPIO_HP_REAR : 0, - GPIO_HP_REAR); + if (data->output_sel != new) { + data->output_sel = new; + ret = output_select_apply(chip); + changed = ret >= 0 ? 1 : ret; + oxygen_update_dac_routing(chip); } mutex_unlock(&chip->mutex); + return changed; }
@@ -301,9 +317,9 @@ static const struct snd_kcontrol_new dg_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Analog Output Playback Enum", - .info = output_switch_info, - .get = output_switch_get, - .put = output_switch_put, + .info = output_select_info, + .get = output_select_get, + .put = output_select_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
I tried both variants: volume control and impedance selector. In the first case one minus is that we can't change the volume of multichannel output without additional software volume control. However, I am using this variant for the last three months and this seems good. All multichannel speaker systems have internal amplifier with the volume control included, but not all headphones have this regulator. In the second case, my software volume control does not save the value after reboot.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg_mixer.c | 115 ++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 32 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 1cc82a7..56e0db1 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -100,54 +100,93 @@ static int output_select_put(struct snd_kcontrol *ctl, return changed; }
-static int hp_volume_offset_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) +/* CS4245 Headphone Channels A&B Volume Control */ + +static int hp_stereo_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) { - static const char *const names[3] = { - "< 64 ohms", "64-150 ohms", "150-300 ohms" - }; + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 255; + return 0; +}
- return snd_ctl_enum_info(info, 1, 3, names); +static int hp_stereo_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + unsigned int tmp; + + mutex_lock(&chip->mutex); + tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255; + val->value.integer.value[0] = tmp; + tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255; + val->value.integer.value[1] = tmp; + mutex_unlock(&chip->mutex); + return 0; }
-static int hp_volume_offset_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) +static int hp_stereo_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) { struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; + int ret; + int changed = 0; + long new1 = val->value.integer.value[0]; + long new2 = val->value.integer.value[1]; + + if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0)) + return -EINVAL;
mutex_lock(&chip->mutex); - if (data->hp_vol_att > 2 * 7) - value->value.enumerated.item[0] = 0; - else if (data->hp_vol_att > 0) - value->value.enumerated.item[0] = 1; - else - value->value.enumerated.item[0] = 2; + if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) || + (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) { + data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1; + data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2; + ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL); + if (ret >= 0) + ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL); + changed = ret >= 0 ? 1 : ret; + } + mutex_unlock(&chip->mutex); + + return changed; +} + +/* Headphone Mute */ + +static int hp_mute_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + val->value.integer.value[0] = ((data->cs4245_shadow[CS4245_DAC_CTRL_1] + & CS4245_MUTE_DAC) == 0); mutex_unlock(&chip->mutex); return 0; }
-static int hp_volume_offset_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) +static int hp_mute_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) { - static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; - s8 att; + int ret; int changed;
- if (value->value.enumerated.item[0] > 2) + if (val->value.integer.value[0] > 1) return -EINVAL; - att = atts[value->value.enumerated.item[0]]; mutex_lock(&chip->mutex); - changed = att != data->hp_vol_att; - if (changed) { - data->hp_vol_att = att; - if (data->output_sel) { - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); - } - } + data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC; + data->cs4245_shadow[CS4245_DAC_CTRL_1] |= + (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC; + ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1); + changed = ret >= 0 ? 1 : ret; mutex_unlock(&chip->mutex); return changed; } @@ -313,6 +352,7 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) .tlv = { .p = cs4245_pga_db_scale }, \ .private_value = index, \ } +static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); static const struct snd_kcontrol_new dg_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -323,10 +363,21 @@ static const struct snd_kcontrol_new dg_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphones Impedance Playback Enum", - .info = hp_volume_offset_info, - .get = hp_volume_offset_get, - .put = hp_volume_offset_put, + .name = "Headphone Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = hp_stereo_volume_info, + .get = hp_stereo_volume_get, + .put = hp_stereo_volume_put, + .tlv = { .p = hp_db_scale, }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_mono_info, + .get = hp_mute_get, + .put = hp_mute_put, }, INPUT_VOLUME("Mic Capture Volume", 0), INPUT_VOLUME("Aux Capture Volume", 1),
Modify the input_vol_* functions to use the new SPI routines, There is a new applying function that will be called when the capture source changed.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.h | 3 ++- sound/pci/oxygen/xonar_dg_mixer.c | 32 +++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index c278809..eb16592 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -28,7 +28,8 @@ struct dg { unsigned char cs4245_shadow[17]; /* Output select: headphone/speakers */ unsigned char output_sel; - s8 input_vol[4][2]; + /* The volumes for all capture sources */ + char input_vol[4][2]; unsigned int input_sel; u8 hp_vol_att; }; diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 56e0db1..c23cdde 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -191,6 +191,22 @@ static int hp_mute_put(struct snd_kcontrol *ctl, return changed; }
+/* Capture volume for all sources */ + +static int input_volume_apply(struct oxygen *chip, char left, + char right) +{ + struct dg *data = chip->model_data; + int ret; + + data->cs4245_shadow[CS4245_PGA_A_CTRL] = left; + data->cs4245_shadow[CS4245_PGA_B_CTRL] = right; + ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL); + if (ret < 0) + return ret; + return cs4245_write_spi(chip, CS4245_PGA_B_CTRL); +} + static int input_vol_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -222,6 +238,7 @@ static int input_vol_put(struct snd_kcontrol *ctl, struct dg *data = chip->model_data; unsigned int idx = ctl->private_value; int changed = 0; + int ret = 0;
if (value->value.integer.value[0] < 2 * -12 || value->value.integer.value[0] > 2 * 12 || @@ -235,18 +252,16 @@ static int input_vol_put(struct snd_kcontrol *ctl, data->input_vol[idx][0] = value->value.integer.value[0]; data->input_vol[idx][1] = value->value.integer.value[1]; if (idx == data->input_sel) { - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[idx][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[idx][1]); + ret = input_volume_apply(chip, + data->input_vol[idx][0], + data->input_vol[idx][1]); } + changed = ret >= 0 ? 1 : ret; } mutex_unlock(&chip->mutex); return changed; }
-static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); - static int input_sel_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -346,13 +361,16 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) #define INPUT_VOLUME(xname, index) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .info = input_vol_info, \ .get = input_vol_get, \ .put = input_vol_put, \ - .tlv = { .p = cs4245_pga_db_scale }, \ + .tlv = { .p = pga_db_scale }, \ .private_value = index, \ } static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); +static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200); static const struct snd_kcontrol_new dg_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
First of all, we should not touch the GPIOs. They are not for selecting the capture source, but they seems just enable the whole audio input curcuit. The 'put' function calls the 'apply' functions to change register values. Change the order of capture sources.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.h | 3 ++- sound/pci/oxygen/xonar_dg_mixer.c | 52 ++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 25 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index eb16592..79192a5 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -30,7 +30,8 @@ struct dg { unsigned char output_sel; /* The volumes for all capture sources */ char input_vol[4][2]; - unsigned int input_sel; + /* Input select: mic/fp mic/line/aux */ + unsigned char input_sel; u8 hp_vol_att; };
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index c23cdde..d6ececb 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -262,11 +262,27 @@ static int input_vol_put(struct snd_kcontrol *ctl, return changed; }
+/* Capture Source */ + +static int input_source_apply(struct oxygen *chip) +{ + struct dg *data = chip->model_data; + + data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK; + if (data->input_sel == CAPTURE_SRC_FP_MIC) + data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2; + else if (data->input_sel == CAPTURE_SRC_LINE) + data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4; + else if (data->input_sel != CAPTURE_SRC_MIC) + data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1; + return cs4245_write_spi(chip, CS4245_ANALOG_IN); +} + static int input_sel_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[4] = { - "Mic", "Aux", "Front Mic", "Line" + "Mic", "Front Mic", "Line", "Aux" };
return snd_ctl_enum_info(info, 1, 4, names); @@ -287,15 +303,10 @@ static int input_sel_get(struct snd_kcontrol *ctl, static int input_sel_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { - static const u8 sel_values[4] = { - CS4245_SEL_MIC, - CS4245_SEL_INPUT_1, - CS4245_SEL_INPUT_2, - CS4245_SEL_INPUT_4 - }; struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; int changed; + int ret;
if (value->value.enumerated.item[0] > 3) return -EINVAL; @@ -305,19 +316,12 @@ static int input_sel_put(struct snd_kcontrol *ctl, if (changed) { data->input_sel = value->value.enumerated.item[0];
- cs4245_write(chip, CS4245_ANALOG_IN, - (data->cs4245_shadow[CS4245_ANALOG_IN] & - ~CS4245_SEL_MASK) | - sel_values[data->input_sel]); - - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[data->input_sel][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[data->input_sel][1]); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->input_sel ? 0 : GPIO_INPUT_ROUTE, - GPIO_INPUT_ROUTE); + ret = input_source_apply(chip); + if (ret >= 0) + ret = input_volume_apply(chip, + data->input_vol[data->input_sel][0], + data->input_vol[data->input_sel][1]); + changed = ret >= 0 ? 1 : ret; } mutex_unlock(&chip->mutex); return changed; @@ -397,10 +401,10 @@ static const struct snd_kcontrol_new dg_controls[] = { .get = hp_mute_get, .put = hp_mute_put, }, - INPUT_VOLUME("Mic Capture Volume", 0), - INPUT_VOLUME("Aux Capture Volume", 1), - INPUT_VOLUME("Front Mic Capture Volume", 2), - INPUT_VOLUME("Line Capture Volume", 3), + INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC), + INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC), + INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE), + INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source",
Change the 'put' function of the High-pass filter control to use the new SPI functions.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg_mixer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index d6ececb..4b80675 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -327,6 +327,8 @@ static int input_sel_put(struct snd_kcontrol *ctl, return changed; }
+/* ADC High-pass filter */ + static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[2] = { "Active", "Frozen" }; @@ -356,8 +358,10 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) if (value->value.enumerated.item[0]) reg |= CS4245_HPF_FREEZE; changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; - if (changed) - cs4245_write(chip, CS4245_ADC_CTRL, reg); + if (changed) { + data->cs4245_shadow[CS4245_ADC_CTRL] = reg; + cs4245_write_spi(chip, CS4245_ADC_CTRL); + } mutex_unlock(&chip->mutex); return changed; }
Remove old SPI control functions, change anti-pop init sequence, remove some garbage from structures. The 'Apply' functions must be called at the mixer initialization, otherwise mixer settings sometimes will not be applied at startup.
Signed-off-by: Roman Volkov v1ron@mail.ru --- sound/pci/oxygen/xonar_dg.c | 36 ++++++------------------------------ sound/pci/oxygen/xonar_dg.h | 3 --- sound/pci/oxygen/xonar_dg_mixer.c | 5 +++++ 3 files changed, 11 insertions(+), 33 deletions(-)
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index f504f4f..7ca7396 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -124,29 +124,6 @@ int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) return 0; }
-void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) -{ - struct dg *data = chip->model_data; - - oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | - OXYGEN_SPI_DATA_LENGTH_3 | - OXYGEN_SPI_CLOCK_1280 | - (0 << OXYGEN_SPI_CODEC_SHIFT) | - OXYGEN_SPI_CEN_LATCH_CLOCK_HI, - CS4245_SPI_ADDRESS_S | - CS4245_SPI_WRITE_S | - (reg << 8) | value); - data->cs4245_shadow[reg] = value; -} - -void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) -{ - struct dg *data = chip->model_data; - - if (value != data->cs4245_shadow[reg]) - cs4245_write(chip, reg, value); -} - void cs4245_init(struct oxygen *chip) { struct dg *data = chip->model_data; @@ -175,8 +152,8 @@ void cs4245_init(struct oxygen *chip) CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0; data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0; - data->cs4245_shadow[CS4245_DAC_A_CTRL] = 4; - data->cs4245_shadow[CS4245_DAC_B_CTRL] = 4; + data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8; + data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); snd_component_add(chip->card, "CS4245"); @@ -186,9 +163,8 @@ void dg_init(struct oxygen *chip) { struct dg *data = chip->model_data;
- data->output_sel = 0; - data->input_sel = 3; - data->hp_vol_att = 2 * 16; + data->output_sel = PLAYBACK_DST_HP_FP; + data->input_sel = CAPTURE_SRC_MIC;
/* * The pin XGPIO1 as XGPIO1, gpios 8,7,6,5,1,0 as outputs. @@ -197,11 +173,11 @@ void dg_init(struct oxygen *chip) cs4245_init(chip); oxygen_write16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE); - oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE); - /* Anti-pop delay */ + /* Anti-pop delay, wait some time before enabling the output */ msleep(2500); oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE); + }
void dg_cleanup(struct oxygen *chip) diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 79192a5..d7b3dd2 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -32,7 +32,6 @@ struct dg { char input_vol[4][2]; /* Input select: mic/fp mic/line/aux */ unsigned char input_sel; - u8 hp_vol_att; };
/* Xonar DG control routines */ @@ -51,8 +50,6 @@ void dump_cs4245_registers(struct oxygen *chip, void dg_suspend(struct oxygen *chip); void dg_resume(struct oxygen *chip); void dg_cleanup(struct oxygen *chip); -void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value); -void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value);
extern struct oxygen_model model_xonar_dg;
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 4b80675..70fc6b6 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -437,12 +437,17 @@ static int dg_mixer_init(struct oxygen *chip) unsigned int i; int err;
+ output_select_apply(chip); + input_source_apply(chip); + oxygen_update_dac_routing(chip); + for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { err = snd_ctl_add(chip->card, snd_ctl_new1(&dg_controls[i], chip)); if (err < 0) return err; } + return 0; }
The following changes since commit d8ec26d7f8287f5788a494f56e8814210f0e64be:
Linux 3.13 (2014-01-19 18:40:07 -0800)
are available in the git repository at:
git://git.alsa-project.org/alsa-kprivate.git xonar-dg
for you to fetch changes up to 3f49a66f6ceff1c87b49858644771c17763902ab:
ALSA: oxygen: Xonar DG(X): cleanup and minor changes (2014-01-29 20:45:53 +0100)
----------------------------------------------------------------
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.
---------------------------------------------------------------- Roman Volkov (19): ALSA: oxygen: add the separate SPI waiting function ALSA: oxygen: modify the SPI writing function ALSA: oxygen: add mute mask for the OXYGEN_PLAY_ROUTING register ALSA: oxygen: export oxygen_update_dac_routing symbol ALSA: oxygen: change description of the xonar_dg.c file ALSA: oxygen: additional definitions for the Xonar DG/DGX card ALSA: oxygen: Xonar DG(X): add new CS4245 SPI functions ALSA: oxygen: Xonar DG(X): modify initialization functions ALSA: oxygen: Xonar DG(X): modify DAC/ADC parameters function ALSA: oxygen: modify adjust_dg_dac_routing function ALSA: oxygen: modify CS4245 register dumping function ALSA: oxygen: Xonar DG(X): move the mixer code into another file ALSA: oxygen: Xonar DG(X): capture from I2S channel 1, not 2 ALSA: oxygen: Xonar DG(X): modify playback output select ALSA: oxygen: Xonar DG(X): use headphone volume control ALSA: oxygen: Xonar DG(X): modify capture volume functions ALSA: oxygen: Xonar DG(X): modify input select functions ALSA: oxygen: Xonar DG(X): modify high-pass filter control ALSA: oxygen: Xonar DG(X): cleanup and minor changes
sound/pci/oxygen/Makefile | 2 +- sound/pci/oxygen/cs4245.h | 7 +- sound/pci/oxygen/oxygen.h | 2 +- sound/pci/oxygen/oxygen_io.c | 25 +- sound/pci/oxygen/oxygen_mixer.c | 1 + sound/pci/oxygen/oxygen_regs.h | 1 + sound/pci/oxygen/xonar_dg.c | 651 ++++++++---------------------- sound/pci/oxygen/xonar_dg.h | 48 +++ sound/pci/oxygen/xonar_dg_mixer.c | 477 ++++++++++++++++++++++ 9 files changed, 713 insertions(+), 501 deletions(-) create mode 100644 sound/pci/oxygen/xonar_dg_mixer.c
At Wed, 29 Jan 2014 22:06:46 +0100, Clemens Ladisch wrote:
The following changes since commit d8ec26d7f8287f5788a494f56e8814210f0e64be:
Linux 3.13 (2014-01-19 18:40:07 -0800)
are available in the git repository at:
git://git.alsa-project.org/alsa-kprivate.git xonar-dg
for you to fetch changes up to 3f49a66f6ceff1c87b49858644771c17763902ab:
ALSA: oxygen: Xonar DG(X): cleanup and minor changes (2014-01-29 20:45:53 +0100)
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.
OK, I pulled now for the next 3.14 merge, since these all look good and are local changes.
thanks,
Takashi
Roman Volkov (19): ALSA: oxygen: add the separate SPI waiting function ALSA: oxygen: modify the SPI writing function ALSA: oxygen: add mute mask for the OXYGEN_PLAY_ROUTING register ALSA: oxygen: export oxygen_update_dac_routing symbol ALSA: oxygen: change description of the xonar_dg.c file ALSA: oxygen: additional definitions for the Xonar DG/DGX card ALSA: oxygen: Xonar DG(X): add new CS4245 SPI functions ALSA: oxygen: Xonar DG(X): modify initialization functions ALSA: oxygen: Xonar DG(X): modify DAC/ADC parameters function ALSA: oxygen: modify adjust_dg_dac_routing function ALSA: oxygen: modify CS4245 register dumping function ALSA: oxygen: Xonar DG(X): move the mixer code into another file ALSA: oxygen: Xonar DG(X): capture from I2S channel 1, not 2 ALSA: oxygen: Xonar DG(X): modify playback output select ALSA: oxygen: Xonar DG(X): use headphone volume control ALSA: oxygen: Xonar DG(X): modify capture volume functions ALSA: oxygen: Xonar DG(X): modify input select functions ALSA: oxygen: Xonar DG(X): modify high-pass filter control ALSA: oxygen: Xonar DG(X): cleanup and minor changes
sound/pci/oxygen/Makefile | 2 +- sound/pci/oxygen/cs4245.h | 7 +- sound/pci/oxygen/oxygen.h | 2 +- sound/pci/oxygen/oxygen_io.c | 25 +- sound/pci/oxygen/oxygen_mixer.c | 1 + sound/pci/oxygen/oxygen_regs.h | 1 + sound/pci/oxygen/xonar_dg.c | 651 ++++++++---------------------- sound/pci/oxygen/xonar_dg.h | 48 +++ sound/pci/oxygen/xonar_dg_mixer.c | 477 ++++++++++++++++++++++ 9 files changed, 713 insertions(+), 501 deletions(-) create mode 100644 sound/pci/oxygen/xonar_dg_mixer.c
participants (4)
-
Clemens Ladisch
-
Mark Brown
-
Roman Volkov
-
Takashi Iwai