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