18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA driver for ICEnsemble VT1724 (Envy24HT) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Lowlevel functions for Philips PSC724 Ultimate Edge 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <sound/core.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "ice1712.h" 168c2ecf20Sopenharmony_ci#include "envy24ht.h" 178c2ecf20Sopenharmony_ci#include "psc724.h" 188c2ecf20Sopenharmony_ci#include "wm8766.h" 198c2ecf20Sopenharmony_ci#include "wm8776.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct psc724_spec { 228c2ecf20Sopenharmony_ci struct snd_wm8766 wm8766; 238c2ecf20Sopenharmony_ci struct snd_wm8776 wm8776; 248c2ecf20Sopenharmony_ci bool mute_all, jack_detect; 258c2ecf20Sopenharmony_ci struct snd_ice1712 *ice; 268c2ecf20Sopenharmony_ci struct delayed_work hp_work; 278c2ecf20Sopenharmony_ci bool hp_connected; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/****************************************************************************/ 318c2ecf20Sopenharmony_ci/* PHILIPS PSC724 ULTIMATE EDGE */ 328c2ecf20Sopenharmony_ci/****************************************************************************/ 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * VT1722 (Envy24GT) - 6 outputs, 4 inputs (only 2 used), 24-bit/96kHz 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * system configuration ICE_EEP2_SYSCONF=0x42 378c2ecf20Sopenharmony_ci * XIN1 49.152MHz 388c2ecf20Sopenharmony_ci * no MPU401 398c2ecf20Sopenharmony_ci * one stereo ADC, no S/PDIF receiver 408c2ecf20Sopenharmony_ci * three stereo DACs (FRONT, REAR, CENTER+LFE) 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * AC-Link configuration ICE_EEP2_ACLINK=0x80 438c2ecf20Sopenharmony_ci * use I2S, not AC97 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * I2S converters feature ICE_EEP2_I2S=0x30 468c2ecf20Sopenharmony_ci * I2S codec has no volume/mute control feature (bug!) 478c2ecf20Sopenharmony_ci * I2S codec does not support 96KHz or 192KHz (bug!) 488c2ecf20Sopenharmony_ci * I2S codec 24bits 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * S/PDIF configuration ICE_EEP2_SPDIF=0xc1 518c2ecf20Sopenharmony_ci * Enable integrated S/PDIF transmitter 528c2ecf20Sopenharmony_ci * internal S/PDIF out implemented 538c2ecf20Sopenharmony_ci * No S/PDIF input 548c2ecf20Sopenharmony_ci * External S/PDIF out implemented 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * ** connected chips ** 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * WM8776 608c2ecf20Sopenharmony_ci * 2-channel DAC used for main output and stereo ADC (with 10-channel MUX) 618c2ecf20Sopenharmony_ci * AIN1: LINE IN, AIN2: CD/VIDEO, AIN3: AUX, AIN4: Front MIC, AIN5: Rear MIC 628c2ecf20Sopenharmony_ci * Controlled by I2C using VT1722 I2C interface: 638c2ecf20Sopenharmony_ci * MODE (pin16) -- GND 648c2ecf20Sopenharmony_ci * CE (pin17) -- GND I2C mode (address=0x34) 658c2ecf20Sopenharmony_ci * DI (pin18) -- SDA (VT1722 pin70) 668c2ecf20Sopenharmony_ci * CL (pin19) -- SCLK (VT1722 pin71) 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * WM8766 698c2ecf20Sopenharmony_ci * 6-channel DAC used for rear & center/LFE outputs (only 4 channels used) 708c2ecf20Sopenharmony_ci * Controlled by SPI using VT1722 GPIO pins: 718c2ecf20Sopenharmony_ci * MODE (pin 1) -- GPIO19 (VT1722 pin99) 728c2ecf20Sopenharmony_ci * ML/I2S (pin11) -- GPIO18 (VT1722 pin98) 738c2ecf20Sopenharmony_ci * MC/IWL (pin12) -- GPIO17 (VT1722 pin97) 748c2ecf20Sopenharmony_ci * MD/DM (pin13) -- GPIO16 (VT1722 pin96) 758c2ecf20Sopenharmony_ci * MUTE (pin14) -- GPIO20 (VT1722 pin101) 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * GPIO14 is used as input for headphone jack detection (1 = connected) 788c2ecf20Sopenharmony_ci * GPIO22 is used as MUTE ALL output, grounding all 6 channels 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * ** output pins and device names ** 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * 5.1ch name -- output connector color -- device (-D option) 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * FRONT 2ch -- green -- plughw:0,0 858c2ecf20Sopenharmony_ci * CENTER(Lch) SUBWOOFER(Rch) -- orange -- plughw:0,2,0 868c2ecf20Sopenharmony_ci * REAR 2ch -- black -- plughw:0,2,1 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* codec access low-level functions */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define GPIO_HP_JACK (1 << 14) 928c2ecf20Sopenharmony_ci#define GPIO_MUTE_SUR (1 << 20) 938c2ecf20Sopenharmony_ci#define GPIO_MUTE_ALL (1 << 22) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define JACK_INTERVAL 1000 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define PSC724_SPI_DELAY 1 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define PSC724_SPI_DATA (1 << 16) 1008c2ecf20Sopenharmony_ci#define PSC724_SPI_CLK (1 << 17) 1018c2ecf20Sopenharmony_ci#define PSC724_SPI_LOAD (1 << 18) 1028c2ecf20Sopenharmony_ci#define PSC724_SPI_MASK (PSC724_SPI_DATA | PSC724_SPI_CLK | PSC724_SPI_LOAD) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void psc724_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8766); 1078c2ecf20Sopenharmony_ci struct snd_ice1712 *ice = spec->ice; 1088c2ecf20Sopenharmony_ci u32 st, bits; 1098c2ecf20Sopenharmony_ci int i; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci snd_ice1712_save_gpio_status(ice); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci st = ((addr & 0x7f) << 9) | (data & 0x1ff); 1148c2ecf20Sopenharmony_ci snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | PSC724_SPI_MASK); 1158c2ecf20Sopenharmony_ci snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~PSC724_SPI_MASK); 1168c2ecf20Sopenharmony_ci bits = snd_ice1712_gpio_read(ice) & ~PSC724_SPI_MASK; 1178c2ecf20Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 1208c2ecf20Sopenharmony_ci udelay(PSC724_SPI_DELAY); 1218c2ecf20Sopenharmony_ci bits &= ~PSC724_SPI_CLK; 1228c2ecf20Sopenharmony_ci /* MSB first */ 1238c2ecf20Sopenharmony_ci st <<= 1; 1248c2ecf20Sopenharmony_ci if (st & 0x10000) 1258c2ecf20Sopenharmony_ci bits |= PSC724_SPI_DATA; 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci bits &= ~PSC724_SPI_DATA; 1288c2ecf20Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 1298c2ecf20Sopenharmony_ci /* CLOCK high */ 1308c2ecf20Sopenharmony_ci udelay(PSC724_SPI_DELAY); 1318c2ecf20Sopenharmony_ci bits |= PSC724_SPI_CLK; 1328c2ecf20Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci /* LOAD high */ 1358c2ecf20Sopenharmony_ci udelay(PSC724_SPI_DELAY); 1368c2ecf20Sopenharmony_ci bits |= PSC724_SPI_LOAD; 1378c2ecf20Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 1388c2ecf20Sopenharmony_ci /* LOAD low, DATA and CLOCK high */ 1398c2ecf20Sopenharmony_ci udelay(PSC724_SPI_DELAY); 1408c2ecf20Sopenharmony_ci bits |= (PSC724_SPI_DATA | PSC724_SPI_CLK); 1418c2ecf20Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci snd_ice1712_restore_gpio_status(ice); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void psc724_wm8776_write(struct snd_wm8776 *wm, u8 addr, u8 data) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8776); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci snd_vt1724_write_i2c(spec->ice, 0x34, addr, data); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* mute all */ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void psc724_set_master_switch(struct snd_ice1712 *ice, bool on) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci unsigned int bits = snd_ice1712_gpio_read(ice); 1588c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci spec->mute_all = !on; 1618c2ecf20Sopenharmony_ci if (on) 1628c2ecf20Sopenharmony_ci bits &= ~(GPIO_MUTE_ALL | GPIO_MUTE_SUR); 1638c2ecf20Sopenharmony_ci else 1648c2ecf20Sopenharmony_ci bits |= GPIO_MUTE_ALL | GPIO_MUTE_SUR; 1658c2ecf20Sopenharmony_ci snd_ice1712_gpio_write(ice, bits); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic bool psc724_get_master_switch(struct snd_ice1712 *ice) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return !spec->mute_all; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* jack detection */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 1808c2ecf20Sopenharmony_ci struct snd_ctl_elem_id elem_id; 1818c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 1828c2ecf20Sopenharmony_ci u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci psc724_set_master_switch(ice, !hp_connected); 1858c2ecf20Sopenharmony_ci if (!hp_connected) 1868c2ecf20Sopenharmony_ci power |= WM8776_PWR_HPPD; 1878c2ecf20Sopenharmony_ci snd_wm8776_set_power(&spec->wm8776, power); 1888c2ecf20Sopenharmony_ci spec->hp_connected = hp_connected; 1898c2ecf20Sopenharmony_ci /* notify about master speaker mute change */ 1908c2ecf20Sopenharmony_ci memset(&elem_id, 0, sizeof(elem_id)); 1918c2ecf20Sopenharmony_ci elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1928c2ecf20Sopenharmony_ci strlcpy(elem_id.name, "Master Speakers Playback Switch", 1938c2ecf20Sopenharmony_ci sizeof(elem_id.name)); 1948c2ecf20Sopenharmony_ci kctl = snd_ctl_find_id(ice->card, &elem_id); 1958c2ecf20Sopenharmony_ci snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 1968c2ecf20Sopenharmony_ci /* and headphone mute change */ 1978c2ecf20Sopenharmony_ci strlcpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name, 1988c2ecf20Sopenharmony_ci sizeof(elem_id.name)); 1998c2ecf20Sopenharmony_ci kctl = snd_ctl_find_id(ice->card, &elem_id); 2008c2ecf20Sopenharmony_ci snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void psc724_update_hp_jack_state(struct work_struct *work) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct psc724_spec *spec = container_of(work, struct psc724_spec, 2068c2ecf20Sopenharmony_ci hp_work.work); 2078c2ecf20Sopenharmony_ci struct snd_ice1712 *ice = spec->ice; 2088c2ecf20Sopenharmony_ci bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci schedule_delayed_work(&spec->hp_work, msecs_to_jiffies(JACK_INTERVAL)); 2118c2ecf20Sopenharmony_ci if (hp_connected == spec->hp_connected) 2128c2ecf20Sopenharmony_ci return; 2138c2ecf20Sopenharmony_ci psc724_set_jack_state(ice, hp_connected); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void psc724_set_jack_detection(struct snd_ice1712 *ice, bool on) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (spec->jack_detect == on) 2218c2ecf20Sopenharmony_ci return; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci spec->jack_detect = on; 2248c2ecf20Sopenharmony_ci if (on) { 2258c2ecf20Sopenharmony_ci bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK; 2268c2ecf20Sopenharmony_ci psc724_set_jack_state(ice, hp_connected); 2278c2ecf20Sopenharmony_ci schedule_delayed_work(&spec->hp_work, 2288c2ecf20Sopenharmony_ci msecs_to_jiffies(JACK_INTERVAL)); 2298c2ecf20Sopenharmony_ci } else 2308c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&spec->hp_work); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic bool psc724_get_jack_detection(struct snd_ice1712 *ice) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return spec->jack_detect; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* mixer controls */ 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistruct psc724_control { 2438c2ecf20Sopenharmony_ci const char *name; 2448c2ecf20Sopenharmony_ci void (*set)(struct snd_ice1712 *ice, bool on); 2458c2ecf20Sopenharmony_ci bool (*get)(struct snd_ice1712 *ice); 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const struct psc724_control psc724_cont[] = { 2498c2ecf20Sopenharmony_ci { 2508c2ecf20Sopenharmony_ci .name = "Master Speakers Playback Switch", 2518c2ecf20Sopenharmony_ci .set = psc724_set_master_switch, 2528c2ecf20Sopenharmony_ci .get = psc724_get_master_switch, 2538c2ecf20Sopenharmony_ci }, 2548c2ecf20Sopenharmony_ci { 2558c2ecf20Sopenharmony_ci .name = "Headphone Jack Detection Playback Switch", 2568c2ecf20Sopenharmony_ci .set = psc724_set_jack_detection, 2578c2ecf20Sopenharmony_ci .get = psc724_get_jack_detection, 2588c2ecf20Sopenharmony_ci }, 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int psc724_ctl_get(struct snd_kcontrol *kcontrol, 2628c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 2658c2ecf20Sopenharmony_ci int n = kcontrol->private_value; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = psc724_cont[n].get(ice); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int psc724_ctl_put(struct snd_kcontrol *kcontrol, 2738c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 2768c2ecf20Sopenharmony_ci int n = kcontrol->private_value; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci psc724_cont[n].set(ice, ucontrol->value.integer.value[0]); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic const char *front_volume = "Front Playback Volume"; 2848c2ecf20Sopenharmony_cistatic const char *front_switch = "Front Playback Switch"; 2858c2ecf20Sopenharmony_cistatic const char *front_zc = "Front Zero Cross Detect Playback Switch"; 2868c2ecf20Sopenharmony_cistatic const char *front_izd = "Front Infinite Zero Detect Playback Switch"; 2878c2ecf20Sopenharmony_cistatic const char *front_phase = "Front Phase Invert Playback Switch"; 2888c2ecf20Sopenharmony_cistatic const char *front_deemph = "Front Deemphasis Playback Switch"; 2898c2ecf20Sopenharmony_cistatic const char *ain1_switch = "Line Capture Switch"; 2908c2ecf20Sopenharmony_cistatic const char *ain2_switch = "CD Capture Switch"; 2918c2ecf20Sopenharmony_cistatic const char *ain3_switch = "AUX Capture Switch"; 2928c2ecf20Sopenharmony_cistatic const char *ain4_switch = "Front Mic Capture Switch"; 2938c2ecf20Sopenharmony_cistatic const char *ain5_switch = "Rear Mic Capture Switch"; 2948c2ecf20Sopenharmony_cistatic const char *rear_volume = "Surround Playback Volume"; 2958c2ecf20Sopenharmony_cistatic const char *clfe_volume = "CLFE Playback Volume"; 2968c2ecf20Sopenharmony_cistatic const char *rear_switch = "Surround Playback Switch"; 2978c2ecf20Sopenharmony_cistatic const char *clfe_switch = "CLFE Playback Switch"; 2988c2ecf20Sopenharmony_cistatic const char *rear_phase = "Surround Phase Invert Playback Switch"; 2998c2ecf20Sopenharmony_cistatic const char *clfe_phase = "CLFE Phase Invert Playback Switch"; 3008c2ecf20Sopenharmony_cistatic const char *rear_deemph = "Surround Deemphasis Playback Switch"; 3018c2ecf20Sopenharmony_cistatic const char *clfe_deemph = "CLFE Deemphasis Playback Switch"; 3028c2ecf20Sopenharmony_cistatic const char *rear_clfe_izd = "Rear Infinite Zero Detect Playback Switch"; 3038c2ecf20Sopenharmony_cistatic const char *rear_clfe_zc = "Rear Zero Cross Detect Playback Switch"; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int psc724_add_controls(struct snd_ice1712 *ice) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct snd_kcontrol_new cont; 3088c2ecf20Sopenharmony_ci struct snd_kcontrol *ctl; 3098c2ecf20Sopenharmony_ci int err, i; 3108c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_VOL].name = front_volume; 3138c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_SW].name = front_switch; 3148c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_ZC_SW].name = front_zc; 3158c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_AUX_SW].name = NULL; 3168c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DAC_IZD_SW].name = front_izd; 3178c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_PHASE_SW].name = front_phase; 3188c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_DEEMPH_SW].name = front_deemph; 3198c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT1_SW].name = ain1_switch; 3208c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT2_SW].name = ain2_switch; 3218c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT3_SW].name = ain3_switch; 3228c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT4_SW].name = ain4_switch; 3238c2ecf20Sopenharmony_ci spec->wm8776.ctl[WM8776_CTL_INPUT5_SW].name = ain5_switch; 3248c2ecf20Sopenharmony_ci snd_wm8776_build_controls(&spec->wm8776); 3258c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH1_VOL].name = rear_volume; 3268c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH2_VOL].name = clfe_volume; 3278c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH3_VOL].name = NULL; 3288c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH1_SW].name = rear_switch; 3298c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH2_SW].name = clfe_switch; 3308c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_CH3_SW].name = NULL; 3318c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_PHASE1_SW].name = rear_phase; 3328c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_PHASE2_SW].name = clfe_phase; 3338c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_PHASE3_SW].name = NULL; 3348c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_DEEMPH1_SW].name = rear_deemph; 3358c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_DEEMPH2_SW].name = clfe_deemph; 3368c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_DEEMPH3_SW].name = NULL; 3378c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_IZD_SW].name = rear_clfe_izd; 3388c2ecf20Sopenharmony_ci spec->wm8766.ctl[WM8766_CTL_ZC_SW].name = rear_clfe_zc; 3398c2ecf20Sopenharmony_ci snd_wm8766_build_controls(&spec->wm8766); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci memset(&cont, 0, sizeof(cont)); 3428c2ecf20Sopenharmony_ci cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 3438c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(psc724_cont); i++) { 3448c2ecf20Sopenharmony_ci cont.private_value = i; 3458c2ecf20Sopenharmony_ci cont.name = psc724_cont[i].name; 3468c2ecf20Sopenharmony_ci cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 3478c2ecf20Sopenharmony_ci cont.info = snd_ctl_boolean_mono_info; 3488c2ecf20Sopenharmony_ci cont.get = psc724_ctl_get; 3498c2ecf20Sopenharmony_ci cont.put = psc724_ctl_put; 3508c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&cont, ice); 3518c2ecf20Sopenharmony_ci if (!ctl) 3528c2ecf20Sopenharmony_ci return -ENOMEM; 3538c2ecf20Sopenharmony_ci err = snd_ctl_add(ice->card, ctl); 3548c2ecf20Sopenharmony_ci if (err < 0) 3558c2ecf20Sopenharmony_ci return err; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void psc724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 3648c2ecf20Sopenharmony_ci /* restore codec volume settings after rate change (PMCLK stop) */ 3658c2ecf20Sopenharmony_ci snd_wm8776_volume_restore(&spec->wm8776); 3668c2ecf20Sopenharmony_ci snd_wm8766_volume_restore(&spec->wm8766); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* power management */ 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3728c2ecf20Sopenharmony_cistatic int psc724_resume(struct snd_ice1712 *ice) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci snd_wm8776_resume(&spec->wm8776); 3778c2ecf20Sopenharmony_ci snd_wm8766_resume(&spec->wm8766); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci#endif 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* init */ 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int psc724_init(struct snd_ice1712 *ice) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct psc724_spec *spec; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3908c2ecf20Sopenharmony_ci if (!spec) 3918c2ecf20Sopenharmony_ci return -ENOMEM; 3928c2ecf20Sopenharmony_ci ice->spec = spec; 3938c2ecf20Sopenharmony_ci spec->ice = ice; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci ice->num_total_dacs = 6; 3968c2ecf20Sopenharmony_ci ice->num_total_adcs = 2; 3978c2ecf20Sopenharmony_ci spec->wm8776.ops.write = psc724_wm8776_write; 3988c2ecf20Sopenharmony_ci spec->wm8776.card = ice->card; 3998c2ecf20Sopenharmony_ci snd_wm8776_init(&spec->wm8776); 4008c2ecf20Sopenharmony_ci spec->wm8766.ops.write = psc724_wm8766_write; 4018c2ecf20Sopenharmony_ci spec->wm8766.card = ice->card; 4028c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4038c2ecf20Sopenharmony_ci ice->pm_resume = psc724_resume; 4048c2ecf20Sopenharmony_ci ice->pm_suspend_enabled = 1; 4058c2ecf20Sopenharmony_ci#endif 4068c2ecf20Sopenharmony_ci snd_wm8766_init(&spec->wm8766); 4078c2ecf20Sopenharmony_ci snd_wm8766_set_if(&spec->wm8766, 4088c2ecf20Sopenharmony_ci WM8766_IF_FMT_I2S | WM8766_IF_IWL_24BIT); 4098c2ecf20Sopenharmony_ci ice->gpio.set_pro_rate = psc724_set_pro_rate; 4108c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&spec->hp_work, psc724_update_hp_jack_state); 4118c2ecf20Sopenharmony_ci psc724_set_jack_detection(ice, true); 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic void psc724_exit(struct snd_ice1712 *ice) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct psc724_spec *spec = ice->spec; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&spec->hp_work); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* PSC724 has buggy EEPROM (no 96&192kHz, all FFh GPIOs), so override it here */ 4238c2ecf20Sopenharmony_cistatic const unsigned char psc724_eeprom[] = { 4248c2ecf20Sopenharmony_ci [ICE_EEP2_SYSCONF] = 0x42, /* 49.152MHz, 1 ADC, 3 DACs */ 4258c2ecf20Sopenharmony_ci [ICE_EEP2_ACLINK] = 0x80, /* I2S */ 4268c2ecf20Sopenharmony_ci [ICE_EEP2_I2S] = 0xf0, /* I2S volume, 96kHz, 24bit */ 4278c2ecf20Sopenharmony_ci [ICE_EEP2_SPDIF] = 0xc1, /* spdif out-en, out-int, no input */ 4288c2ecf20Sopenharmony_ci /* GPIO outputs */ 4298c2ecf20Sopenharmony_ci [ICE_EEP2_GPIO_DIR2] = 0x5f, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */ 4308c2ecf20Sopenharmony_ci /* GPIO write enable */ 4318c2ecf20Sopenharmony_ci [ICE_EEP2_GPIO_MASK] = 0xff, /* read-only */ 4328c2ecf20Sopenharmony_ci [ICE_EEP2_GPIO_MASK1] = 0xff, /* read-only */ 4338c2ecf20Sopenharmony_ci [ICE_EEP2_GPIO_MASK2] = 0xa0, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */ 4348c2ecf20Sopenharmony_ci /* GPIO initial state */ 4358c2ecf20Sopenharmony_ci [ICE_EEP2_GPIO_STATE2] = 0x20, /* unmuted, all WM8766 pins low */ 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistruct snd_ice1712_card_info snd_vt1724_psc724_cards[] = { 4398c2ecf20Sopenharmony_ci { 4408c2ecf20Sopenharmony_ci .subvendor = VT1724_SUBDEVICE_PSC724, 4418c2ecf20Sopenharmony_ci .name = "Philips PSC724 Ultimate Edge", 4428c2ecf20Sopenharmony_ci .model = "psc724", 4438c2ecf20Sopenharmony_ci .chip_init = psc724_init, 4448c2ecf20Sopenharmony_ci .chip_exit = psc724_exit, 4458c2ecf20Sopenharmony_ci .build_controls = psc724_add_controls, 4468c2ecf20Sopenharmony_ci .eeprom_size = sizeof(psc724_eeprom), 4478c2ecf20Sopenharmony_ci .eeprom_data = psc724_eeprom, 4488c2ecf20Sopenharmony_ci }, 4498c2ecf20Sopenharmony_ci {} /*terminator*/ 4508c2ecf20Sopenharmony_ci}; 451