brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · b90421a Raw
286 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * card driver for the Xonar DG/DGX4 *5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>6 * Copyright (c) Roman Volkov <v1ron@mail.ru>7 */8 9/*10 * Xonar DG/DGX11 * ------------12 *13 * CS4245 and CS4361 both will mute all outputs if any clock ratio14 * is invalid.15 *16 * CMI8788:17 *18 *   SPI 0 -> CS424519 *20 *   Playback:21 *   I²S 1 -> CS424522 *   I²S 2 -> CS4361 (center/LFE)23 *   I²S 3 -> CS4361 (surround)24 *   I²S 4 -> CS4361 (front)25 *   Capture:26 *   I²S ADC 1 <- CS424527 *28 *   GPIO 3 <- ?29 *   GPIO 4 <- headphone detect30 *   GPIO 5 -> enable ADC analog circuit for the left channel31 *   GPIO 6 -> enable ADC analog circuit for the right channel32 *   GPIO 7 -> switch green rear output jack between CS4245 and the first33 *             channel of CS4361 (mechanical relay)34 *   GPIO 8 -> enable output to speakers35 *36 * CS4245:37 *38 *   input 0 <- mic39 *   input 1 <- aux40 *   input 2 <- front mic41 *   input 4 <- line42 *   DAC out -> headphones43 *   aux out -> front panel headphones44 */45 46#include <linux/pci.h>47#include <linux/delay.h>48#include <sound/control.h>49#include <sound/core.h>50#include <sound/info.h>51#include <sound/pcm.h>52#include <sound/tlv.h>53#include "oxygen.h"54#include "xonar_dg.h"55#include "cs4245.h"56 57int cs4245_write_spi(struct oxygen *chip, u8 reg)58{59	struct dg *data = chip->model_data;60	unsigned int packet;61 62	packet = reg << 8;63	packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;64	packet |= data->cs4245_shadow[reg];65 66	return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |67				OXYGEN_SPI_DATA_LENGTH_3 |68				OXYGEN_SPI_CLOCK_1280 |69				(0 << OXYGEN_SPI_CODEC_SHIFT) |70				OXYGEN_SPI_CEN_LATCH_CLOCK_HI,71				packet);72}73 74int cs4245_read_spi(struct oxygen *chip, u8 addr)75{76	struct dg *data = chip->model_data;77	int ret;78 79	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |80		OXYGEN_SPI_DATA_LENGTH_2 |81		OXYGEN_SPI_CEN_LATCH_CLOCK_HI |82		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),83		((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);84	if (ret < 0)85		return ret;86 87	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |88		OXYGEN_SPI_DATA_LENGTH_2 |89		OXYGEN_SPI_CEN_LATCH_CLOCK_HI |90		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),91		(CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);92	if (ret < 0)93		return ret;94 95	data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);96 97	return 0;98}99 100int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)101{102	struct dg *data = chip->model_data;103	unsigned char addr;104	int ret;105 106	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {107		ret = (op == CS4245_SAVE_TO_SHADOW ?108			cs4245_read_spi(chip, addr) :109			cs4245_write_spi(chip, addr));110		if (ret < 0)111			return ret;112	}113	return 0;114}115 116static void cs4245_init(struct oxygen *chip)117{118	struct dg *data = chip->model_data;119 120	/* save the initial state: codec version, registers */121	cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);122 123	/*124	 * Power up the CODEC internals, enable soft ramp & zero cross, work in125	 * async. mode, enable aux output from DAC. Invert DAC output as in the126	 * Windows driver.127	 */128	data->cs4245_shadow[CS4245_POWER_CTRL] = 0;129	data->cs4245_shadow[CS4245_SIGNAL_SEL] =130		CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;131	data->cs4245_shadow[CS4245_DAC_CTRL_1] =132		CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;133	data->cs4245_shadow[CS4245_DAC_CTRL_2] =134		CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;135	data->cs4245_shadow[CS4245_ADC_CTRL] =136		CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;137	data->cs4245_shadow[CS4245_ANALOG_IN] =138		CS4245_PGA_SOFT | CS4245_PGA_ZERO;139	data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;140	data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;141	data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;142	data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;143 144	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);145	snd_component_add(chip->card, "CS4245");146}147 148void dg_init(struct oxygen *chip)149{150	struct dg *data = chip->model_data;151 152	data->output_sel = PLAYBACK_DST_HP_FP;153	data->input_sel = CAPTURE_SRC_MIC;154 155	cs4245_init(chip);156	oxygen_write16(chip, OXYGEN_GPIO_CONTROL,157		       GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);158	/* anti-pop delay, wait some time before enabling the output */159	msleep(2500);160	oxygen_write16(chip, OXYGEN_GPIO_DATA,161		       GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);162}163 164void dg_cleanup(struct oxygen *chip)165{166	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);167}168 169void dg_suspend(struct oxygen *chip)170{171	dg_cleanup(chip);172}173 174void dg_resume(struct oxygen *chip)175{176	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);177	msleep(2500);178	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);179}180 181void set_cs4245_dac_params(struct oxygen *chip,182				  struct snd_pcm_hw_params *params)183{184	struct dg *data = chip->model_data;185	unsigned char dac_ctrl;186	unsigned char mclk_freq;187 188	dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;189	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;190	if (params_rate(params) <= 50000) {191		dac_ctrl |= CS4245_DAC_FM_SINGLE;192		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;193	} else if (params_rate(params) <= 100000) {194		dac_ctrl |= CS4245_DAC_FM_DOUBLE;195		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;196	} else {197		dac_ctrl |= CS4245_DAC_FM_QUAD;198		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;199	}200	data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;201	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;202	cs4245_write_spi(chip, CS4245_DAC_CTRL_1);203	cs4245_write_spi(chip, CS4245_MCLK_FREQ);204}205 206void set_cs4245_adc_params(struct oxygen *chip,207				  struct snd_pcm_hw_params *params)208{209	struct dg *data = chip->model_data;210	unsigned char adc_ctrl;211	unsigned char mclk_freq;212 213	adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;214	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;215	if (params_rate(params) <= 50000) {216		adc_ctrl |= CS4245_ADC_FM_SINGLE;217		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;218	} else if (params_rate(params) <= 100000) {219		adc_ctrl |= CS4245_ADC_FM_DOUBLE;220		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;221	} else {222		adc_ctrl |= CS4245_ADC_FM_QUAD;223		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;224	}225	data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;226	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;227	cs4245_write_spi(chip, CS4245_ADC_CTRL);228	cs4245_write_spi(chip, CS4245_MCLK_FREQ);229}230 231static inline unsigned int shift_bits(unsigned int value,232				      unsigned int shift_from,233				      unsigned int shift_to,234				      unsigned int mask)235{236	if (shift_from < shift_to)237		return (value << (shift_to - shift_from)) & mask;238	else239		return (value >> (shift_from - shift_to)) & mask;240}241 242unsigned int adjust_dg_dac_routing(struct oxygen *chip,243					  unsigned int play_routing)244{245	struct dg *data = chip->model_data;246 247	switch (data->output_sel) {248	case PLAYBACK_DST_HP:249	case PLAYBACK_DST_HP_FP:250		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,251			OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |252			OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);253		break;254	case PLAYBACK_DST_MULTICH:255		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,256			OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);257		break;258	}259	return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |260	       shift_bits(play_routing,261			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT,262			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT,263			  OXYGEN_PLAY_DAC1_SOURCE_MASK) |264	       shift_bits(play_routing,265			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT,266			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT,267			  OXYGEN_PLAY_DAC2_SOURCE_MASK) |268	       shift_bits(play_routing,269			  OXYGEN_PLAY_DAC0_SOURCE_SHIFT,270			  OXYGEN_PLAY_DAC3_SOURCE_SHIFT,271			  OXYGEN_PLAY_DAC3_SOURCE_MASK);272}273 274void dump_cs4245_registers(struct oxygen *chip,275				  struct snd_info_buffer *buffer)276{277	struct dg *data = chip->model_data;278	unsigned int addr;279 280	snd_iprintf(buffer, "\nCS4245:");281	cs4245_read_spi(chip, CS4245_INT_STATUS);282	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)283		snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);284	snd_iprintf(buffer, "\n");285}286