162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * C-Media CMI8788 driver for C-Media's reference design and similar models 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * CMI8788: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * SPI 0 -> 1st AK4396 (front) 1262306a36Sopenharmony_ci * SPI 1 -> 2nd AK4396 (surround) 1362306a36Sopenharmony_ci * SPI 2 -> 3rd AK4396 (center/LFE) 1462306a36Sopenharmony_ci * SPI 3 -> WM8785 1562306a36Sopenharmony_ci * SPI 4 -> 4th AK4396 (back) 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * GPIO 0 -> DFS0 of AK5385 1862306a36Sopenharmony_ci * GPIO 1 -> DFS1 of AK5385 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * X-Meridian models: 2162306a36Sopenharmony_ci * GPIO 4 -> enable extension S/PDIF input 2262306a36Sopenharmony_ci * GPIO 6 -> enable on-board S/PDIF input 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Claro models: 2562306a36Sopenharmony_ci * GPIO 6 -> S/PDIF from optical (0) or coaxial (1) input 2662306a36Sopenharmony_ci * GPIO 8 -> enable headphone amplifier 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * CM9780: 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * LINE_OUT -> input of ADC 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * AUX_IN <- aux 3362306a36Sopenharmony_ci * CD_IN <- CD 3462306a36Sopenharmony_ci * MIC_IN <- mic 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/delay.h> 4062306a36Sopenharmony_ci#include <linux/mutex.h> 4162306a36Sopenharmony_ci#include <linux/pci.h> 4262306a36Sopenharmony_ci#include <linux/module.h> 4362306a36Sopenharmony_ci#include <sound/ac97_codec.h> 4462306a36Sopenharmony_ci#include <sound/control.h> 4562306a36Sopenharmony_ci#include <sound/core.h> 4662306a36Sopenharmony_ci#include <sound/info.h> 4762306a36Sopenharmony_ci#include <sound/initval.h> 4862306a36Sopenharmony_ci#include <sound/pcm.h> 4962306a36Sopenharmony_ci#include <sound/pcm_params.h> 5062306a36Sopenharmony_ci#include <sound/tlv.h> 5162306a36Sopenharmony_ci#include "oxygen.h" 5262306a36Sopenharmony_ci#include "xonar_dg.h" 5362306a36Sopenharmony_ci#include "ak4396.h" 5462306a36Sopenharmony_ci#include "wm8785.h" 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciMODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 5762306a36Sopenharmony_ciMODULE_DESCRIPTION("C-Media CMI8788 driver"); 5862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 6162306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 6262306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 6562306a36Sopenharmony_ciMODULE_PARM_DESC(index, "card index"); 6662306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 6762306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string"); 6862306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "enable card"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cienum { 7262306a36Sopenharmony_ci MODEL_CMEDIA_REF, 7362306a36Sopenharmony_ci MODEL_MERIDIAN, 7462306a36Sopenharmony_ci MODEL_MERIDIAN_2G, 7562306a36Sopenharmony_ci MODEL_CLARO, 7662306a36Sopenharmony_ci MODEL_CLARO_HALO, 7762306a36Sopenharmony_ci MODEL_FANTASIA, 7862306a36Sopenharmony_ci MODEL_SERENADE, 7962306a36Sopenharmony_ci MODEL_2CH_OUTPUT, 8062306a36Sopenharmony_ci MODEL_HG2PCI, 8162306a36Sopenharmony_ci MODEL_XONAR_DG, 8262306a36Sopenharmony_ci MODEL_XONAR_DGX, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic const struct pci_device_id oxygen_ids[] = { 8662306a36Sopenharmony_ci /* C-Media's reference design */ 8762306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF }, 8862306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x10b0, 0x0217), .driver_data = MODEL_CMEDIA_REF }, 8962306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF }, 9062306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF }, 9162306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF }, 9262306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF }, 9362306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF }, 9462306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF }, 9562306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF }, 9662306a36Sopenharmony_ci /* Asus Xonar DG */ 9762306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG }, 9862306a36Sopenharmony_ci /* Asus Xonar DGX */ 9962306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX }, 10062306a36Sopenharmony_ci /* PCI 2.0 HD Audio */ 10162306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT }, 10262306a36Sopenharmony_ci /* Kuroutoshikou CMI8787-HG2PCI */ 10362306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI }, 10462306a36Sopenharmony_ci /* TempoTec HiFier Fantasia */ 10562306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA }, 10662306a36Sopenharmony_ci /* TempoTec HiFier Serenade */ 10762306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE }, 10862306a36Sopenharmony_ci /* AuzenTech X-Meridian */ 10962306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN }, 11062306a36Sopenharmony_ci /* AuzenTech X-Meridian 2G */ 11162306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G }, 11262306a36Sopenharmony_ci /* HT-Omega Claro */ 11362306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO }, 11462306a36Sopenharmony_ci /* HT-Omega Claro halo */ 11562306a36Sopenharmony_ci { OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO }, 11662306a36Sopenharmony_ci { } 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, oxygen_ids); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define GPIO_AK5385_DFS_MASK 0x0003 12262306a36Sopenharmony_ci#define GPIO_AK5385_DFS_NORMAL 0x0000 12362306a36Sopenharmony_ci#define GPIO_AK5385_DFS_DOUBLE 0x0001 12462306a36Sopenharmony_ci#define GPIO_AK5385_DFS_QUAD 0x0002 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define GPIO_MERIDIAN_DIG_MASK 0x0050 12762306a36Sopenharmony_ci#define GPIO_MERIDIAN_DIG_EXT 0x0010 12862306a36Sopenharmony_ci#define GPIO_MERIDIAN_DIG_BOARD 0x0040 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define GPIO_CLARO_DIG_COAX 0x0040 13162306a36Sopenharmony_ci#define GPIO_CLARO_HP 0x0100 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistruct generic_data { 13462306a36Sopenharmony_ci unsigned int dacs; 13562306a36Sopenharmony_ci u8 ak4396_regs[4][5]; 13662306a36Sopenharmony_ci u16 wm8785_regs[3]; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void ak4396_write(struct oxygen *chip, unsigned int codec, 14062306a36Sopenharmony_ci u8 reg, u8 value) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci /* maps ALSA channel pair number to SPI output */ 14362306a36Sopenharmony_ci static const u8 codec_spi_map[4] = { 14462306a36Sopenharmony_ci 0, 1, 2, 4 14562306a36Sopenharmony_ci }; 14662306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | 14962306a36Sopenharmony_ci OXYGEN_SPI_DATA_LENGTH_2 | 15062306a36Sopenharmony_ci OXYGEN_SPI_CLOCK_160 | 15162306a36Sopenharmony_ci (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | 15262306a36Sopenharmony_ci OXYGEN_SPI_CEN_LATCH_CLOCK_HI, 15362306a36Sopenharmony_ci AK4396_WRITE | (reg << 8) | value); 15462306a36Sopenharmony_ci data->ak4396_regs[codec][reg] = value; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void ak4396_write_cached(struct oxygen *chip, unsigned int codec, 15862306a36Sopenharmony_ci u8 reg, u8 value) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (value != data->ak4396_regs[codec][reg]) 16362306a36Sopenharmony_ci ak4396_write(chip, codec, reg, value); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | 17162306a36Sopenharmony_ci OXYGEN_SPI_DATA_LENGTH_2 | 17262306a36Sopenharmony_ci OXYGEN_SPI_CLOCK_160 | 17362306a36Sopenharmony_ci (3 << OXYGEN_SPI_CODEC_SHIFT) | 17462306a36Sopenharmony_ci OXYGEN_SPI_CEN_LATCH_CLOCK_LO, 17562306a36Sopenharmony_ci (reg << 9) | value); 17662306a36Sopenharmony_ci if (reg < ARRAY_SIZE(data->wm8785_regs)) 17762306a36Sopenharmony_ci data->wm8785_regs[reg] = value; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void ak4396_registers_init(struct oxygen *chip) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 18362306a36Sopenharmony_ci unsigned int i; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < data->dacs; ++i) { 18662306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_CONTROL_1, 18762306a36Sopenharmony_ci AK4396_DIF_24_MSB | AK4396_RSTN); 18862306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_CONTROL_2, 18962306a36Sopenharmony_ci data->ak4396_regs[0][AK4396_CONTROL_2]); 19062306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_CONTROL_3, 19162306a36Sopenharmony_ci AK4396_PCM); 19262306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_LCH_ATT, 19362306a36Sopenharmony_ci chip->dac_volume[i * 2]); 19462306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_RCH_ATT, 19562306a36Sopenharmony_ci chip->dac_volume[i * 2 + 1]); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void ak4396_init(struct oxygen *chip) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci data->dacs = chip->model.dac_channels_pcm / 2; 20462306a36Sopenharmony_ci data->ak4396_regs[0][AK4396_CONTROL_2] = 20562306a36Sopenharmony_ci AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; 20662306a36Sopenharmony_ci ak4396_registers_init(chip); 20762306a36Sopenharmony_ci snd_component_add(chip->card, "AK4396"); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void ak5385_init(struct oxygen *chip) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK); 21362306a36Sopenharmony_ci oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK); 21462306a36Sopenharmony_ci snd_component_add(chip->card, "AK5385"); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void wm8785_registers_init(struct oxygen *chip) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci wm8785_write(chip, WM8785_R7, 0); 22262306a36Sopenharmony_ci wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]); 22362306a36Sopenharmony_ci wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void wm8785_init(struct oxygen *chip) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci data->wm8785_regs[0] = 23162306a36Sopenharmony_ci WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; 23262306a36Sopenharmony_ci data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL; 23362306a36Sopenharmony_ci wm8785_registers_init(chip); 23462306a36Sopenharmony_ci snd_component_add(chip->card, "WM8785"); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void generic_init(struct oxygen *chip) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci ak4396_init(chip); 24062306a36Sopenharmony_ci wm8785_init(chip); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void meridian_init(struct oxygen *chip) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 24662306a36Sopenharmony_ci GPIO_MERIDIAN_DIG_MASK); 24762306a36Sopenharmony_ci oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, 24862306a36Sopenharmony_ci GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK); 24962306a36Sopenharmony_ci ak4396_init(chip); 25062306a36Sopenharmony_ci ak5385_init(chip); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void claro_enable_hp(struct oxygen *chip) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci msleep(300); 25662306a36Sopenharmony_ci oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_HP); 25762306a36Sopenharmony_ci oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void claro_init(struct oxygen *chip) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX); 26362306a36Sopenharmony_ci oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX); 26462306a36Sopenharmony_ci ak4396_init(chip); 26562306a36Sopenharmony_ci wm8785_init(chip); 26662306a36Sopenharmony_ci claro_enable_hp(chip); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void claro_halo_init(struct oxygen *chip) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX); 27262306a36Sopenharmony_ci oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX); 27362306a36Sopenharmony_ci ak4396_init(chip); 27462306a36Sopenharmony_ci ak5385_init(chip); 27562306a36Sopenharmony_ci claro_enable_hp(chip); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void fantasia_init(struct oxygen *chip) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci ak4396_init(chip); 28162306a36Sopenharmony_ci snd_component_add(chip->card, "CS5340"); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void stereo_output_init(struct oxygen *chip) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci ak4396_init(chip); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void generic_cleanup(struct oxygen *chip) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void claro_disable_hp(struct oxygen *chip) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void claro_cleanup(struct oxygen *chip) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci claro_disable_hp(chip); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic void claro_suspend(struct oxygen *chip) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci claro_disable_hp(chip); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void generic_resume(struct oxygen *chip) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci ak4396_registers_init(chip); 31162306a36Sopenharmony_ci wm8785_registers_init(chip); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void meridian_resume(struct oxygen *chip) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci ak4396_registers_init(chip); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void claro_resume(struct oxygen *chip) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci ak4396_registers_init(chip); 32262306a36Sopenharmony_ci claro_enable_hp(chip); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void stereo_resume(struct oxygen *chip) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci ak4396_registers_init(chip); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void set_ak4396_params(struct oxygen *chip, 33162306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 33462306a36Sopenharmony_ci unsigned int i; 33562306a36Sopenharmony_ci u8 value; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK; 33862306a36Sopenharmony_ci if (params_rate(params) <= 54000) 33962306a36Sopenharmony_ci value |= AK4396_DFS_NORMAL; 34062306a36Sopenharmony_ci else if (params_rate(params) <= 108000) 34162306a36Sopenharmony_ci value |= AK4396_DFS_DOUBLE; 34262306a36Sopenharmony_ci else 34362306a36Sopenharmony_ci value |= AK4396_DFS_QUAD; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci msleep(1); /* wait for the new MCLK to become stable */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) { 34862306a36Sopenharmony_ci for (i = 0; i < data->dacs; ++i) { 34962306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_CONTROL_1, 35062306a36Sopenharmony_ci AK4396_DIF_24_MSB); 35162306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_CONTROL_2, value); 35262306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_CONTROL_1, 35362306a36Sopenharmony_ci AK4396_DIF_24_MSB | AK4396_RSTN); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void update_ak4396_volume(struct oxygen *chip) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 36162306a36Sopenharmony_ci unsigned int i; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci for (i = 0; i < data->dacs; ++i) { 36462306a36Sopenharmony_ci ak4396_write_cached(chip, i, AK4396_LCH_ATT, 36562306a36Sopenharmony_ci chip->dac_volume[i * 2]); 36662306a36Sopenharmony_ci ak4396_write_cached(chip, i, AK4396_RCH_ATT, 36762306a36Sopenharmony_ci chip->dac_volume[i * 2 + 1]); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic void update_ak4396_mute(struct oxygen *chip) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 37462306a36Sopenharmony_ci unsigned int i; 37562306a36Sopenharmony_ci u8 value; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE; 37862306a36Sopenharmony_ci if (chip->dac_mute) 37962306a36Sopenharmony_ci value |= AK4396_SMUTE; 38062306a36Sopenharmony_ci for (i = 0; i < data->dacs; ++i) 38162306a36Sopenharmony_ci ak4396_write_cached(chip, i, AK4396_CONTROL_2, value); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void set_wm8785_params(struct oxygen *chip, 38562306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 38862306a36Sopenharmony_ci unsigned int value; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST; 39162306a36Sopenharmony_ci if (params_rate(params) <= 48000) 39262306a36Sopenharmony_ci value |= WM8785_OSR_SINGLE; 39362306a36Sopenharmony_ci else if (params_rate(params) <= 96000) 39462306a36Sopenharmony_ci value |= WM8785_OSR_DOUBLE; 39562306a36Sopenharmony_ci else 39662306a36Sopenharmony_ci value |= WM8785_OSR_QUAD; 39762306a36Sopenharmony_ci if (value != data->wm8785_regs[0]) { 39862306a36Sopenharmony_ci wm8785_write(chip, WM8785_R7, 0); 39962306a36Sopenharmony_ci wm8785_write(chip, WM8785_R0, value); 40062306a36Sopenharmony_ci wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void set_ak5385_params(struct oxygen *chip, 40562306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci unsigned int value; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (params_rate(params) <= 54000) 41062306a36Sopenharmony_ci value = GPIO_AK5385_DFS_NORMAL; 41162306a36Sopenharmony_ci else if (params_rate(params) <= 108000) 41262306a36Sopenharmony_ci value = GPIO_AK5385_DFS_DOUBLE; 41362306a36Sopenharmony_ci else 41462306a36Sopenharmony_ci value = GPIO_AK5385_DFS_QUAD; 41562306a36Sopenharmony_ci oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, 41662306a36Sopenharmony_ci value, GPIO_AK5385_DFS_MASK); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int rolloff_info(struct snd_kcontrol *ctl, 42462306a36Sopenharmony_ci struct snd_ctl_elem_info *info) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci static const char *const names[2] = { 42762306a36Sopenharmony_ci "Sharp Roll-off", "Slow Roll-off" 42862306a36Sopenharmony_ci }; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return snd_ctl_enum_info(info, 1, 2, names); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int rolloff_get(struct snd_kcontrol *ctl, 43462306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 43762306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci value->value.enumerated.item[0] = 44062306a36Sopenharmony_ci (data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0; 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int rolloff_put(struct snd_kcontrol *ctl, 44562306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 44862306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 44962306a36Sopenharmony_ci unsigned int i; 45062306a36Sopenharmony_ci int changed; 45162306a36Sopenharmony_ci u8 reg; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci mutex_lock(&chip->mutex); 45462306a36Sopenharmony_ci reg = data->ak4396_regs[0][AK4396_CONTROL_2]; 45562306a36Sopenharmony_ci if (value->value.enumerated.item[0]) 45662306a36Sopenharmony_ci reg |= AK4396_SLOW; 45762306a36Sopenharmony_ci else 45862306a36Sopenharmony_ci reg &= ~AK4396_SLOW; 45962306a36Sopenharmony_ci changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2]; 46062306a36Sopenharmony_ci if (changed) { 46162306a36Sopenharmony_ci for (i = 0; i < data->dacs; ++i) 46262306a36Sopenharmony_ci ak4396_write(chip, i, AK4396_CONTROL_2, reg); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 46562306a36Sopenharmony_ci return changed; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct snd_kcontrol_new rolloff_control = { 46962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 47062306a36Sopenharmony_ci .name = "DAC Filter Playback Enum", 47162306a36Sopenharmony_ci .info = rolloff_info, 47262306a36Sopenharmony_ci .get = rolloff_get, 47362306a36Sopenharmony_ci .put = rolloff_put, 47462306a36Sopenharmony_ci}; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci static const char *const names[2] = { 47962306a36Sopenharmony_ci "None", "High-pass Filter" 48062306a36Sopenharmony_ci }; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return snd_ctl_enum_info(info, 1, 2, names); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 48862306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci value->value.enumerated.item[0] = 49162306a36Sopenharmony_ci (data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0; 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 49862306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 49962306a36Sopenharmony_ci unsigned int reg; 50062306a36Sopenharmony_ci int changed; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci mutex_lock(&chip->mutex); 50362306a36Sopenharmony_ci reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL); 50462306a36Sopenharmony_ci if (value->value.enumerated.item[0]) 50562306a36Sopenharmony_ci reg |= WM8785_HPFR | WM8785_HPFL; 50662306a36Sopenharmony_ci changed = reg != data->wm8785_regs[WM8785_R2]; 50762306a36Sopenharmony_ci if (changed) 50862306a36Sopenharmony_ci wm8785_write(chip, WM8785_R2, reg); 50962306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 51062306a36Sopenharmony_ci return changed; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic const struct snd_kcontrol_new hpf_control = { 51462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 51562306a36Sopenharmony_ci .name = "ADC Filter Capture Enum", 51662306a36Sopenharmony_ci .info = hpf_info, 51762306a36Sopenharmony_ci .get = hpf_get, 51862306a36Sopenharmony_ci .put = hpf_put, 51962306a36Sopenharmony_ci}; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int meridian_dig_source_info(struct snd_kcontrol *ctl, 52262306a36Sopenharmony_ci struct snd_ctl_elem_info *info) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci static const char *const names[2] = { "On-board", "Extension" }; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return snd_ctl_enum_info(info, 1, 2, names); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int claro_dig_source_info(struct snd_kcontrol *ctl, 53062306a36Sopenharmony_ci struct snd_ctl_elem_info *info) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci static const char *const names[2] = { "Optical", "Coaxial" }; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return snd_ctl_enum_info(info, 1, 2, names); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int meridian_dig_source_get(struct snd_kcontrol *ctl, 53862306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci value->value.enumerated.item[0] = 54362306a36Sopenharmony_ci !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & 54462306a36Sopenharmony_ci GPIO_MERIDIAN_DIG_EXT); 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int claro_dig_source_get(struct snd_kcontrol *ctl, 54962306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci value->value.enumerated.item[0] = 55462306a36Sopenharmony_ci !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & 55562306a36Sopenharmony_ci GPIO_CLARO_DIG_COAX); 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int meridian_dig_source_put(struct snd_kcontrol *ctl, 56062306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 56362306a36Sopenharmony_ci u16 old_reg, new_reg; 56462306a36Sopenharmony_ci int changed; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci mutex_lock(&chip->mutex); 56762306a36Sopenharmony_ci old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); 56862306a36Sopenharmony_ci new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK; 56962306a36Sopenharmony_ci if (value->value.enumerated.item[0] == 0) 57062306a36Sopenharmony_ci new_reg |= GPIO_MERIDIAN_DIG_BOARD; 57162306a36Sopenharmony_ci else 57262306a36Sopenharmony_ci new_reg |= GPIO_MERIDIAN_DIG_EXT; 57362306a36Sopenharmony_ci changed = new_reg != old_reg; 57462306a36Sopenharmony_ci if (changed) 57562306a36Sopenharmony_ci oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); 57662306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 57762306a36Sopenharmony_ci return changed; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int claro_dig_source_put(struct snd_kcontrol *ctl, 58162306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct oxygen *chip = ctl->private_data; 58462306a36Sopenharmony_ci u16 old_reg, new_reg; 58562306a36Sopenharmony_ci int changed; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci mutex_lock(&chip->mutex); 58862306a36Sopenharmony_ci old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); 58962306a36Sopenharmony_ci new_reg = old_reg & ~GPIO_CLARO_DIG_COAX; 59062306a36Sopenharmony_ci if (value->value.enumerated.item[0]) 59162306a36Sopenharmony_ci new_reg |= GPIO_CLARO_DIG_COAX; 59262306a36Sopenharmony_ci changed = new_reg != old_reg; 59362306a36Sopenharmony_ci if (changed) 59462306a36Sopenharmony_ci oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); 59562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 59662306a36Sopenharmony_ci return changed; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic const struct snd_kcontrol_new meridian_dig_source_control = { 60062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 60162306a36Sopenharmony_ci .name = "IEC958 Source Capture Enum", 60262306a36Sopenharmony_ci .info = meridian_dig_source_info, 60362306a36Sopenharmony_ci .get = meridian_dig_source_get, 60462306a36Sopenharmony_ci .put = meridian_dig_source_put, 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic const struct snd_kcontrol_new claro_dig_source_control = { 60862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 60962306a36Sopenharmony_ci .name = "IEC958 Source Capture Enum", 61062306a36Sopenharmony_ci .info = claro_dig_source_info, 61162306a36Sopenharmony_ci .get = claro_dig_source_get, 61262306a36Sopenharmony_ci .put = claro_dig_source_put, 61362306a36Sopenharmony_ci}; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int generic_mixer_init(struct oxygen *chip) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int generic_wm8785_mixer_init(struct oxygen *chip) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci int err; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci err = generic_mixer_init(chip); 62562306a36Sopenharmony_ci if (err < 0) 62662306a36Sopenharmony_ci return err; 62762306a36Sopenharmony_ci err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip)); 62862306a36Sopenharmony_ci if (err < 0) 62962306a36Sopenharmony_ci return err; 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int meridian_mixer_init(struct oxygen *chip) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci int err; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci err = generic_mixer_init(chip); 63862306a36Sopenharmony_ci if (err < 0) 63962306a36Sopenharmony_ci return err; 64062306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 64162306a36Sopenharmony_ci snd_ctl_new1(&meridian_dig_source_control, chip)); 64262306a36Sopenharmony_ci if (err < 0) 64362306a36Sopenharmony_ci return err; 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int claro_mixer_init(struct oxygen *chip) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci int err; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci err = generic_wm8785_mixer_init(chip); 65262306a36Sopenharmony_ci if (err < 0) 65362306a36Sopenharmony_ci return err; 65462306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 65562306a36Sopenharmony_ci snd_ctl_new1(&claro_dig_source_control, chip)); 65662306a36Sopenharmony_ci if (err < 0) 65762306a36Sopenharmony_ci return err; 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int claro_halo_mixer_init(struct oxygen *chip) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci int err; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci err = generic_mixer_init(chip); 66662306a36Sopenharmony_ci if (err < 0) 66762306a36Sopenharmony_ci return err; 66862306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 66962306a36Sopenharmony_ci snd_ctl_new1(&claro_dig_source_control, chip)); 67062306a36Sopenharmony_ci if (err < 0) 67162306a36Sopenharmony_ci return err; 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic void dump_ak4396_registers(struct oxygen *chip, 67662306a36Sopenharmony_ci struct snd_info_buffer *buffer) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 67962306a36Sopenharmony_ci unsigned int dac, i; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci for (dac = 0; dac < data->dacs; ++dac) { 68262306a36Sopenharmony_ci snd_iprintf(buffer, "\nAK4396 %u:", dac + 1); 68362306a36Sopenharmony_ci for (i = 0; i < 5; ++i) 68462306a36Sopenharmony_ci snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci snd_iprintf(buffer, "\n"); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic void dump_wm8785_registers(struct oxygen *chip, 69062306a36Sopenharmony_ci struct snd_info_buffer *buffer) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct generic_data *data = chip->model_data; 69362306a36Sopenharmony_ci unsigned int i; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci snd_iprintf(buffer, "\nWM8785:"); 69662306a36Sopenharmony_ci for (i = 0; i < 3; ++i) 69762306a36Sopenharmony_ci snd_iprintf(buffer, " %03x", data->wm8785_regs[i]); 69862306a36Sopenharmony_ci snd_iprintf(buffer, "\n"); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic void dump_oxygen_registers(struct oxygen *chip, 70262306a36Sopenharmony_ci struct snd_info_buffer *buffer) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci dump_ak4396_registers(chip, buffer); 70562306a36Sopenharmony_ci dump_wm8785_registers(chip, buffer); 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic const struct oxygen_model model_generic = { 71162306a36Sopenharmony_ci .shortname = "C-Media CMI8788", 71262306a36Sopenharmony_ci .longname = "C-Media Oxygen HD Audio", 71362306a36Sopenharmony_ci .chip = "CMI8788", 71462306a36Sopenharmony_ci .init = generic_init, 71562306a36Sopenharmony_ci .mixer_init = generic_wm8785_mixer_init, 71662306a36Sopenharmony_ci .cleanup = generic_cleanup, 71762306a36Sopenharmony_ci .resume = generic_resume, 71862306a36Sopenharmony_ci .set_dac_params = set_ak4396_params, 71962306a36Sopenharmony_ci .set_adc_params = set_wm8785_params, 72062306a36Sopenharmony_ci .update_dac_volume = update_ak4396_volume, 72162306a36Sopenharmony_ci .update_dac_mute = update_ak4396_mute, 72262306a36Sopenharmony_ci .dump_registers = dump_oxygen_registers, 72362306a36Sopenharmony_ci .dac_tlv = ak4396_db_scale, 72462306a36Sopenharmony_ci .model_data_size = sizeof(struct generic_data), 72562306a36Sopenharmony_ci .device_config = PLAYBACK_0_TO_I2S | 72662306a36Sopenharmony_ci PLAYBACK_1_TO_SPDIF | 72762306a36Sopenharmony_ci PLAYBACK_2_TO_AC97_1 | 72862306a36Sopenharmony_ci CAPTURE_0_FROM_I2S_1 | 72962306a36Sopenharmony_ci CAPTURE_1_FROM_SPDIF | 73062306a36Sopenharmony_ci CAPTURE_2_FROM_AC97_1 | 73162306a36Sopenharmony_ci AC97_CD_INPUT, 73262306a36Sopenharmony_ci .dac_channels_pcm = 8, 73362306a36Sopenharmony_ci .dac_channels_mixer = 8, 73462306a36Sopenharmony_ci .dac_volume_min = 0, 73562306a36Sopenharmony_ci .dac_volume_max = 255, 73662306a36Sopenharmony_ci .function_flags = OXYGEN_FUNCTION_SPI | 73762306a36Sopenharmony_ci OXYGEN_FUNCTION_ENABLE_SPI_4_5, 73862306a36Sopenharmony_ci .dac_mclks = OXYGEN_MCLKS(256, 128, 128), 73962306a36Sopenharmony_ci .adc_mclks = OXYGEN_MCLKS(256, 256, 128), 74062306a36Sopenharmony_ci .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 74162306a36Sopenharmony_ci .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 74262306a36Sopenharmony_ci}; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int get_oxygen_model(struct oxygen *chip, 74562306a36Sopenharmony_ci const struct pci_device_id *id) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci static const char *const names[] = { 74862306a36Sopenharmony_ci [MODEL_MERIDIAN] = "AuzenTech X-Meridian", 74962306a36Sopenharmony_ci [MODEL_MERIDIAN_2G] = "AuzenTech X-Meridian 2G", 75062306a36Sopenharmony_ci [MODEL_CLARO] = "HT-Omega Claro", 75162306a36Sopenharmony_ci [MODEL_CLARO_HALO] = "HT-Omega Claro halo", 75262306a36Sopenharmony_ci [MODEL_FANTASIA] = "TempoTec HiFier Fantasia", 75362306a36Sopenharmony_ci [MODEL_SERENADE] = "TempoTec HiFier Serenade", 75462306a36Sopenharmony_ci [MODEL_HG2PCI] = "CMI8787-HG2PCI", 75562306a36Sopenharmony_ci [MODEL_XONAR_DG] = "Xonar DG", 75662306a36Sopenharmony_ci [MODEL_XONAR_DGX] = "Xonar DGX", 75762306a36Sopenharmony_ci }; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci chip->model = model_generic; 76062306a36Sopenharmony_ci switch (id->driver_data) { 76162306a36Sopenharmony_ci case MODEL_MERIDIAN: 76262306a36Sopenharmony_ci case MODEL_MERIDIAN_2G: 76362306a36Sopenharmony_ci chip->model.init = meridian_init; 76462306a36Sopenharmony_ci chip->model.mixer_init = meridian_mixer_init; 76562306a36Sopenharmony_ci chip->model.resume = meridian_resume; 76662306a36Sopenharmony_ci chip->model.set_adc_params = set_ak5385_params; 76762306a36Sopenharmony_ci chip->model.dump_registers = dump_ak4396_registers; 76862306a36Sopenharmony_ci chip->model.device_config = PLAYBACK_0_TO_I2S | 76962306a36Sopenharmony_ci PLAYBACK_1_TO_SPDIF | 77062306a36Sopenharmony_ci CAPTURE_0_FROM_I2S_2 | 77162306a36Sopenharmony_ci CAPTURE_1_FROM_SPDIF; 77262306a36Sopenharmony_ci if (id->driver_data == MODEL_MERIDIAN) 77362306a36Sopenharmony_ci chip->model.device_config |= AC97_CD_INPUT; 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci case MODEL_CLARO: 77662306a36Sopenharmony_ci chip->model.init = claro_init; 77762306a36Sopenharmony_ci chip->model.mixer_init = claro_mixer_init; 77862306a36Sopenharmony_ci chip->model.cleanup = claro_cleanup; 77962306a36Sopenharmony_ci chip->model.suspend = claro_suspend; 78062306a36Sopenharmony_ci chip->model.resume = claro_resume; 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci case MODEL_CLARO_HALO: 78362306a36Sopenharmony_ci chip->model.init = claro_halo_init; 78462306a36Sopenharmony_ci chip->model.mixer_init = claro_halo_mixer_init; 78562306a36Sopenharmony_ci chip->model.cleanup = claro_cleanup; 78662306a36Sopenharmony_ci chip->model.suspend = claro_suspend; 78762306a36Sopenharmony_ci chip->model.resume = claro_resume; 78862306a36Sopenharmony_ci chip->model.set_adc_params = set_ak5385_params; 78962306a36Sopenharmony_ci chip->model.dump_registers = dump_ak4396_registers; 79062306a36Sopenharmony_ci chip->model.device_config = PLAYBACK_0_TO_I2S | 79162306a36Sopenharmony_ci PLAYBACK_1_TO_SPDIF | 79262306a36Sopenharmony_ci CAPTURE_0_FROM_I2S_2 | 79362306a36Sopenharmony_ci CAPTURE_1_FROM_SPDIF; 79462306a36Sopenharmony_ci break; 79562306a36Sopenharmony_ci case MODEL_FANTASIA: 79662306a36Sopenharmony_ci case MODEL_SERENADE: 79762306a36Sopenharmony_ci case MODEL_2CH_OUTPUT: 79862306a36Sopenharmony_ci case MODEL_HG2PCI: 79962306a36Sopenharmony_ci chip->model.shortname = "C-Media CMI8787"; 80062306a36Sopenharmony_ci chip->model.chip = "CMI8787"; 80162306a36Sopenharmony_ci if (id->driver_data == MODEL_FANTASIA) 80262306a36Sopenharmony_ci chip->model.init = fantasia_init; 80362306a36Sopenharmony_ci else 80462306a36Sopenharmony_ci chip->model.init = stereo_output_init; 80562306a36Sopenharmony_ci chip->model.resume = stereo_resume; 80662306a36Sopenharmony_ci chip->model.mixer_init = generic_mixer_init; 80762306a36Sopenharmony_ci chip->model.set_adc_params = set_no_params; 80862306a36Sopenharmony_ci chip->model.dump_registers = dump_ak4396_registers; 80962306a36Sopenharmony_ci chip->model.device_config = PLAYBACK_0_TO_I2S | 81062306a36Sopenharmony_ci PLAYBACK_1_TO_SPDIF; 81162306a36Sopenharmony_ci if (id->driver_data == MODEL_FANTASIA) { 81262306a36Sopenharmony_ci chip->model.device_config |= CAPTURE_0_FROM_I2S_1; 81362306a36Sopenharmony_ci chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128); 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci chip->model.dac_channels_pcm = 2; 81662306a36Sopenharmony_ci chip->model.dac_channels_mixer = 2; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case MODEL_XONAR_DG: 81962306a36Sopenharmony_ci case MODEL_XONAR_DGX: 82062306a36Sopenharmony_ci chip->model = model_xonar_dg; 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci if (id->driver_data == MODEL_MERIDIAN || 82462306a36Sopenharmony_ci id->driver_data == MODEL_MERIDIAN_2G || 82562306a36Sopenharmony_ci id->driver_data == MODEL_CLARO_HALO) { 82662306a36Sopenharmony_ci chip->model.misc_flags = OXYGEN_MISC_MIDI; 82762306a36Sopenharmony_ci chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data]) 83062306a36Sopenharmony_ci chip->model.shortname = names[id->driver_data]; 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic int generic_oxygen_probe(struct pci_dev *pci, 83562306a36Sopenharmony_ci const struct pci_device_id *pci_id) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci static int dev; 83862306a36Sopenharmony_ci int err; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 84162306a36Sopenharmony_ci return -ENODEV; 84262306a36Sopenharmony_ci if (!enable[dev]) { 84362306a36Sopenharmony_ci ++dev; 84462306a36Sopenharmony_ci return -ENOENT; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE, 84762306a36Sopenharmony_ci oxygen_ids, get_oxygen_model); 84862306a36Sopenharmony_ci if (err >= 0) 84962306a36Sopenharmony_ci ++dev; 85062306a36Sopenharmony_ci return err; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic struct pci_driver oxygen_driver = { 85462306a36Sopenharmony_ci .name = KBUILD_MODNAME, 85562306a36Sopenharmony_ci .id_table = oxygen_ids, 85662306a36Sopenharmony_ci .probe = generic_oxygen_probe, 85762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 85862306a36Sopenharmony_ci .driver = { 85962306a36Sopenharmony_ci .pm = &oxygen_pci_pm, 86062306a36Sopenharmony_ci }, 86162306a36Sopenharmony_ci#endif 86262306a36Sopenharmony_ci}; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cimodule_pci_driver(oxygen_driver); 865