root/sound/pci/oxygen/xonar_dg.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cs4245_write_spi
  2. cs4245_read_spi
  3. cs4245_shadow_control
  4. cs4245_init
  5. dg_init
  6. dg_cleanup
  7. dg_suspend
  8. dg_resume
  9. set_cs4245_dac_params
  10. set_cs4245_adc_params
  11. shift_bits
  12. adjust_dg_dac_routing
  13. dump_cs4245_registers

   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 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 
  57 int 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 
  74 int 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 
 100 int 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 
 116 static 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 
 148 void 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 
 164 void dg_cleanup(struct oxygen *chip)
 165 {
 166         oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
 167 }
 168 
 169 void dg_suspend(struct oxygen *chip)
 170 {
 171         dg_cleanup(chip);
 172 }
 173 
 174 void 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 
 181 void 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 
 206 void 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 
 231 static 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 
 242 unsigned 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 
 274 void 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 }

/* [<][>][^][v][top][bottom][index][help] */