cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

xonar_dg.c (8088B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * card driver for the Xonar DG/DGX
      4 *
      5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      6 * Copyright (c) Roman Volkov <v1ron@mail.ru>
      7 */
      8
      9/*
     10 * Xonar DG/DGX
     11 * ------------
     12 *
     13 * CS4245 and CS4361 both will mute all outputs if any clock ratio
     14 * is invalid.
     15 *
     16 * CMI8788:
     17 *
     18 *   SPI 0 -> CS4245
     19 *
     20 *   Playback:
     21 *   I²S 1 -> CS4245
     22 *   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 <- CS4245
     27 *
     28 *   GPIO 3 <- ?
     29 *   GPIO 4 <- headphone detect
     30 *   GPIO 5 -> enable ADC analog circuit for the left channel
     31 *   GPIO 6 -> enable ADC analog circuit for the right channel
     32 *   GPIO 7 -> switch green rear output jack between CS4245 and the first
     33 *             channel of CS4361 (mechanical relay)
     34 *   GPIO 8 -> enable output to speakers
     35 *
     36 * CS4245:
     37 *
     38 *   input 0 <- mic
     39 *   input 1 <- aux
     40 *   input 2 <- front mic
     41 *   input 4 <- line
     42 *   DAC out -> headphones
     43 *   aux out -> front panel headphones
     44 */
     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 in
    125	 * async. mode, enable aux output from DAC. Invert DAC output as in the
    126	 * 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	else
    239		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}