18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * wm9713.c -- ALSA Soc WM9713 codec support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2006-10 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci * Author: Liam Girdwood <lrg@slimlogic.co.uk> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Features:- 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * o Support for AC97 Codec, Voice DAC and Aux DAC 118c2ecf20Sopenharmony_ci * o Support for DAPM 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/mfd/wm97xx.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm.h> 228c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 238c2ecf20Sopenharmony_ci#include <sound/ac97/codec.h> 248c2ecf20Sopenharmony_ci#include <sound/ac97/compat.h> 258c2ecf20Sopenharmony_ci#include <sound/initval.h> 268c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 278c2ecf20Sopenharmony_ci#include <sound/tlv.h> 288c2ecf20Sopenharmony_ci#include <sound/soc.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "wm9713.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define WM9713_VENDOR_ID 0x574d4c13 338c2ecf20Sopenharmony_ci#define WM9713_VENDOR_ID_MASK 0xffffffff 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct wm9713_priv { 368c2ecf20Sopenharmony_ci struct snd_ac97 *ac97; 378c2ecf20Sopenharmony_ci u32 pll_in; /* PLL input frequency */ 388c2ecf20Sopenharmony_ci unsigned int hp_mixer[2]; 398c2ecf20Sopenharmony_ci struct mutex lock; 408c2ecf20Sopenharmony_ci struct wm97xx_platform_data *mfd_pdata; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define HPL_MIXER 0 448c2ecf20Sopenharmony_ci#define HPR_MIXER 1 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; 478c2ecf20Sopenharmony_cistatic const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; 488c2ecf20Sopenharmony_cistatic const char *wm9713_rec_src[] = 498c2ecf20Sopenharmony_ci {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker", 508c2ecf20Sopenharmony_ci "Mono Out", "Zh"}; 518c2ecf20Sopenharmony_cistatic const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; 528c2ecf20Sopenharmony_cistatic const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"}; 538c2ecf20Sopenharmony_cistatic const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv"}; 548c2ecf20Sopenharmony_cistatic const char *wm9713_spk_pga[] = 558c2ecf20Sopenharmony_ci {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid", 568c2ecf20Sopenharmony_ci "Speaker Vmid", "Inv Vmid"}; 578c2ecf20Sopenharmony_cistatic const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone", 588c2ecf20Sopenharmony_ci "Headphone Vmid"}; 598c2ecf20Sopenharmony_cistatic const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"}; 608c2ecf20Sopenharmony_cistatic const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"}; 618c2ecf20Sopenharmony_cistatic const char *wm9713_dac_inv[] = 628c2ecf20Sopenharmony_ci {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone", 638c2ecf20Sopenharmony_ci "Headphone Mono", "NC", "Vmid"}; 648c2ecf20Sopenharmony_cistatic const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"}; 658c2ecf20Sopenharmony_cistatic const char *wm9713_ng_type[] = {"Constant Gain", "Mute"}; 668c2ecf20Sopenharmony_cistatic const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"}; 678c2ecf20Sopenharmony_cistatic const char *wm9713_micb_select[] = {"MPB", "MPA"}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic const struct soc_enum wm9713_enum[] = { 708c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */ 718c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */ 728c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */ 738c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */ 748c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/ 758c2ecf20Sopenharmony_ciSOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */ 768c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/ 778c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */ 788c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */ 798c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */ 808c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */ 818c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */ 828c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */ 838c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */ 848c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */ 858c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ 868c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ 878c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ 888c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ 898c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */ 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0); 938c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0); 948c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0); 958c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(mic_tlv, 968c2ecf20Sopenharmony_ci 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0), 978c2ecf20Sopenharmony_ci 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0) 988c2ecf20Sopenharmony_ci); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = { 1018c2ecf20Sopenharmony_ciSOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv), 1028c2ecf20Sopenharmony_ciSOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1), 1038c2ecf20Sopenharmony_ciSOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1, 1048c2ecf20Sopenharmony_ci out_tlv), 1058c2ecf20Sopenharmony_ciSOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1), 1068c2ecf20Sopenharmony_ciSOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv), 1078c2ecf20Sopenharmony_ciSOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv), 1088c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv), 1098c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), 1108c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mic 1 Preamp Volume", AC97_3D_CONTROL, 10, 3, 0, mic_tlv), 1118c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mic 2 Preamp Volume", AC97_3D_CONTROL, 12, 3, 0, mic_tlv), 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciSOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), 1148c2ecf20Sopenharmony_ciSOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciSOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1), 1178c2ecf20Sopenharmony_ciSOC_ENUM("Capture Volume Steps", wm9713_enum[5]), 1188c2ecf20Sopenharmony_ciSOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0), 1198c2ecf20Sopenharmony_ciSOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1, misc_tlv), 1228c2ecf20Sopenharmony_ciSOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), 1238c2ecf20Sopenharmony_ciSOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciSOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), 1268c2ecf20Sopenharmony_ciSOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), 1278c2ecf20Sopenharmony_ciSOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0), 1288c2ecf20Sopenharmony_ciSOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), 1298c2ecf20Sopenharmony_ciSOC_ENUM("ALC Function", wm9713_enum[6]), 1308c2ecf20Sopenharmony_ciSOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), 1318c2ecf20Sopenharmony_ciSOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0), 1328c2ecf20Sopenharmony_ciSOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), 1338c2ecf20Sopenharmony_ciSOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), 1348c2ecf20Sopenharmony_ciSOC_ENUM("ALC NG Type", wm9713_enum[17]), 1358c2ecf20Sopenharmony_ciSOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0), 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciSOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0), 1388c2ecf20Sopenharmony_ciSOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciSOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1), 1418c2ecf20Sopenharmony_ciSOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0), 1428c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Out4 Playback Volume", AC97_MASTER_MONO, 8, 31, 1, out_tlv), 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ciSOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1), 1458c2ecf20Sopenharmony_ciSOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0), 1468c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Out3 Playback Volume", AC97_MASTER_MONO, 0, 31, 1, out_tlv), 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1, main_tlv), 1498c2ecf20Sopenharmony_ciSOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1), 1508c2ecf20Sopenharmony_ciSOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0), 1518c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1, out_tlv), 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1, 1548c2ecf20Sopenharmony_ci misc_tlv), 1558c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Speaker Mixer Beep Playback Volume", AC97_AUX, 8, 7, 1, 1568c2ecf20Sopenharmony_ci misc_tlv), 1578c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mono Mixer Beep Playback Volume", AC97_AUX, 4, 7, 1, misc_tlv), 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1, 1608c2ecf20Sopenharmony_ci misc_tlv), 1618c2ecf20Sopenharmony_ciSOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1), 1628c2ecf20Sopenharmony_ciSOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1), 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Headphone Mixer Aux Playback Volume", AC97_REC_SEL, 12, 7, 1, 1658c2ecf20Sopenharmony_ci misc_tlv), 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Speaker Mixer Voice Playback Volume", AC97_PCM, 8, 7, 1, 1688c2ecf20Sopenharmony_ci misc_tlv), 1698c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Speaker Mixer Aux Playback Volume", AC97_REC_SEL, 8, 7, 1, 1708c2ecf20Sopenharmony_ci misc_tlv), 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mono Mixer Voice Playback Volume", AC97_PCM, 4, 7, 1, 1738c2ecf20Sopenharmony_ci misc_tlv), 1748c2ecf20Sopenharmony_ciSOC_SINGLE_TLV("Mono Mixer Aux Playback Volume", AC97_REC_SEL, 4, 7, 1, 1758c2ecf20Sopenharmony_ci misc_tlv), 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciSOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1), 1788c2ecf20Sopenharmony_ciSOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1), 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciSOC_ENUM("Bass Control", wm9713_enum[16]), 1818c2ecf20Sopenharmony_ciSOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), 1828c2ecf20Sopenharmony_ciSOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1), 1838c2ecf20Sopenharmony_ciSOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0), 1848c2ecf20Sopenharmony_ciSOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1), 1858c2ecf20Sopenharmony_ciSOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1), 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciSOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0), 1888c2ecf20Sopenharmony_ciSOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0), 1898c2ecf20Sopenharmony_ciSOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, 1938c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD)) 1988c2ecf20Sopenharmony_ci return -EINVAL; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Gracefully shut down the voice interface. */ 2018c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, 0x0200); 2028c2ecf20Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(1)); 2038c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, 0x0f00); 2048c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x1000, 0x1000); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic const unsigned int wm9713_mixer_mute_regs[] = { 2108c2ecf20Sopenharmony_ci AC97_PC_BEEP, 2118c2ecf20Sopenharmony_ci AC97_MASTER_TONE, 2128c2ecf20Sopenharmony_ci AC97_PHONE, 2138c2ecf20Sopenharmony_ci AC97_REC_SEL, 2148c2ecf20Sopenharmony_ci AC97_PCM, 2158c2ecf20Sopenharmony_ci AC97_AUX, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* We have to create a fake left and right HP mixers because 2198c2ecf20Sopenharmony_ci * the codec only has a single control that is shared by both channels. 2208c2ecf20Sopenharmony_ci * This makes it impossible to determine the audio path using the current 2218c2ecf20Sopenharmony_ci * register map, thus we add a new (virtual) register to help determine the 2228c2ecf20Sopenharmony_ci * audio route within the device. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_cistatic int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, 2258c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 2288c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); 2298c2ecf20Sopenharmony_ci struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); 2308c2ecf20Sopenharmony_ci unsigned int val = ucontrol->value.integer.value[0]; 2318c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 2328c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 2338c2ecf20Sopenharmony_ci unsigned int mixer, mask, shift, old; 2348c2ecf20Sopenharmony_ci struct snd_soc_dapm_update update = {}; 2358c2ecf20Sopenharmony_ci bool change; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci mixer = mc->shift >> 8; 2388c2ecf20Sopenharmony_ci shift = mc->shift & 0xff; 2398c2ecf20Sopenharmony_ci mask = (1 << shift); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci mutex_lock(&wm9713->lock); 2428c2ecf20Sopenharmony_ci old = wm9713->hp_mixer[mixer]; 2438c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0]) 2448c2ecf20Sopenharmony_ci wm9713->hp_mixer[mixer] |= mask; 2458c2ecf20Sopenharmony_ci else 2468c2ecf20Sopenharmony_ci wm9713->hp_mixer[mixer] &= ~mask; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci change = old != wm9713->hp_mixer[mixer]; 2498c2ecf20Sopenharmony_ci if (change) { 2508c2ecf20Sopenharmony_ci update.kcontrol = kcontrol; 2518c2ecf20Sopenharmony_ci update.reg = wm9713_mixer_mute_regs[shift]; 2528c2ecf20Sopenharmony_ci update.mask = 0x8000; 2538c2ecf20Sopenharmony_ci if ((wm9713->hp_mixer[0] & mask) || 2548c2ecf20Sopenharmony_ci (wm9713->hp_mixer[1] & mask)) 2558c2ecf20Sopenharmony_ci update.val = 0x0; 2568c2ecf20Sopenharmony_ci else 2578c2ecf20Sopenharmony_ci update.val = 0x8000; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, 2608c2ecf20Sopenharmony_ci &update); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci mutex_unlock(&wm9713->lock); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return change; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol, 2698c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 2728c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); 2738c2ecf20Sopenharmony_ci struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); 2748c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 2758c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 2768c2ecf20Sopenharmony_ci unsigned int mixer, shift; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci mixer = mc->shift >> 8; 2798c2ecf20Sopenharmony_ci shift = mc->shift & 0xff; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 2828c2ecf20Sopenharmony_ci (wm9713->hp_mixer[mixer] >> shift) & 1; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \ 2888c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 2898c2ecf20Sopenharmony_ci .info = snd_soc_info_volsw, \ 2908c2ecf20Sopenharmony_ci .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \ 2918c2ecf20Sopenharmony_ci .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \ 2928c2ecf20Sopenharmony_ci xshift, xmixer, 1, 0, 0) \ 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* Left Headphone Mixers */ 2968c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { 2978c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5), 2988c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4), 2998c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3), 3008c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2), 3018c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1), 3028c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0), 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci/* Right Headphone Mixers */ 3068c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { 3078c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5), 3088c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4), 3098c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3), 3108c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2), 3118c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1), 3128c2ecf20Sopenharmony_ciWM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0), 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* headphone capture mux */ 3168c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hp_rec_mux_controls = 3178c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[1]); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* headphone mic mux */ 3208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hp_mic_mux_controls = 3218c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[0]); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* Speaker Mixer */ 3248c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = { 3258c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 11, 1, 1), 3268c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1), 3278c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1), 3288c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1), 3298c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1), 3308c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1), 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* Mono Mixer */ 3348c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = { 3358c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 7, 1, 1), 3368c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1), 3378c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1), 3388c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1), 3398c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1), 3408c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1), 3418c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1), 3428c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1), 3438c2ecf20Sopenharmony_ci}; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/* mono mic mux */ 3468c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_mono_mic_mux_controls = 3478c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[2]); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci/* mono output mux */ 3508c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_mono_mux_controls = 3518c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[7]); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* speaker left output mux */ 3548c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls = 3558c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[8]); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* speaker right output mux */ 3588c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls = 3598c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[9]); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* headphone left output mux */ 3628c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hpl_out_mux_controls = 3638c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[10]); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/* headphone right output mux */ 3668c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_hpr_out_mux_controls = 3678c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[11]); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* Out3 mux */ 3708c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_out3_mux_controls = 3718c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[12]); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/* Out4 mux */ 3748c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_out4_mux_controls = 3758c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[13]); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* DAC inv mux 1 */ 3788c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls = 3798c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[14]); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* DAC inv mux 2 */ 3828c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls = 3838c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[15]); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/* Capture source left */ 3868c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls = 3878c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[3]); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* Capture source right */ 3908c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls = 3918c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[4]); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* mic source */ 3948c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_mic_sel_mux_controls = 3958c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[18]); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* mic source B virtual control */ 3988c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9713_micb_sel_mux_controls = 3998c2ecf20Sopenharmony_ciSOC_DAPM_ENUM("Route", wm9713_enum[19]); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = { 4028c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0, 4038c2ecf20Sopenharmony_ci &wm9713_hp_rec_mux_controls), 4048c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, 4058c2ecf20Sopenharmony_ci &wm9713_hp_mic_mux_controls), 4068c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0, 4078c2ecf20Sopenharmony_ci &wm9713_mono_mic_mux_controls), 4088c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0, 4098c2ecf20Sopenharmony_ci &wm9713_mono_mux_controls), 4108c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0, 4118c2ecf20Sopenharmony_ci &wm9713_hp_spkl_mux_controls), 4128c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0, 4138c2ecf20Sopenharmony_ci &wm9713_hp_spkr_mux_controls), 4148c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0, 4158c2ecf20Sopenharmony_ci &wm9713_hpl_out_mux_controls), 4168c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0, 4178c2ecf20Sopenharmony_ci &wm9713_hpr_out_mux_controls), 4188c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0, 4198c2ecf20Sopenharmony_ci &wm9713_out3_mux_controls), 4208c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0, 4218c2ecf20Sopenharmony_ci &wm9713_out4_mux_controls), 4228c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0, 4238c2ecf20Sopenharmony_ci &wm9713_dac_inv1_mux_controls), 4248c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0, 4258c2ecf20Sopenharmony_ci &wm9713_dac_inv2_mux_controls), 4268c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0, 4278c2ecf20Sopenharmony_ci &wm9713_rec_srcl_mux_controls), 4288c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0, 4298c2ecf20Sopenharmony_ci &wm9713_rec_srcr_mux_controls), 4308c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, 4318c2ecf20Sopenharmony_ci &wm9713_mic_sel_mux_controls), 4328c2ecf20Sopenharmony_ciSND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, 4338c2ecf20Sopenharmony_ci &wm9713_micb_sel_mux_controls), 4348c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, 4358c2ecf20Sopenharmony_ci &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)), 4368c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, 4378c2ecf20Sopenharmony_ci &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)), 4388c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, 4398c2ecf20Sopenharmony_ci &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), 4408c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, 4418c2ecf20Sopenharmony_ci &wm9713_speaker_mixer_controls[0], 4428c2ecf20Sopenharmony_ci ARRAY_SIZE(wm9713_speaker_mixer_controls)), 4438c2ecf20Sopenharmony_ciSND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1), 4448c2ecf20Sopenharmony_ciSND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1), 4458c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 4468c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 4478c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 4488c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 4498c2ecf20Sopenharmony_ciSND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1, 4508c2ecf20Sopenharmony_ci wm9713_voice_shutdown, SND_SOC_DAPM_PRE_PMD), 4518c2ecf20Sopenharmony_ciSND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), 4528c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0), 4538c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0), 4548c2ecf20Sopenharmony_ciSND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0), 4558c2ecf20Sopenharmony_ciSND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0), 4568c2ecf20Sopenharmony_ciSND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0), 4578c2ecf20Sopenharmony_ciSND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0), 4588c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), 4598c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), 4608c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), 4618c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0), 4628c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0), 4638c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0), 4648c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0), 4658c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0), 4668c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0), 4678c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0), 4688c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0), 4698c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0), 4708c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0), 4718c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0), 4728c2ecf20Sopenharmony_ciSND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1), 4738c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("MONO"), 4748c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPL"), 4758c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPR"), 4768c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKL"), 4778c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKR"), 4788c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("OUT3"), 4798c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("OUT4"), 4808c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("LINEL"), 4818c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("LINER"), 4828c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("MONOIN"), 4838c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("PCBEEP"), 4848c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("MIC1"), 4858c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("MIC2A"), 4868c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("MIC2B"), 4878c2ecf20Sopenharmony_ciSND_SOC_DAPM_VMID("VMID"), 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route wm9713_audio_map[] = { 4918c2ecf20Sopenharmony_ci /* left HP mixer */ 4928c2ecf20Sopenharmony_ci {"Left HP Mixer", "Beep Playback Switch", "PCBEEP"}, 4938c2ecf20Sopenharmony_ci {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, 4948c2ecf20Sopenharmony_ci {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, 4958c2ecf20Sopenharmony_ci {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"}, 4968c2ecf20Sopenharmony_ci {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, 4978c2ecf20Sopenharmony_ci {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"}, 4988c2ecf20Sopenharmony_ci {"Left HP Mixer", NULL, "Capture Headphone Mux"}, 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* right HP mixer */ 5018c2ecf20Sopenharmony_ci {"Right HP Mixer", "Beep Playback Switch", "PCBEEP"}, 5028c2ecf20Sopenharmony_ci {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"}, 5038c2ecf20Sopenharmony_ci {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, 5048c2ecf20Sopenharmony_ci {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"}, 5058c2ecf20Sopenharmony_ci {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, 5068c2ecf20Sopenharmony_ci {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"}, 5078c2ecf20Sopenharmony_ci {"Right HP Mixer", NULL, "Capture Headphone Mux"}, 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* virtual mixer - mixes left & right channels for spk and mono */ 5108c2ecf20Sopenharmony_ci {"AC97 Mixer", NULL, "Left DAC"}, 5118c2ecf20Sopenharmony_ci {"AC97 Mixer", NULL, "Right DAC"}, 5128c2ecf20Sopenharmony_ci {"Line Mixer", NULL, "Right Line In"}, 5138c2ecf20Sopenharmony_ci {"Line Mixer", NULL, "Left Line In"}, 5148c2ecf20Sopenharmony_ci {"HP Mixer", NULL, "Left HP Mixer"}, 5158c2ecf20Sopenharmony_ci {"HP Mixer", NULL, "Right HP Mixer"}, 5168c2ecf20Sopenharmony_ci {"Capture Mixer", NULL, "Left Capture Source"}, 5178c2ecf20Sopenharmony_ci {"Capture Mixer", NULL, "Right Capture Source"}, 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* speaker mixer */ 5208c2ecf20Sopenharmony_ci {"Speaker Mixer", "Beep Playback Switch", "PCBEEP"}, 5218c2ecf20Sopenharmony_ci {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"}, 5228c2ecf20Sopenharmony_ci {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, 5238c2ecf20Sopenharmony_ci {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"}, 5248c2ecf20Sopenharmony_ci {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, 5258c2ecf20Sopenharmony_ci {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"}, 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* mono mixer */ 5288c2ecf20Sopenharmony_ci {"Mono Mixer", "Beep Playback Switch", "PCBEEP"}, 5298c2ecf20Sopenharmony_ci {"Mono Mixer", "Voice Playback Switch", "Voice DAC"}, 5308c2ecf20Sopenharmony_ci {"Mono Mixer", "Aux Playback Switch", "Aux DAC"}, 5318c2ecf20Sopenharmony_ci {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"}, 5328c2ecf20Sopenharmony_ci {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"}, 5338c2ecf20Sopenharmony_ci {"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"}, 5348c2ecf20Sopenharmony_ci {"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"}, 5358c2ecf20Sopenharmony_ci {"Mono Mixer", NULL, "Capture Mono Mux"}, 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* DAC inv mux 1 */ 5388c2ecf20Sopenharmony_ci {"DAC Inv Mux 1", "Mono", "Mono Mixer"}, 5398c2ecf20Sopenharmony_ci {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"}, 5408c2ecf20Sopenharmony_ci {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"}, 5418c2ecf20Sopenharmony_ci {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"}, 5428c2ecf20Sopenharmony_ci {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"}, 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* DAC inv mux 2 */ 5458c2ecf20Sopenharmony_ci {"DAC Inv Mux 2", "Mono", "Mono Mixer"}, 5468c2ecf20Sopenharmony_ci {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"}, 5478c2ecf20Sopenharmony_ci {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"}, 5488c2ecf20Sopenharmony_ci {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"}, 5498c2ecf20Sopenharmony_ci {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"}, 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* headphone left mux */ 5528c2ecf20Sopenharmony_ci {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"}, 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* headphone right mux */ 5558c2ecf20Sopenharmony_ci {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"}, 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* speaker left mux */ 5588c2ecf20Sopenharmony_ci {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"}, 5598c2ecf20Sopenharmony_ci {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"}, 5608c2ecf20Sopenharmony_ci {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"}, 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* speaker right mux */ 5638c2ecf20Sopenharmony_ci {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"}, 5648c2ecf20Sopenharmony_ci {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"}, 5658c2ecf20Sopenharmony_ci {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"}, 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* mono mux */ 5688c2ecf20Sopenharmony_ci {"Mono Out Mux", "Mono", "Mono Mixer"}, 5698c2ecf20Sopenharmony_ci {"Mono Out Mux", "Inv", "DAC Inv Mux 1"}, 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* out 3 mux */ 5728c2ecf20Sopenharmony_ci {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"}, 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* out 4 mux */ 5758c2ecf20Sopenharmony_ci {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"}, 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* output pga */ 5788c2ecf20Sopenharmony_ci {"HPL", NULL, "Left Headphone"}, 5798c2ecf20Sopenharmony_ci {"Left Headphone", NULL, "Left Headphone Out Mux"}, 5808c2ecf20Sopenharmony_ci {"HPR", NULL, "Right Headphone"}, 5818c2ecf20Sopenharmony_ci {"Right Headphone", NULL, "Right Headphone Out Mux"}, 5828c2ecf20Sopenharmony_ci {"OUT3", NULL, "Out 3"}, 5838c2ecf20Sopenharmony_ci {"Out 3", NULL, "Out 3 Mux"}, 5848c2ecf20Sopenharmony_ci {"OUT4", NULL, "Out 4"}, 5858c2ecf20Sopenharmony_ci {"Out 4", NULL, "Out 4 Mux"}, 5868c2ecf20Sopenharmony_ci {"SPKL", NULL, "Left Speaker"}, 5878c2ecf20Sopenharmony_ci {"Left Speaker", NULL, "Left Speaker Out Mux"}, 5888c2ecf20Sopenharmony_ci {"SPKR", NULL, "Right Speaker"}, 5898c2ecf20Sopenharmony_ci {"Right Speaker", NULL, "Right Speaker Out Mux"}, 5908c2ecf20Sopenharmony_ci {"MONO", NULL, "Mono Out"}, 5918c2ecf20Sopenharmony_ci {"Mono Out", NULL, "Mono Out Mux"}, 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* input pga */ 5948c2ecf20Sopenharmony_ci {"Left Line In", NULL, "LINEL"}, 5958c2ecf20Sopenharmony_ci {"Right Line In", NULL, "LINER"}, 5968c2ecf20Sopenharmony_ci {"Mono In", NULL, "MONOIN"}, 5978c2ecf20Sopenharmony_ci {"Mic A PGA", NULL, "Mic A Pre Amp"}, 5988c2ecf20Sopenharmony_ci {"Mic B PGA", NULL, "Mic B Pre Amp"}, 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* left capture select */ 6018c2ecf20Sopenharmony_ci {"Left Capture Source", "Mic 1", "Mic A Pre Amp"}, 6028c2ecf20Sopenharmony_ci {"Left Capture Source", "Mic 2", "Mic B Pre Amp"}, 6038c2ecf20Sopenharmony_ci {"Left Capture Source", "Line", "LINEL"}, 6048c2ecf20Sopenharmony_ci {"Left Capture Source", "Mono In", "MONOIN"}, 6058c2ecf20Sopenharmony_ci {"Left Capture Source", "Headphone", "Left HP Mixer"}, 6068c2ecf20Sopenharmony_ci {"Left Capture Source", "Speaker", "Speaker Mixer"}, 6078c2ecf20Sopenharmony_ci {"Left Capture Source", "Mono Out", "Mono Mixer"}, 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* right capture select */ 6108c2ecf20Sopenharmony_ci {"Right Capture Source", "Mic 1", "Mic A Pre Amp"}, 6118c2ecf20Sopenharmony_ci {"Right Capture Source", "Mic 2", "Mic B Pre Amp"}, 6128c2ecf20Sopenharmony_ci {"Right Capture Source", "Line", "LINER"}, 6138c2ecf20Sopenharmony_ci {"Right Capture Source", "Mono In", "MONOIN"}, 6148c2ecf20Sopenharmony_ci {"Right Capture Source", "Headphone", "Right HP Mixer"}, 6158c2ecf20Sopenharmony_ci {"Right Capture Source", "Speaker", "Speaker Mixer"}, 6168c2ecf20Sopenharmony_ci {"Right Capture Source", "Mono Out", "Mono Mixer"}, 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* left ADC */ 6198c2ecf20Sopenharmony_ci {"Left ADC", NULL, "Left Capture Source"}, 6208c2ecf20Sopenharmony_ci {"Left Voice ADC", NULL, "Left ADC"}, 6218c2ecf20Sopenharmony_ci {"Left HiFi ADC", NULL, "Left ADC"}, 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* right ADC */ 6248c2ecf20Sopenharmony_ci {"Right ADC", NULL, "Right Capture Source"}, 6258c2ecf20Sopenharmony_ci {"Right Voice ADC", NULL, "Right ADC"}, 6268c2ecf20Sopenharmony_ci {"Right HiFi ADC", NULL, "Right ADC"}, 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* mic */ 6298c2ecf20Sopenharmony_ci {"Mic A Pre Amp", NULL, "Mic A Source"}, 6308c2ecf20Sopenharmony_ci {"Mic A Source", "Mic 1", "MIC1"}, 6318c2ecf20Sopenharmony_ci {"Mic A Source", "Mic 2 A", "MIC2A"}, 6328c2ecf20Sopenharmony_ci {"Mic A Source", "Mic 2 B", "Mic B Source"}, 6338c2ecf20Sopenharmony_ci {"Mic B Pre Amp", "MPB", "Mic B Source"}, 6348c2ecf20Sopenharmony_ci {"Mic B Source", NULL, "MIC2B"}, 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* headphone capture */ 6378c2ecf20Sopenharmony_ci {"Capture Headphone Mux", "Stereo", "Capture Mixer"}, 6388c2ecf20Sopenharmony_ci {"Capture Headphone Mux", "Left", "Left Capture Source"}, 6398c2ecf20Sopenharmony_ci {"Capture Headphone Mux", "Right", "Right Capture Source"}, 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* mono capture */ 6428c2ecf20Sopenharmony_ci {"Capture Mono Mux", "Stereo", "Capture Mixer"}, 6438c2ecf20Sopenharmony_ci {"Capture Mono Mux", "Left", "Left Capture Source"}, 6448c2ecf20Sopenharmony_ci {"Capture Mono Mux", "Right", "Right Capture Source"}, 6458c2ecf20Sopenharmony_ci}; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic bool wm9713_readable_reg(struct device *dev, unsigned int reg) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci switch (reg) { 6508c2ecf20Sopenharmony_ci case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: 6518c2ecf20Sopenharmony_ci case AC97_PCM_LR_ADC_RATE: 6528c2ecf20Sopenharmony_ci case AC97_CENTER_LFE_MASTER: 6538c2ecf20Sopenharmony_ci case AC97_SPDIF ... AC97_LINE1_LEVEL: 6548c2ecf20Sopenharmony_ci case AC97_GPIO_CFG ... 0x5c: 6558c2ecf20Sopenharmony_ci case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: 6568c2ecf20Sopenharmony_ci case 0x74 ... AC97_VENDOR_ID2: 6578c2ecf20Sopenharmony_ci return true; 6588c2ecf20Sopenharmony_ci default: 6598c2ecf20Sopenharmony_ci return false; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic bool wm9713_writeable_reg(struct device *dev, unsigned int reg) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci switch (reg) { 6668c2ecf20Sopenharmony_ci case AC97_VENDOR_ID1: 6678c2ecf20Sopenharmony_ci case AC97_VENDOR_ID2: 6688c2ecf20Sopenharmony_ci return false; 6698c2ecf20Sopenharmony_ci default: 6708c2ecf20Sopenharmony_ci return wm9713_readable_reg(dev, reg); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic const struct reg_default wm9713_reg_defaults[] = { 6758c2ecf20Sopenharmony_ci { 0x02, 0x8080 }, /* Speaker Output Volume */ 6768c2ecf20Sopenharmony_ci { 0x04, 0x8080 }, /* Headphone Output Volume */ 6778c2ecf20Sopenharmony_ci { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ 6788c2ecf20Sopenharmony_ci { 0x08, 0xc880 }, /* Mono Volume */ 6798c2ecf20Sopenharmony_ci { 0x0a, 0xe808 }, /* LINEIN Volume */ 6808c2ecf20Sopenharmony_ci { 0x0c, 0xe808 }, /* DAC PGA Volume */ 6818c2ecf20Sopenharmony_ci { 0x0e, 0x0808 }, /* MIC PGA Volume */ 6828c2ecf20Sopenharmony_ci { 0x10, 0x00da }, /* MIC Routing Control */ 6838c2ecf20Sopenharmony_ci { 0x12, 0x8000 }, /* Record PGA Volume */ 6848c2ecf20Sopenharmony_ci { 0x14, 0xd600 }, /* Record Routing */ 6858c2ecf20Sopenharmony_ci { 0x16, 0xaaa0 }, /* PCBEEP Volume */ 6868c2ecf20Sopenharmony_ci { 0x18, 0xaaa0 }, /* VxDAC Volume */ 6878c2ecf20Sopenharmony_ci { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ 6888c2ecf20Sopenharmony_ci { 0x1c, 0x0000 }, /* Output PGA Mux */ 6898c2ecf20Sopenharmony_ci { 0x1e, 0x0000 }, /* DAC 3D control */ 6908c2ecf20Sopenharmony_ci { 0x20, 0x0f0f }, /* DAC Tone Control*/ 6918c2ecf20Sopenharmony_ci { 0x22, 0x0040 }, /* MIC Input Select & Bias */ 6928c2ecf20Sopenharmony_ci { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ 6938c2ecf20Sopenharmony_ci { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ 6948c2ecf20Sopenharmony_ci { 0x28, 0x0405 }, /* Extended Audio ID */ 6958c2ecf20Sopenharmony_ci { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ 6968c2ecf20Sopenharmony_ci { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ 6978c2ecf20Sopenharmony_ci { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ 6988c2ecf20Sopenharmony_ci { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ 6998c2ecf20Sopenharmony_ci { 0x36, 0x4523 }, /* PCM codec control */ 7008c2ecf20Sopenharmony_ci { 0x3a, 0x2000 }, /* SPDIF control */ 7018c2ecf20Sopenharmony_ci { 0x3c, 0xfdff }, /* Powerdown 1 */ 7028c2ecf20Sopenharmony_ci { 0x3e, 0xffff }, /* Powerdown 2 */ 7038c2ecf20Sopenharmony_ci { 0x40, 0x0000 }, /* General Purpose */ 7048c2ecf20Sopenharmony_ci { 0x42, 0x0000 }, /* Fast Power-Up Control */ 7058c2ecf20Sopenharmony_ci { 0x44, 0x0080 }, /* MCLK/PLL Control */ 7068c2ecf20Sopenharmony_ci { 0x46, 0x0000 }, /* MCLK/PLL Control */ 7078c2ecf20Sopenharmony_ci { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ 7088c2ecf20Sopenharmony_ci { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ 7098c2ecf20Sopenharmony_ci { 0x50, 0x0000 }, /* GPIO Pin Sticky */ 7108c2ecf20Sopenharmony_ci { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ 7118c2ecf20Sopenharmony_ci /* GPIO Pin Status */ 7128c2ecf20Sopenharmony_ci { 0x56, 0xfffe }, /* GPIO Pin Sharing */ 7138c2ecf20Sopenharmony_ci { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ 7148c2ecf20Sopenharmony_ci { 0x5a, 0x0000 }, /* Additional Functions 1 */ 7158c2ecf20Sopenharmony_ci { 0x5c, 0x0000 }, /* Additional Functions 2 */ 7168c2ecf20Sopenharmony_ci { 0x60, 0xb032 }, /* ALC Control */ 7178c2ecf20Sopenharmony_ci { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ 7188c2ecf20Sopenharmony_ci { 0x64, 0x0000 }, /* AUXDAC input control */ 7198c2ecf20Sopenharmony_ci { 0x74, 0x0000 }, /* Digitiser Reg 1 */ 7208c2ecf20Sopenharmony_ci { 0x76, 0x0006 }, /* Digitiser Reg 2 */ 7218c2ecf20Sopenharmony_ci { 0x78, 0x0001 }, /* Digitiser Reg 3 */ 7228c2ecf20Sopenharmony_ci { 0x7a, 0x0000 }, /* Digitiser Read Back */ 7238c2ecf20Sopenharmony_ci}; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic const struct regmap_config wm9713_regmap_config = { 7268c2ecf20Sopenharmony_ci .reg_bits = 16, 7278c2ecf20Sopenharmony_ci .reg_stride = 2, 7288c2ecf20Sopenharmony_ci .val_bits = 16, 7298c2ecf20Sopenharmony_ci .max_register = 0x7e, 7308c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci .reg_defaults = wm9713_reg_defaults, 7338c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), 7348c2ecf20Sopenharmony_ci .volatile_reg = regmap_ac97_default_volatile, 7358c2ecf20Sopenharmony_ci .readable_reg = wm9713_readable_reg, 7368c2ecf20Sopenharmony_ci .writeable_reg = wm9713_writeable_reg, 7378c2ecf20Sopenharmony_ci}; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/* PLL divisors */ 7408c2ecf20Sopenharmony_cistruct _pll_div { 7418c2ecf20Sopenharmony_ci u32 divsel:1; 7428c2ecf20Sopenharmony_ci u32 divctl:1; 7438c2ecf20Sopenharmony_ci u32 lf:1; 7448c2ecf20Sopenharmony_ci u32 n:4; 7458c2ecf20Sopenharmony_ci u32 k:24; 7468c2ecf20Sopenharmony_ci}; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/* The size in bits of the PLL divide multiplied by 10 7498c2ecf20Sopenharmony_ci * to allow rounding later */ 7508c2ecf20Sopenharmony_ci#define FIXED_PLL_SIZE ((1 << 22) * 10) 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic void pll_factors(struct snd_soc_component *component, 7538c2ecf20Sopenharmony_ci struct _pll_div *pll_div, unsigned int source) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci u64 Kpart; 7568c2ecf20Sopenharmony_ci unsigned int K, Ndiv, Nmod, target; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* The PLL output is always 98.304MHz. */ 7598c2ecf20Sopenharmony_ci target = 98304000; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* If the input frequency is over 14.4MHz then scale it down. */ 7628c2ecf20Sopenharmony_ci if (source > 14400000) { 7638c2ecf20Sopenharmony_ci source >>= 1; 7648c2ecf20Sopenharmony_ci pll_div->divsel = 1; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (source > 14400000) { 7678c2ecf20Sopenharmony_ci source >>= 1; 7688c2ecf20Sopenharmony_ci pll_div->divctl = 1; 7698c2ecf20Sopenharmony_ci } else 7708c2ecf20Sopenharmony_ci pll_div->divctl = 0; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci } else { 7738c2ecf20Sopenharmony_ci pll_div->divsel = 0; 7748c2ecf20Sopenharmony_ci pll_div->divctl = 0; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* Low frequency sources require an additional divide in the 7788c2ecf20Sopenharmony_ci * loop. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_ci if (source < 8192000) { 7818c2ecf20Sopenharmony_ci pll_div->lf = 1; 7828c2ecf20Sopenharmony_ci target >>= 2; 7838c2ecf20Sopenharmony_ci } else 7848c2ecf20Sopenharmony_ci pll_div->lf = 0; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci Ndiv = target / source; 7878c2ecf20Sopenharmony_ci if ((Ndiv < 5) || (Ndiv > 12)) 7888c2ecf20Sopenharmony_ci dev_warn(component->dev, 7898c2ecf20Sopenharmony_ci "WM9713 PLL N value %u out of recommended range!\n", 7908c2ecf20Sopenharmony_ci Ndiv); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci pll_div->n = Ndiv; 7938c2ecf20Sopenharmony_ci Nmod = target % source; 7948c2ecf20Sopenharmony_ci Kpart = FIXED_PLL_SIZE * (long long)Nmod; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci do_div(Kpart, source); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci K = Kpart & 0xFFFFFFFF; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* Check if we need to round */ 8018c2ecf20Sopenharmony_ci if ((K % 10) >= 5) 8028c2ecf20Sopenharmony_ci K += 5; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Move down to proper range now rounding is done */ 8058c2ecf20Sopenharmony_ci K /= 10; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci pll_div->k = K; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci/* 8118c2ecf20Sopenharmony_ci * Please note that changing the PLL input frequency may require 8128c2ecf20Sopenharmony_ci * resynchronisation with the AC97 controller. 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_cistatic int wm9713_set_pll(struct snd_soc_component *component, 8158c2ecf20Sopenharmony_ci int pll_id, unsigned int freq_in, unsigned int freq_out) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); 8188c2ecf20Sopenharmony_ci u16 reg, reg2; 8198c2ecf20Sopenharmony_ci struct _pll_div pll_div; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* turn PLL off ? */ 8228c2ecf20Sopenharmony_ci if (freq_in == 0) { 8238c2ecf20Sopenharmony_ci /* disable PLL power and select ext source */ 8248c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0080, 0x0080); 8258c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x0200, 0x0200); 8268c2ecf20Sopenharmony_ci wm9713->pll_in = 0; 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci pll_factors(component, &pll_div, freq_in); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (pll_div.k == 0) { 8338c2ecf20Sopenharmony_ci reg = (pll_div.n << 12) | (pll_div.lf << 11) | 8348c2ecf20Sopenharmony_ci (pll_div.divsel << 9) | (pll_div.divctl << 8); 8358c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); 8368c2ecf20Sopenharmony_ci } else { 8378c2ecf20Sopenharmony_ci /* write the fractional k to the reg 0x46 pages */ 8388c2ecf20Sopenharmony_ci reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | 8398c2ecf20Sopenharmony_ci (pll_div.divsel << 9) | (pll_div.divctl << 8); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* K [21:20] */ 8428c2ecf20Sopenharmony_ci reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); 8438c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* K [19:16] */ 8468c2ecf20Sopenharmony_ci reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); 8478c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* K [15:12] */ 8508c2ecf20Sopenharmony_ci reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); 8518c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* K [11:8] */ 8548c2ecf20Sopenharmony_ci reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); 8558c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* K [7:4] */ 8588c2ecf20Sopenharmony_ci reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); 8598c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ 8628c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci /* turn PLL on and select as source */ 8668c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x0200, 0x0000); 8678c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0080, 0x0000); 8688c2ecf20Sopenharmony_ci wm9713->pll_in = freq_in; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* wait 10ms AC97 link frames for the link to stabilise */ 8718c2ecf20Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(10)); 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 8768c2ecf20Sopenharmony_ci int source, unsigned int freq_in, unsigned int freq_out) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 8798c2ecf20Sopenharmony_ci return wm9713_set_pll(component, pll_id, freq_in, freq_out); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci/* 8838c2ecf20Sopenharmony_ci * Tristate the PCM DAI lines, tristate can be disabled by calling 8848c2ecf20Sopenharmony_ci * wm9713_set_dai_fmt() 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_cistatic int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai, 8878c2ecf20Sopenharmony_ci int tristate) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (tristate) 8928c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, 8938c2ecf20Sopenharmony_ci 0x6000, 0x0000); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/* 8998c2ecf20Sopenharmony_ci * Configure WM9713 clock dividers. 9008c2ecf20Sopenharmony_ci * Voice DAC needs 256 FS 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_cistatic int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 9038c2ecf20Sopenharmony_ci int div_id, int div) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci switch (div_id) { 9088c2ecf20Sopenharmony_ci case WM9713_PCMCLK_DIV: 9098c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, div); 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case WM9713_CLKA_MULT: 9128c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0002, div); 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci case WM9713_CLKB_MULT: 9158c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0004, div); 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci case WM9713_HIFI_DIV: 9188c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x7000, div); 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci case WM9713_PCMBCLK_DIV: 9218c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, 0x0e00, div); 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci case WM9713_PCMCLK_PLL_DIV: 9248c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_LINE1_LEVEL, 9258c2ecf20Sopenharmony_ci 0x007f, div | 0x60); 9268c2ecf20Sopenharmony_ci break; 9278c2ecf20Sopenharmony_ci case WM9713_HIFI_PLL_DIV: 9288c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_LINE1_LEVEL, 9298c2ecf20Sopenharmony_ci 0x007f, div | 0x70); 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci default: 9328c2ecf20Sopenharmony_ci return -EINVAL; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci return 0; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai, 9398c2ecf20Sopenharmony_ci unsigned int fmt) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 9428c2ecf20Sopenharmony_ci u16 gpio = snd_soc_component_read(component, AC97_GPIO_CFG) & 0xffc5; 9438c2ecf20Sopenharmony_ci u16 reg = 0x8000; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* clock masters */ 9468c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 9478c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 9488c2ecf20Sopenharmony_ci reg |= 0x4000; 9498c2ecf20Sopenharmony_ci gpio |= 0x0010; 9508c2ecf20Sopenharmony_ci break; 9518c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 9528c2ecf20Sopenharmony_ci reg |= 0x6000; 9538c2ecf20Sopenharmony_ci gpio |= 0x0018; 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 9568c2ecf20Sopenharmony_ci reg |= 0x2000; 9578c2ecf20Sopenharmony_ci gpio |= 0x001a; 9588c2ecf20Sopenharmony_ci break; 9598c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 9608c2ecf20Sopenharmony_ci gpio |= 0x0012; 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* clock inversion */ 9658c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 9668c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 9678c2ecf20Sopenharmony_ci reg |= 0x00c0; 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 9708c2ecf20Sopenharmony_ci reg |= 0x0080; 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 9738c2ecf20Sopenharmony_ci reg |= 0x0040; 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* DAI format */ 9788c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 9798c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 9808c2ecf20Sopenharmony_ci reg |= 0x0002; 9818c2ecf20Sopenharmony_ci break; 9828c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 9858c2ecf20Sopenharmony_ci reg |= 0x0001; 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 9888c2ecf20Sopenharmony_ci reg |= 0x0003; 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 9918c2ecf20Sopenharmony_ci reg |= 0x0043; 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_GPIO_CFG, gpio); 9968c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_CENTER_LFE_MASTER, reg); 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, 10018c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 10028c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* enable PCM interface in master mode */ 10078c2ecf20Sopenharmony_ci switch (params_width(params)) { 10088c2ecf20Sopenharmony_ci case 16: 10098c2ecf20Sopenharmony_ci break; 10108c2ecf20Sopenharmony_ci case 20: 10118c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, 10128c2ecf20Sopenharmony_ci 0x000c, 0x0004); 10138c2ecf20Sopenharmony_ci break; 10148c2ecf20Sopenharmony_ci case 24: 10158c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, 10168c2ecf20Sopenharmony_ci 0x000c, 0x0008); 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci case 32: 10198c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, 10208c2ecf20Sopenharmony_ci 0x000c, 0x000c); 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci return 0; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic int ac97_hifi_prepare(struct snd_pcm_substream *substream, 10278c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 10308c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10318c2ecf20Sopenharmony_ci int reg; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x0001, 0x0001); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 10368c2ecf20Sopenharmony_ci reg = AC97_PCM_FRONT_DAC_RATE; 10378c2ecf20Sopenharmony_ci else 10388c2ecf20Sopenharmony_ci reg = AC97_PCM_LR_ADC_RATE; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return snd_soc_component_write(component, reg, runtime->rate); 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic int ac97_aux_prepare(struct snd_pcm_substream *substream, 10448c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 10478c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x0001, 0x0001); 10508c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_PCI_SID, 0x8000, 0x8000); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 10538c2ecf20Sopenharmony_ci return -ENODEV; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci return snd_soc_component_write(component, AC97_PCM_SURR_DAC_RATE, runtime->rate); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \ 10598c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_11025 | \ 10608c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_22050 | \ 10618c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_44100 | \ 10628c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000) 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci#define WM9713_PCM_RATES (SNDRV_PCM_RATE_8000 | \ 10658c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_11025 | \ 10668c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_16000 | \ 10678c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_22050 | \ 10688c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_44100 | \ 10698c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000) 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci#define WM9713_PCM_FORMATS \ 10728c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 10738c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops wm9713_dai_ops_hifi = { 10768c2ecf20Sopenharmony_ci .prepare = ac97_hifi_prepare, 10778c2ecf20Sopenharmony_ci .set_clkdiv = wm9713_set_dai_clkdiv, 10788c2ecf20Sopenharmony_ci .set_pll = wm9713_set_dai_pll, 10798c2ecf20Sopenharmony_ci}; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops wm9713_dai_ops_aux = { 10828c2ecf20Sopenharmony_ci .prepare = ac97_aux_prepare, 10838c2ecf20Sopenharmony_ci .set_clkdiv = wm9713_set_dai_clkdiv, 10848c2ecf20Sopenharmony_ci .set_pll = wm9713_set_dai_pll, 10858c2ecf20Sopenharmony_ci}; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops wm9713_dai_ops_voice = { 10888c2ecf20Sopenharmony_ci .hw_params = wm9713_pcm_hw_params, 10898c2ecf20Sopenharmony_ci .set_clkdiv = wm9713_set_dai_clkdiv, 10908c2ecf20Sopenharmony_ci .set_pll = wm9713_set_dai_pll, 10918c2ecf20Sopenharmony_ci .set_fmt = wm9713_set_dai_fmt, 10928c2ecf20Sopenharmony_ci .set_tristate = wm9713_set_dai_tristate, 10938c2ecf20Sopenharmony_ci}; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver wm9713_dai[] = { 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci .name = "wm9713-hifi", 10988c2ecf20Sopenharmony_ci .playback = { 10998c2ecf20Sopenharmony_ci .stream_name = "HiFi Playback", 11008c2ecf20Sopenharmony_ci .channels_min = 1, 11018c2ecf20Sopenharmony_ci .channels_max = 2, 11028c2ecf20Sopenharmony_ci .rates = WM9713_RATES, 11038c2ecf20Sopenharmony_ci .formats = SND_SOC_STD_AC97_FMTS,}, 11048c2ecf20Sopenharmony_ci .capture = { 11058c2ecf20Sopenharmony_ci .stream_name = "HiFi Capture", 11068c2ecf20Sopenharmony_ci .channels_min = 1, 11078c2ecf20Sopenharmony_ci .channels_max = 2, 11088c2ecf20Sopenharmony_ci .rates = WM9713_RATES, 11098c2ecf20Sopenharmony_ci .formats = SND_SOC_STD_AC97_FMTS,}, 11108c2ecf20Sopenharmony_ci .ops = &wm9713_dai_ops_hifi, 11118c2ecf20Sopenharmony_ci }, 11128c2ecf20Sopenharmony_ci { 11138c2ecf20Sopenharmony_ci .name = "wm9713-aux", 11148c2ecf20Sopenharmony_ci .playback = { 11158c2ecf20Sopenharmony_ci .stream_name = "Aux Playback", 11168c2ecf20Sopenharmony_ci .channels_min = 1, 11178c2ecf20Sopenharmony_ci .channels_max = 1, 11188c2ecf20Sopenharmony_ci .rates = WM9713_RATES, 11198c2ecf20Sopenharmony_ci .formats = SND_SOC_STD_AC97_FMTS,}, 11208c2ecf20Sopenharmony_ci .ops = &wm9713_dai_ops_aux, 11218c2ecf20Sopenharmony_ci }, 11228c2ecf20Sopenharmony_ci { 11238c2ecf20Sopenharmony_ci .name = "wm9713-voice", 11248c2ecf20Sopenharmony_ci .playback = { 11258c2ecf20Sopenharmony_ci .stream_name = "Voice Playback", 11268c2ecf20Sopenharmony_ci .channels_min = 1, 11278c2ecf20Sopenharmony_ci .channels_max = 1, 11288c2ecf20Sopenharmony_ci .rates = WM9713_PCM_RATES, 11298c2ecf20Sopenharmony_ci .formats = WM9713_PCM_FORMATS,}, 11308c2ecf20Sopenharmony_ci .capture = { 11318c2ecf20Sopenharmony_ci .stream_name = "Voice Capture", 11328c2ecf20Sopenharmony_ci .channels_min = 1, 11338c2ecf20Sopenharmony_ci .channels_max = 2, 11348c2ecf20Sopenharmony_ci .rates = WM9713_PCM_RATES, 11358c2ecf20Sopenharmony_ci .formats = WM9713_PCM_FORMATS,}, 11368c2ecf20Sopenharmony_ci .ops = &wm9713_dai_ops_voice, 11378c2ecf20Sopenharmony_ci .symmetric_rates = 1, 11388c2ecf20Sopenharmony_ci }, 11398c2ecf20Sopenharmony_ci}; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic int wm9713_set_bias_level(struct snd_soc_component *component, 11428c2ecf20Sopenharmony_ci enum snd_soc_bias_level level) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci switch (level) { 11458c2ecf20Sopenharmony_ci case SND_SOC_BIAS_ON: 11468c2ecf20Sopenharmony_ci /* enable thermal shutdown */ 11478c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0xe400, 0x0000); 11488c2ecf20Sopenharmony_ci break; 11498c2ecf20Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 11528c2ecf20Sopenharmony_ci /* enable master bias and vmid */ 11538c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0xc400, 0x0000); 11548c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_POWERDOWN, 0x0000); 11558c2ecf20Sopenharmony_ci break; 11568c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 11578c2ecf20Sopenharmony_ci /* disable everything including AC link */ 11588c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_EXTENDED_MID, 0xffff); 11598c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff); 11608c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_POWERDOWN, 0xffff); 11618c2ecf20Sopenharmony_ci break; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic int wm9713_soc_suspend(struct snd_soc_component *component) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci /* Disable everything except touchpanel - that will be handled 11698c2ecf20Sopenharmony_ci * by the touch driver and left disabled if touch is not in 11708c2ecf20Sopenharmony_ci * use. */ 11718c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x7fff, 11728c2ecf20Sopenharmony_ci 0x7fff); 11738c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff); 11748c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_POWERDOWN, 0x6f00); 11758c2ecf20Sopenharmony_ci snd_soc_component_write(component, AC97_POWERDOWN, 0xffff); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return 0; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic int wm9713_soc_resume(struct snd_soc_component *component) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); 11838c2ecf20Sopenharmony_ci int ret; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID, 11868c2ecf20Sopenharmony_ci WM9713_VENDOR_ID_MASK); 11878c2ecf20Sopenharmony_ci if (ret < 0) 11888c2ecf20Sopenharmony_ci return ret; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* do we need to re-start the PLL ? */ 11938c2ecf20Sopenharmony_ci if (wm9713->pll_in) 11948c2ecf20Sopenharmony_ci wm9713_set_pll(component, 0, wm9713->pll_in, 0); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* only synchronise the codec if warm reset failed */ 11978c2ecf20Sopenharmony_ci if (ret == 0) { 11988c2ecf20Sopenharmony_ci regcache_mark_dirty(component->regmap); 11998c2ecf20Sopenharmony_ci snd_soc_component_cache_sync(component); 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci return ret; 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic int wm9713_soc_probe(struct snd_soc_component *component) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); 12088c2ecf20Sopenharmony_ci struct regmap *regmap = NULL; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (wm9713->mfd_pdata) { 12118c2ecf20Sopenharmony_ci wm9713->ac97 = wm9713->mfd_pdata->ac97; 12128c2ecf20Sopenharmony_ci regmap = wm9713->mfd_pdata->regmap; 12138c2ecf20Sopenharmony_ci } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { 12148c2ecf20Sopenharmony_ci wm9713->ac97 = snd_soc_new_ac97_component(component, WM9713_VENDOR_ID, 12158c2ecf20Sopenharmony_ci WM9713_VENDOR_ID_MASK); 12168c2ecf20Sopenharmony_ci if (IS_ERR(wm9713->ac97)) 12178c2ecf20Sopenharmony_ci return PTR_ERR(wm9713->ac97); 12188c2ecf20Sopenharmony_ci regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); 12198c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 12208c2ecf20Sopenharmony_ci snd_soc_free_ac97_component(wm9713->ac97); 12218c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci } else { 12248c2ecf20Sopenharmony_ci return -ENXIO; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci snd_soc_component_init_regmap(component, regmap); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* unmute the adc - move to kcontrol */ 12308c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AC97_CD, 0x7fff, 0x0000); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci return 0; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic void wm9713_soc_remove(struct snd_soc_component *component) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9713->mfd_pdata) { 12408c2ecf20Sopenharmony_ci snd_soc_component_exit_regmap(component); 12418c2ecf20Sopenharmony_ci snd_soc_free_ac97_component(wm9713->ac97); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm9713 = { 12468c2ecf20Sopenharmony_ci .probe = wm9713_soc_probe, 12478c2ecf20Sopenharmony_ci .remove = wm9713_soc_remove, 12488c2ecf20Sopenharmony_ci .suspend = wm9713_soc_suspend, 12498c2ecf20Sopenharmony_ci .resume = wm9713_soc_resume, 12508c2ecf20Sopenharmony_ci .set_bias_level = wm9713_set_bias_level, 12518c2ecf20Sopenharmony_ci .controls = wm9713_snd_ac97_controls, 12528c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), 12538c2ecf20Sopenharmony_ci .dapm_widgets = wm9713_dapm_widgets, 12548c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), 12558c2ecf20Sopenharmony_ci .dapm_routes = wm9713_audio_map, 12568c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map), 12578c2ecf20Sopenharmony_ci .idle_bias_on = 1, 12588c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 12598c2ecf20Sopenharmony_ci .endianness = 1, 12608c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 12618c2ecf20Sopenharmony_ci}; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistatic int wm9713_probe(struct platform_device *pdev) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci struct wm9713_priv *wm9713; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL); 12688c2ecf20Sopenharmony_ci if (wm9713 == NULL) 12698c2ecf20Sopenharmony_ci return -ENOMEM; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci mutex_init(&wm9713->lock); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci wm9713->mfd_pdata = dev_get_platdata(&pdev->dev); 12748c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, wm9713); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(&pdev->dev, 12778c2ecf20Sopenharmony_ci &soc_component_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)); 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic struct platform_driver wm9713_codec_driver = { 12818c2ecf20Sopenharmony_ci .driver = { 12828c2ecf20Sopenharmony_ci .name = "wm9713-codec", 12838c2ecf20Sopenharmony_ci }, 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci .probe = wm9713_probe, 12868c2ecf20Sopenharmony_ci}; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_cimodule_platform_driver(wm9713_codec_driver); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM9713/WM9714 driver"); 12918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood"); 12928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1293