162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ALSA driver for ICEnsemble VT1724 (Envy24HT) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Lowlevel functions for Philips PSC724 Ultimate Edge 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <sound/core.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "ice1712.h" 1662306a36Sopenharmony_ci#include "envy24ht.h" 1762306a36Sopenharmony_ci#include "psc724.h" 1862306a36Sopenharmony_ci#include "wm8766.h" 1962306a36Sopenharmony_ci#include "wm8776.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct psc724_spec { 2262306a36Sopenharmony_ci struct snd_wm8766 wm8766; 2362306a36Sopenharmony_ci struct snd_wm8776 wm8776; 2462306a36Sopenharmony_ci bool mute_all, jack_detect; 2562306a36Sopenharmony_ci struct snd_ice1712 *ice; 2662306a36Sopenharmony_ci struct delayed_work hp_work; 2762306a36Sopenharmony_ci bool hp_connected; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/****************************************************************************/ 3162306a36Sopenharmony_ci/* PHILIPS PSC724 ULTIMATE EDGE */ 3262306a36Sopenharmony_ci/****************************************************************************/ 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * VT1722 (Envy24GT) - 6 outputs, 4 inputs (only 2 used), 24-bit/96kHz 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * system configuration ICE_EEP2_SYSCONF=0x42 3762306a36Sopenharmony_ci * XIN1 49.152MHz 3862306a36Sopenharmony_ci * no MPU401 3962306a36Sopenharmony_ci * one stereo ADC, no S/PDIF receiver 4062306a36Sopenharmony_ci * three stereo DACs (FRONT, REAR, CENTER+LFE) 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * AC-Link configuration ICE_EEP2_ACLINK=0x80 4362306a36Sopenharmony_ci * use I2S, not AC97 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * I2S converters feature ICE_EEP2_I2S=0x30 4662306a36Sopenharmony_ci * I2S codec has no volume/mute control feature (bug!) 4762306a36Sopenharmony_ci * I2S codec does not support 96KHz or 192KHz (bug!) 4862306a36Sopenharmony_ci * I2S codec 24bits 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * S/PDIF configuration ICE_EEP2_SPDIF=0xc1 5162306a36Sopenharmony_ci * Enable integrated S/PDIF transmitter 5262306a36Sopenharmony_ci * internal S/PDIF out implemented 5362306a36Sopenharmony_ci * No S/PDIF input 5462306a36Sopenharmony_ci * External S/PDIF out implemented 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * ** connected chips ** 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * WM8776 6062306a36Sopenharmony_ci * 2-channel DAC used for main output and stereo ADC (with 10-channel MUX) 6162306a36Sopenharmony_ci * AIN1: LINE IN, AIN2: CD/VIDEO, AIN3: AUX, AIN4: Front MIC, AIN5: Rear MIC 6262306a36Sopenharmony_ci * Controlled by I2C using VT1722 I2C interface: 6362306a36Sopenharmony_ci * MODE (pin16) -- GND 6462306a36Sopenharmony_ci * CE (pin17) -- GND I2C mode (address=0x34) 6562306a36Sopenharmony_ci * DI (pin18) -- SDA (VT1722 pin70) 6662306a36Sopenharmony_ci * CL (pin19) -- SCLK (VT1722 pin71) 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * WM8766 6962306a36Sopenharmony_ci * 6-channel DAC used for rear & center/LFE outputs (only 4 channels used) 7062306a36Sopenharmony_ci * Controlled by SPI using VT1722 GPIO pins: 7162306a36Sopenharmony_ci * MODE (pin 1) -- GPIO19 (VT1722 pin99) 7262306a36Sopenharmony_ci * ML/I2S (pin11) -- GPIO18 (VT1722 pin98) 7362306a36Sopenharmony_ci * MC/IWL (pin12) -- GPIO17 (VT1722 pin97) 7462306a36Sopenharmony_ci * MD/DM (pin13) -- GPIO16 (VT1722 pin96) 7562306a36Sopenharmony_ci * MUTE (pin14) -- GPIO20 (VT1722 pin101) 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * GPIO14 is used as input for headphone jack detection (1 = connected) 7862306a36Sopenharmony_ci * GPIO22 is used as MUTE ALL output, grounding all 6 channels 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * ** output pins and device names ** 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * 5.1ch name -- output connector color -- device (-D option) 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * FRONT 2ch -- green -- plughw:0,0 8562306a36Sopenharmony_ci * CENTER(Lch) SUBWOOFER(Rch) -- orange -- plughw:0,2,0 8662306a36Sopenharmony_ci * REAR 2ch -- black -- plughw:0,2,1 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* codec access low-level functions */ 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define GPIO_HP_JACK (1 << 14) 9262306a36Sopenharmony_ci#define GPIO_MUTE_SUR (1 << 20) 9362306a36Sopenharmony_ci#define GPIO_MUTE_ALL (1 << 22) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define JACK_INTERVAL 1000 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define PSC724_SPI_DELAY 1 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define PSC724_SPI_DATA (1 << 16) 10062306a36Sopenharmony_ci#define PSC724_SPI_CLK (1 << 17) 10162306a36Sopenharmony_ci#define PSC724_SPI_LOAD (1 << 18) 10262306a36Sopenharmony_ci#define PSC724_SPI_MASK (PSC724_SPI_DATA | PSC724_SPI_CLK | PSC724_SPI_LOAD) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void psc724_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8766); 10762306a36Sopenharmony_ci struct snd_ice1712 *ice = spec->ice; 10862306a36Sopenharmony_ci u32 st, bits; 10962306a36Sopenharmony_ci int i; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci snd_ice1712_save_gpio_status(ice); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci st = ((addr & 0x7f) << 9) | (data & 0x1ff); 11462306a36Sopenharmony_ci snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | PSC724_SPI_MASK); 11562306a36Sopenharmony_ci snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~PSC724_SPI_MASK); 11662306a36Sopenharmony_ci bits = snd_ice1712_gpio_read(ice) & ~PSC724_SPI_MASK; 11762306a36Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 12062306a36Sopenharmony_ci udelay(PSC724_SPI_DELAY); 12162306a36Sopenharmony_ci bits &= ~PSC724_SPI_CLK; 12262306a36Sopenharmony_ci /* MSB first */ 12362306a36Sopenharmony_ci st <<= 1; 12462306a36Sopenharmony_ci if (st & 0x10000) 12562306a36Sopenharmony_ci bits |= PSC724_SPI_DATA; 12662306a36Sopenharmony_ci else 12762306a36Sopenharmony_ci bits &= ~PSC724_SPI_DATA; 12862306a36Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 12962306a36Sopenharmony_ci /* CLOCK high */ 13062306a36Sopenharmony_ci udelay(PSC724_SPI_DELAY); 13162306a36Sopenharmony_ci bits |= PSC724_SPI_CLK; 13262306a36Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci /* LOAD high */ 13562306a36Sopenharmony_ci udelay(PSC724_SPI_DELAY); 13662306a36Sopenharmony_ci bits |= PSC724_SPI_LOAD; 13762306a36Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 13862306a36Sopenharmony_ci /* LOAD low, DATA and CLOCK high */ 13962306a36Sopenharmony_ci udelay(PSC724_SPI_DELAY); 14062306a36Sopenharmony_ci bits |= (PSC724_SPI_DATA | PSC724_SPI_CLK); 14162306a36Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci snd_ice1712_restore_gpio_status(ice); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void psc724_wm8776_write(struct snd_wm8776 *wm, u8 addr, u8 data) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8776); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci snd_vt1724_write_i2c(spec->ice, 0x34, addr, data); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* mute all */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void psc724_set_master_switch(struct snd_ice1712 *ice, bool on) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned int bits = snd_ice1712_gpio_read(ice); 15862306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci spec->mute_all = !on; 16162306a36Sopenharmony_ci if (on) 16262306a36Sopenharmony_ci bits &= ~(GPIO_MUTE_ALL | GPIO_MUTE_SUR); 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci bits |= GPIO_MUTE_ALL | GPIO_MUTE_SUR; 16562306a36Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic bool psc724_get_master_switch(struct snd_ice1712 *ice) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return !spec->mute_all; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* jack detection */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 18062306a36Sopenharmony_ci struct snd_kcontrol *kctl; 18162306a36Sopenharmony_ci u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci psc724_set_master_switch(ice, !hp_connected); 18462306a36Sopenharmony_ci if (!hp_connected) 18562306a36Sopenharmony_ci power |= WM8776_PWR_HPPD; 18662306a36Sopenharmony_ci snd_wm8776_set_power(&spec->wm8776, power); 18762306a36Sopenharmony_ci spec->hp_connected = hp_connected; 18862306a36Sopenharmony_ci /* notify about master speaker mute change */ 18962306a36Sopenharmony_ci kctl = snd_ctl_find_id_mixer(ice->card, 19062306a36Sopenharmony_ci "Master Speakers Playback Switch"); 19162306a36Sopenharmony_ci if (kctl) 19262306a36Sopenharmony_ci snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 19362306a36Sopenharmony_ci /* and headphone mute change */ 19462306a36Sopenharmony_ci kctl = snd_ctl_find_id_mixer(ice->card, 19562306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_HP_SW].name); 19662306a36Sopenharmony_ci if (kctl) 19762306a36Sopenharmony_ci snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void psc724_update_hp_jack_state(struct work_struct *work) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct psc724_spec *spec = container_of(work, struct psc724_spec, 20362306a36Sopenharmony_ci hp_work.work); 20462306a36Sopenharmony_ci struct snd_ice1712 *ice = spec->ice; 20562306a36Sopenharmony_ci bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci schedule_delayed_work(&spec->hp_work, msecs_to_jiffies(JACK_INTERVAL)); 20862306a36Sopenharmony_ci if (hp_connected == spec->hp_connected) 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci psc724_set_jack_state(ice, hp_connected); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void psc724_set_jack_detection(struct snd_ice1712 *ice, bool on) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (spec->jack_detect == on) 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci spec->jack_detect = on; 22162306a36Sopenharmony_ci if (on) { 22262306a36Sopenharmony_ci bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK; 22362306a36Sopenharmony_ci psc724_set_jack_state(ice, hp_connected); 22462306a36Sopenharmony_ci schedule_delayed_work(&spec->hp_work, 22562306a36Sopenharmony_ci msecs_to_jiffies(JACK_INTERVAL)); 22662306a36Sopenharmony_ci } else 22762306a36Sopenharmony_ci cancel_delayed_work_sync(&spec->hp_work); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic bool psc724_get_jack_detection(struct snd_ice1712 *ice) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return spec->jack_detect; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* mixer controls */ 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistruct psc724_control { 24062306a36Sopenharmony_ci const char *name; 24162306a36Sopenharmony_ci void (*set)(struct snd_ice1712 *ice, bool on); 24262306a36Sopenharmony_ci bool (*get)(struct snd_ice1712 *ice); 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic const struct psc724_control psc724_cont[] = { 24662306a36Sopenharmony_ci { 24762306a36Sopenharmony_ci .name = "Master Speakers Playback Switch", 24862306a36Sopenharmony_ci .set = psc724_set_master_switch, 24962306a36Sopenharmony_ci .get = psc724_get_master_switch, 25062306a36Sopenharmony_ci }, 25162306a36Sopenharmony_ci { 25262306a36Sopenharmony_ci .name = "Headphone Jack Detection Playback Switch", 25362306a36Sopenharmony_ci .set = psc724_set_jack_detection, 25462306a36Sopenharmony_ci .get = psc724_get_jack_detection, 25562306a36Sopenharmony_ci }, 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int psc724_ctl_get(struct snd_kcontrol *kcontrol, 25962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 26262306a36Sopenharmony_ci int n = kcontrol->private_value; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = psc724_cont[n].get(ice); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int psc724_ctl_put(struct snd_kcontrol *kcontrol, 27062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 27362306a36Sopenharmony_ci int n = kcontrol->private_value; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci psc724_cont[n].set(ice, ucontrol->value.integer.value[0]); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic const char *front_volume = "Front Playback Volume"; 28162306a36Sopenharmony_cistatic const char *front_switch = "Front Playback Switch"; 28262306a36Sopenharmony_cistatic const char *front_zc = "Front Zero Cross Detect Playback Switch"; 28362306a36Sopenharmony_cistatic const char *front_izd = "Front Infinite Zero Detect Playback Switch"; 28462306a36Sopenharmony_cistatic const char *front_phase = "Front Phase Invert Playback Switch"; 28562306a36Sopenharmony_cistatic const char *front_deemph = "Front Deemphasis Playback Switch"; 28662306a36Sopenharmony_cistatic const char *ain1_switch = "Line Capture Switch"; 28762306a36Sopenharmony_cistatic const char *ain2_switch = "CD Capture Switch"; 28862306a36Sopenharmony_cistatic const char *ain3_switch = "AUX Capture Switch"; 28962306a36Sopenharmony_cistatic const char *ain4_switch = "Front Mic Capture Switch"; 29062306a36Sopenharmony_cistatic const char *ain5_switch = "Rear Mic Capture Switch"; 29162306a36Sopenharmony_cistatic const char *rear_volume = "Surround Playback Volume"; 29262306a36Sopenharmony_cistatic const char *clfe_volume = "CLFE Playback Volume"; 29362306a36Sopenharmony_cistatic const char *rear_switch = "Surround Playback Switch"; 29462306a36Sopenharmony_cistatic const char *clfe_switch = "CLFE Playback Switch"; 29562306a36Sopenharmony_cistatic const char *rear_phase = "Surround Phase Invert Playback Switch"; 29662306a36Sopenharmony_cistatic const char *clfe_phase = "CLFE Phase Invert Playback Switch"; 29762306a36Sopenharmony_cistatic const char *rear_deemph = "Surround Deemphasis Playback Switch"; 29862306a36Sopenharmony_cistatic const char *clfe_deemph = "CLFE Deemphasis Playback Switch"; 29962306a36Sopenharmony_cistatic const char *rear_clfe_izd = "Rear Infinite Zero Detect Playback Switch"; 30062306a36Sopenharmony_cistatic const char *rear_clfe_zc = "Rear Zero Cross Detect Playback Switch"; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int psc724_add_controls(struct snd_ice1712 *ice) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct snd_kcontrol_new cont; 30562306a36Sopenharmony_ci struct snd_kcontrol *ctl; 30662306a36Sopenharmony_ci int err, i; 30762306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_VOL].name = front_volume; 31062306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_SW].name = front_switch; 31162306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_ZC_SW].name = front_zc; 31262306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_AUX_SW].name = NULL; 31362306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_IZD_SW].name = front_izd; 31462306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_PHASE_SW].name = front_phase; 31562306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DEEMPH_SW].name = front_deemph; 31662306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT1_SW].name = ain1_switch; 31762306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT2_SW].name = ain2_switch; 31862306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT3_SW].name = ain3_switch; 31962306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT4_SW].name = ain4_switch; 32062306a36Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT5_SW].name = ain5_switch; 32162306a36Sopenharmony_ci snd_wm8776_build_controls(&spec->wm8776); 32262306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH1_VOL].name = rear_volume; 32362306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH2_VOL].name = clfe_volume; 32462306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH3_VOL].name = NULL; 32562306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH1_SW].name = rear_switch; 32662306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH2_SW].name = clfe_switch; 32762306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH3_SW].name = NULL; 32862306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_PHASE1_SW].name = rear_phase; 32962306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_PHASE2_SW].name = clfe_phase; 33062306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_PHASE3_SW].name = NULL; 33162306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_DEEMPH1_SW].name = rear_deemph; 33262306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_DEEMPH2_SW].name = clfe_deemph; 33362306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_DEEMPH3_SW].name = NULL; 33462306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_IZD_SW].name = rear_clfe_izd; 33562306a36Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_ZC_SW].name = rear_clfe_zc; 33662306a36Sopenharmony_ci snd_wm8766_build_controls(&spec->wm8766); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci memset(&cont, 0, sizeof(cont)); 33962306a36Sopenharmony_ci cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 34062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(psc724_cont); i++) { 34162306a36Sopenharmony_ci cont.private_value = i; 34262306a36Sopenharmony_ci cont.name = psc724_cont[i].name; 34362306a36Sopenharmony_ci cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 34462306a36Sopenharmony_ci cont.info = snd_ctl_boolean_mono_info; 34562306a36Sopenharmony_ci cont.get = psc724_ctl_get; 34662306a36Sopenharmony_ci cont.put = psc724_ctl_put; 34762306a36Sopenharmony_ci ctl = snd_ctl_new1(&cont, ice); 34862306a36Sopenharmony_ci if (!ctl) 34962306a36Sopenharmony_ci return -ENOMEM; 35062306a36Sopenharmony_ci err = snd_ctl_add(ice->card, ctl); 35162306a36Sopenharmony_ci if (err < 0) 35262306a36Sopenharmony_ci return err; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void psc724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 36162306a36Sopenharmony_ci /* restore codec volume settings after rate change (PMCLK stop) */ 36262306a36Sopenharmony_ci snd_wm8776_volume_restore(&spec->wm8776); 36362306a36Sopenharmony_ci snd_wm8766_volume_restore(&spec->wm8766); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* power management */ 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 36962306a36Sopenharmony_cistatic int psc724_resume(struct snd_ice1712 *ice) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci snd_wm8776_resume(&spec->wm8776); 37462306a36Sopenharmony_ci snd_wm8766_resume(&spec->wm8766); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci#endif 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* init */ 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int psc724_init(struct snd_ice1712 *ice) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct psc724_spec *spec; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci spec = kzalloc(sizeof(*spec), GFP_KERNEL); 38762306a36Sopenharmony_ci if (!spec) 38862306a36Sopenharmony_ci return -ENOMEM; 38962306a36Sopenharmony_ci ice->spec = spec; 39062306a36Sopenharmony_ci spec->ice = ice; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ice->num_total_dacs = 6; 39362306a36Sopenharmony_ci ice->num_total_adcs = 2; 39462306a36Sopenharmony_ci spec->wm8776.ops.write = psc724_wm8776_write; 39562306a36Sopenharmony_ci spec->wm8776.card = ice->card; 39662306a36Sopenharmony_ci snd_wm8776_init(&spec->wm8776); 39762306a36Sopenharmony_ci spec->wm8766.ops.write = psc724_wm8766_write; 39862306a36Sopenharmony_ci spec->wm8766.card = ice->card; 39962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 40062306a36Sopenharmony_ci ice->pm_resume = psc724_resume; 40162306a36Sopenharmony_ci ice->pm_suspend_enabled = 1; 40262306a36Sopenharmony_ci#endif 40362306a36Sopenharmony_ci snd_wm8766_init(&spec->wm8766); 40462306a36Sopenharmony_ci snd_wm8766_set_if(&spec->wm8766, 40562306a36Sopenharmony_ci WM8766_IF_FMT_I2S | WM8766_IF_IWL_24BIT); 40662306a36Sopenharmony_ci ice->gpio.set_pro_rate = psc724_set_pro_rate; 40762306a36Sopenharmony_ci INIT_DELAYED_WORK(&spec->hp_work, psc724_update_hp_jack_state); 40862306a36Sopenharmony_ci psc724_set_jack_detection(ice, true); 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic void psc724_exit(struct snd_ice1712 *ice) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct psc724_spec *spec = ice->spec; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci cancel_delayed_work_sync(&spec->hp_work); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/* PSC724 has buggy EEPROM (no 96&192kHz, all FFh GPIOs), so override it here */ 42062306a36Sopenharmony_cistatic const unsigned char psc724_eeprom[] = { 42162306a36Sopenharmony_ci [ICE_EEP2_SYSCONF] = 0x42, /* 49.152MHz, 1 ADC, 3 DACs */ 42262306a36Sopenharmony_ci [ICE_EEP2_ACLINK] = 0x80, /* I2S */ 42362306a36Sopenharmony_ci [ICE_EEP2_I2S] = 0xf0, /* I2S volume, 96kHz, 24bit */ 42462306a36Sopenharmony_ci [ICE_EEP2_SPDIF] = 0xc1, /* spdif out-en, out-int, no input */ 42562306a36Sopenharmony_ci /* GPIO outputs */ 42662306a36Sopenharmony_ci [ICE_EEP2_GPIO_DIR2] = 0x5f, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */ 42762306a36Sopenharmony_ci /* GPIO write enable */ 42862306a36Sopenharmony_ci [ICE_EEP2_GPIO_MASK] = 0xff, /* read-only */ 42962306a36Sopenharmony_ci [ICE_EEP2_GPIO_MASK1] = 0xff, /* read-only */ 43062306a36Sopenharmony_ci [ICE_EEP2_GPIO_MASK2] = 0xa0, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */ 43162306a36Sopenharmony_ci /* GPIO initial state */ 43262306a36Sopenharmony_ci [ICE_EEP2_GPIO_STATE2] = 0x20, /* unmuted, all WM8766 pins low */ 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistruct snd_ice1712_card_info snd_vt1724_psc724_cards[] = { 43662306a36Sopenharmony_ci { 43762306a36Sopenharmony_ci .subvendor = VT1724_SUBDEVICE_PSC724, 43862306a36Sopenharmony_ci .name = "Philips PSC724 Ultimate Edge", 43962306a36Sopenharmony_ci .model = "psc724", 44062306a36Sopenharmony_ci .chip_init = psc724_init, 44162306a36Sopenharmony_ci .chip_exit = psc724_exit, 44262306a36Sopenharmony_ci .build_controls = psc724_add_controls, 44362306a36Sopenharmony_ci .eeprom_size = sizeof(psc724_eeprom), 44462306a36Sopenharmony_ci .eeprom_data = psc724_eeprom, 44562306a36Sopenharmony_ci }, 44662306a36Sopenharmony_ci {} /*terminator*/ 44762306a36Sopenharmony_ci}; 448