162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * card driver for the Xonar DG/DGX 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 662306a36Sopenharmony_ci * Copyright (c) Roman Volkov <v1ron@mail.ru> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Xonar DG/DGX 1162306a36Sopenharmony_ci * ------------ 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * CS4245 and CS4361 both will mute all outputs if any clock ratio 1462306a36Sopenharmony_ci * is invalid. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * CMI8788: 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * SPI 0 -> CS4245 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Playback: 2162306a36Sopenharmony_ci * I²S 1 -> CS4245 2262306a36Sopenharmony_ci * I²S 2 -> CS4361 (center/LFE) 2362306a36Sopenharmony_ci * I²S 3 -> CS4361 (surround) 2462306a36Sopenharmony_ci * I²S 4 -> CS4361 (front) 2562306a36Sopenharmony_ci * Capture: 2662306a36Sopenharmony_ci * I²S ADC 1 <- CS4245 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * GPIO 3 <- ? 2962306a36Sopenharmony_ci * GPIO 4 <- headphone detect 3062306a36Sopenharmony_ci * GPIO 5 -> enable ADC analog circuit for the left channel 3162306a36Sopenharmony_ci * GPIO 6 -> enable ADC analog circuit for the right channel 3262306a36Sopenharmony_ci * GPIO 7 -> switch green rear output jack between CS4245 and the first 3362306a36Sopenharmony_ci * channel of CS4361 (mechanical relay) 3462306a36Sopenharmony_ci * GPIO 8 -> enable output to speakers 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * CS4245: 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * input 0 <- mic 3962306a36Sopenharmony_ci * input 1 <- aux 4062306a36Sopenharmony_ci * input 2 <- front mic 4162306a36Sopenharmony_ci * input 4 <- line 4262306a36Sopenharmony_ci * DAC out -> headphones 4362306a36Sopenharmony_ci * aux out -> front panel headphones 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <linux/pci.h> 4762306a36Sopenharmony_ci#include <linux/delay.h> 4862306a36Sopenharmony_ci#include <sound/control.h> 4962306a36Sopenharmony_ci#include <sound/core.h> 5062306a36Sopenharmony_ci#include <sound/info.h> 5162306a36Sopenharmony_ci#include <sound/pcm.h> 5262306a36Sopenharmony_ci#include <sound/tlv.h> 5362306a36Sopenharmony_ci#include "oxygen.h" 5462306a36Sopenharmony_ci#include "xonar_dg.h" 5562306a36Sopenharmony_ci#include "cs4245.h" 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciint cs4245_write_spi(struct oxygen *chip, u8 reg) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct dg *data = chip->model_data; 6062306a36Sopenharmony_ci unsigned int packet; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci packet = reg << 8; 6362306a36Sopenharmony_ci packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16; 6462306a36Sopenharmony_ci packet |= data->cs4245_shadow[reg]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | 6762306a36Sopenharmony_ci OXYGEN_SPI_DATA_LENGTH_3 | 6862306a36Sopenharmony_ci OXYGEN_SPI_CLOCK_1280 | 6962306a36Sopenharmony_ci (0 << OXYGEN_SPI_CODEC_SHIFT) | 7062306a36Sopenharmony_ci OXYGEN_SPI_CEN_LATCH_CLOCK_HI, 7162306a36Sopenharmony_ci packet); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciint cs4245_read_spi(struct oxygen *chip, u8 addr) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct dg *data = chip->model_data; 7762306a36Sopenharmony_ci int ret; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | 8062306a36Sopenharmony_ci OXYGEN_SPI_DATA_LENGTH_2 | 8162306a36Sopenharmony_ci OXYGEN_SPI_CEN_LATCH_CLOCK_HI | 8262306a36Sopenharmony_ci OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), 8362306a36Sopenharmony_ci ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr); 8462306a36Sopenharmony_ci if (ret < 0) 8562306a36Sopenharmony_ci return ret; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | 8862306a36Sopenharmony_ci OXYGEN_SPI_DATA_LENGTH_2 | 8962306a36Sopenharmony_ci OXYGEN_SPI_CEN_LATCH_CLOCK_HI | 9062306a36Sopenharmony_ci OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), 9162306a36Sopenharmony_ci (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8); 9262306a36Sopenharmony_ci if (ret < 0) 9362306a36Sopenharmony_ci return ret; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciint cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct dg *data = chip->model_data; 10362306a36Sopenharmony_ci unsigned char addr; 10462306a36Sopenharmony_ci int ret; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) { 10762306a36Sopenharmony_ci ret = (op == CS4245_SAVE_TO_SHADOW ? 10862306a36Sopenharmony_ci cs4245_read_spi(chip, addr) : 10962306a36Sopenharmony_ci cs4245_write_spi(chip, addr)); 11062306a36Sopenharmony_ci if (ret < 0) 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void cs4245_init(struct oxygen *chip) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct dg *data = chip->model_data; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* save the initial state: codec version, registers */ 12162306a36Sopenharmony_ci cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * Power up the CODEC internals, enable soft ramp & zero cross, work in 12562306a36Sopenharmony_ci * async. mode, enable aux output from DAC. Invert DAC output as in the 12662306a36Sopenharmony_ci * Windows driver. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci data->cs4245_shadow[CS4245_POWER_CTRL] = 0; 12962306a36Sopenharmony_ci data->cs4245_shadow[CS4245_SIGNAL_SEL] = 13062306a36Sopenharmony_ci CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH; 13162306a36Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_CTRL_1] = 13262306a36Sopenharmony_ci CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST; 13362306a36Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_CTRL_2] = 13462306a36Sopenharmony_ci CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC; 13562306a36Sopenharmony_ci data->cs4245_shadow[CS4245_ADC_CTRL] = 13662306a36Sopenharmony_ci CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; 13762306a36Sopenharmony_ci data->cs4245_shadow[CS4245_ANALOG_IN] = 13862306a36Sopenharmony_ci CS4245_PGA_SOFT | CS4245_PGA_ZERO; 13962306a36Sopenharmony_ci data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0; 14062306a36Sopenharmony_ci data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0; 14162306a36Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8; 14262306a36Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); 14562306a36Sopenharmony_ci snd_component_add(chip->card, "CS4245"); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_civoid dg_init(struct oxygen *chip) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct dg *data = chip->model_data; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci data->output_sel = PLAYBACK_DST_HP_FP; 15362306a36Sopenharmony_ci data->input_sel = CAPTURE_SRC_MIC; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci cs4245_init(chip); 15662306a36Sopenharmony_ci oxygen_write16(chip, OXYGEN_GPIO_CONTROL, 15762306a36Sopenharmony_ci GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE); 15862306a36Sopenharmony_ci /* anti-pop delay, wait some time before enabling the output */ 15962306a36Sopenharmony_ci msleep(2500); 16062306a36Sopenharmony_ci oxygen_write16(chip, OXYGEN_GPIO_DATA, 16162306a36Sopenharmony_ci GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid dg_cleanup(struct oxygen *chip) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid dg_suspend(struct oxygen *chip) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci dg_cleanup(chip); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid dg_resume(struct oxygen *chip) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); 17762306a36Sopenharmony_ci msleep(2500); 17862306a36Sopenharmony_ci oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_civoid set_cs4245_dac_params(struct oxygen *chip, 18262306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct dg *data = chip->model_data; 18562306a36Sopenharmony_ci unsigned char dac_ctrl; 18662306a36Sopenharmony_ci unsigned char mclk_freq; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; 18962306a36Sopenharmony_ci mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK; 19062306a36Sopenharmony_ci if (params_rate(params) <= 50000) { 19162306a36Sopenharmony_ci dac_ctrl |= CS4245_DAC_FM_SINGLE; 19262306a36Sopenharmony_ci mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; 19362306a36Sopenharmony_ci } else if (params_rate(params) <= 100000) { 19462306a36Sopenharmony_ci dac_ctrl |= CS4245_DAC_FM_DOUBLE; 19562306a36Sopenharmony_ci mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci dac_ctrl |= CS4245_DAC_FM_QUAD; 19862306a36Sopenharmony_ci mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl; 20162306a36Sopenharmony_ci data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; 20262306a36Sopenharmony_ci cs4245_write_spi(chip, CS4245_DAC_CTRL_1); 20362306a36Sopenharmony_ci cs4245_write_spi(chip, CS4245_MCLK_FREQ); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_civoid set_cs4245_adc_params(struct oxygen *chip, 20762306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct dg *data = chip->model_data; 21062306a36Sopenharmony_ci unsigned char adc_ctrl; 21162306a36Sopenharmony_ci unsigned char mclk_freq; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; 21462306a36Sopenharmony_ci mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK; 21562306a36Sopenharmony_ci if (params_rate(params) <= 50000) { 21662306a36Sopenharmony_ci adc_ctrl |= CS4245_ADC_FM_SINGLE; 21762306a36Sopenharmony_ci mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; 21862306a36Sopenharmony_ci } else if (params_rate(params) <= 100000) { 21962306a36Sopenharmony_ci adc_ctrl |= CS4245_ADC_FM_DOUBLE; 22062306a36Sopenharmony_ci mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; 22162306a36Sopenharmony_ci } else { 22262306a36Sopenharmony_ci adc_ctrl |= CS4245_ADC_FM_QUAD; 22362306a36Sopenharmony_ci mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl; 22662306a36Sopenharmony_ci data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; 22762306a36Sopenharmony_ci cs4245_write_spi(chip, CS4245_ADC_CTRL); 22862306a36Sopenharmony_ci cs4245_write_spi(chip, CS4245_MCLK_FREQ); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline unsigned int shift_bits(unsigned int value, 23262306a36Sopenharmony_ci unsigned int shift_from, 23362306a36Sopenharmony_ci unsigned int shift_to, 23462306a36Sopenharmony_ci unsigned int mask) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci if (shift_from < shift_to) 23762306a36Sopenharmony_ci return (value << (shift_to - shift_from)) & mask; 23862306a36Sopenharmony_ci else 23962306a36Sopenharmony_ci return (value >> (shift_from - shift_to)) & mask; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciunsigned int adjust_dg_dac_routing(struct oxygen *chip, 24362306a36Sopenharmony_ci unsigned int play_routing) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct dg *data = chip->model_data; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci switch (data->output_sel) { 24862306a36Sopenharmony_ci case PLAYBACK_DST_HP: 24962306a36Sopenharmony_ci case PLAYBACK_DST_HP_FP: 25062306a36Sopenharmony_ci oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, 25162306a36Sopenharmony_ci OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 | 25262306a36Sopenharmony_ci OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK); 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case PLAYBACK_DST_MULTICH: 25562306a36Sopenharmony_ci oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, 25662306a36Sopenharmony_ci OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK); 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) | 26062306a36Sopenharmony_ci shift_bits(play_routing, 26162306a36Sopenharmony_ci OXYGEN_PLAY_DAC2_SOURCE_SHIFT, 26262306a36Sopenharmony_ci OXYGEN_PLAY_DAC1_SOURCE_SHIFT, 26362306a36Sopenharmony_ci OXYGEN_PLAY_DAC1_SOURCE_MASK) | 26462306a36Sopenharmony_ci shift_bits(play_routing, 26562306a36Sopenharmony_ci OXYGEN_PLAY_DAC1_SOURCE_SHIFT, 26662306a36Sopenharmony_ci OXYGEN_PLAY_DAC2_SOURCE_SHIFT, 26762306a36Sopenharmony_ci OXYGEN_PLAY_DAC2_SOURCE_MASK) | 26862306a36Sopenharmony_ci shift_bits(play_routing, 26962306a36Sopenharmony_ci OXYGEN_PLAY_DAC0_SOURCE_SHIFT, 27062306a36Sopenharmony_ci OXYGEN_PLAY_DAC3_SOURCE_SHIFT, 27162306a36Sopenharmony_ci OXYGEN_PLAY_DAC3_SOURCE_MASK); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid dump_cs4245_registers(struct oxygen *chip, 27562306a36Sopenharmony_ci struct snd_info_buffer *buffer) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct dg *data = chip->model_data; 27862306a36Sopenharmony_ci unsigned int addr; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci snd_iprintf(buffer, "\nCS4245:"); 28162306a36Sopenharmony_ci cs4245_read_spi(chip, CS4245_INT_STATUS); 28262306a36Sopenharmony_ci for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) 28362306a36Sopenharmony_ci snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]); 28462306a36Sopenharmony_ci snd_iprintf(buffer, "\n"); 28562306a36Sopenharmony_ci} 286