162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * wm8974.c -- WM8974 ALSA Soc Audio driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2006-2009 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Liam Girdwood <Liam.Girdwood@wolfsonmicro.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/pm.h> 1562306a36Sopenharmony_ci#include <linux/i2c.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <sound/core.h> 1962306a36Sopenharmony_ci#include <sound/pcm.h> 2062306a36Sopenharmony_ci#include <sound/pcm_params.h> 2162306a36Sopenharmony_ci#include <sound/soc.h> 2262306a36Sopenharmony_ci#include <sound/initval.h> 2362306a36Sopenharmony_ci#include <sound/tlv.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "wm8974.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct wm8974_priv { 2862306a36Sopenharmony_ci unsigned int mclk; 2962306a36Sopenharmony_ci unsigned int fs; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic const struct reg_default wm8974_reg_defaults[] = { 3362306a36Sopenharmony_ci { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 }, 3462306a36Sopenharmony_ci { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 }, 3562306a36Sopenharmony_ci { 8, 0x0000 }, { 9, 0x0000 }, { 10, 0x0000 }, { 11, 0x00ff }, 3662306a36Sopenharmony_ci { 12, 0x0000 }, { 13, 0x0000 }, { 14, 0x0100 }, { 15, 0x00ff }, 3762306a36Sopenharmony_ci { 16, 0x0000 }, { 17, 0x0000 }, { 18, 0x012c }, { 19, 0x002c }, 3862306a36Sopenharmony_ci { 20, 0x002c }, { 21, 0x002c }, { 22, 0x002c }, { 23, 0x0000 }, 3962306a36Sopenharmony_ci { 24, 0x0032 }, { 25, 0x0000 }, { 26, 0x0000 }, { 27, 0x0000 }, 4062306a36Sopenharmony_ci { 28, 0x0000 }, { 29, 0x0000 }, { 30, 0x0000 }, { 31, 0x0000 }, 4162306a36Sopenharmony_ci { 32, 0x0038 }, { 33, 0x000b }, { 34, 0x0032 }, { 35, 0x0000 }, 4262306a36Sopenharmony_ci { 36, 0x0008 }, { 37, 0x000c }, { 38, 0x0093 }, { 39, 0x00e9 }, 4362306a36Sopenharmony_ci { 40, 0x0000 }, { 41, 0x0000 }, { 42, 0x0000 }, { 43, 0x0000 }, 4462306a36Sopenharmony_ci { 44, 0x0003 }, { 45, 0x0010 }, { 46, 0x0000 }, { 47, 0x0000 }, 4562306a36Sopenharmony_ci { 48, 0x0000 }, { 49, 0x0002 }, { 50, 0x0000 }, { 51, 0x0000 }, 4662306a36Sopenharmony_ci { 52, 0x0000 }, { 53, 0x0000 }, { 54, 0x0039 }, { 55, 0x0000 }, 4762306a36Sopenharmony_ci { 56, 0x0000 }, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define WM8974_POWER1_BIASEN 0x08 5162306a36Sopenharmony_ci#define WM8974_POWER1_BUFIOEN 0x04 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define wm8974_reset(c) snd_soc_component_write(c, WM8974_RESET, 0) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" }; 5662306a36Sopenharmony_cistatic const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" }; 5762306a36Sopenharmony_cistatic const char *wm8974_eqmode[] = {"Capture", "Playback" }; 5862306a36Sopenharmony_cistatic const char *wm8974_bw[] = {"Narrow", "Wide" }; 5962306a36Sopenharmony_cistatic const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" }; 6062306a36Sopenharmony_cistatic const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" }; 6162306a36Sopenharmony_cistatic const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" }; 6262306a36Sopenharmony_cistatic const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; 6362306a36Sopenharmony_cistatic const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; 6462306a36Sopenharmony_cistatic const char *wm8974_alc[] = {"ALC", "Limiter" }; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct soc_enum wm8974_enum[] = { 6762306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */ 6862306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */ 6962306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp), 7062306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode), 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1), 7362306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw), 7462306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2), 7562306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw), 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3), 7862306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw), 7962306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4), 8062306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw), 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5), 8362306a36Sopenharmony_ci SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc), 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" }; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(wm8974_auxmode, 8962306a36Sopenharmony_ci WM8974_INPUT, 3, wm8974_auxmode_text); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); 9262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); 9362306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); 9462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8974_snd_controls[] = { 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciSOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0), 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciSOC_ENUM("DAC Companding", wm8974_enum[1]), 10162306a36Sopenharmony_ciSOC_ENUM("ADC Companding", wm8974_enum[0]), 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciSOC_ENUM("Playback De-emphasis", wm8974_enum[2]), 10462306a36Sopenharmony_ciSOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0), 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciSOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv), 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciSOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0), 10962306a36Sopenharmony_ciSOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0), 11062306a36Sopenharmony_ciSOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0), 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciSOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv), 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciSOC_ENUM("Equaliser Function", wm8974_enum[3]), 11562306a36Sopenharmony_ciSOC_ENUM("EQ1 Cut Off", wm8974_enum[4]), 11662306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv), 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciSOC_ENUM("Equaliser EQ2 Bandwidth", wm8974_enum[5]), 11962306a36Sopenharmony_ciSOC_ENUM("EQ2 Cut Off", wm8974_enum[6]), 12062306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv), 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciSOC_ENUM("Equaliser EQ3 Bandwidth", wm8974_enum[7]), 12362306a36Sopenharmony_ciSOC_ENUM("EQ3 Cut Off", wm8974_enum[8]), 12462306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv), 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciSOC_ENUM("Equaliser EQ4 Bandwidth", wm8974_enum[9]), 12762306a36Sopenharmony_ciSOC_ENUM("EQ4 Cut Off", wm8974_enum[10]), 12862306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv), 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciSOC_ENUM("Equaliser EQ5 Bandwidth", wm8974_enum[11]), 13162306a36Sopenharmony_ciSOC_ENUM("EQ5 Cut Off", wm8974_enum[12]), 13262306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv), 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciSOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0), 13562306a36Sopenharmony_ciSOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0), 13662306a36Sopenharmony_ciSOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0), 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciSOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0), 13962306a36Sopenharmony_ciSOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0), 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciSOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0), 14262306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0), 14362306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0), 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciSOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0), 14662306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0), 14762306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0), 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ciSOC_ENUM("ALC Capture Mode", wm8974_enum[13]), 15062306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0), 15162306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0), 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0), 15462306a36Sopenharmony_ciSOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0), 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciSOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0), 15762306a36Sopenharmony_ciSOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv), 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciSOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0), 16062306a36Sopenharmony_ciSOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1), 16162306a36Sopenharmony_ciSOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv), 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciSOC_ENUM("Aux Mode", wm8974_auxmode), 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciSOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0), 16662306a36Sopenharmony_ciSOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1), 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* DAC / ADC oversampling */ 16962306a36Sopenharmony_ciSOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0), 17062306a36Sopenharmony_ciSOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0), 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* Speaker Output Mixer */ 17462306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = { 17562306a36Sopenharmony_ciSOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0), 17662306a36Sopenharmony_ciSOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0), 17762306a36Sopenharmony_ciSOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0), 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* Mono Output Mixer */ 18162306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = { 18262306a36Sopenharmony_ciSOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0), 18362306a36Sopenharmony_ciSOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0), 18462306a36Sopenharmony_ciSOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0), 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* Boost mixer */ 18862306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8974_boost_mixer[] = { 18962306a36Sopenharmony_ciSOC_DAPM_SINGLE("PGA Switch", WM8974_INPPGA, 6, 1, 1), 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* Input PGA */ 19362306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8974_inpga[] = { 19462306a36Sopenharmony_ciSOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0), 19562306a36Sopenharmony_ciSOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0), 19662306a36Sopenharmony_ciSOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0), 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = { 20062306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0, 20162306a36Sopenharmony_ci &wm8974_speaker_mixer_controls[0], 20262306a36Sopenharmony_ci ARRAY_SIZE(wm8974_speaker_mixer_controls)), 20362306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0, 20462306a36Sopenharmony_ci &wm8974_mono_mixer_controls[0], 20562306a36Sopenharmony_ci ARRAY_SIZE(wm8974_mono_mixer_controls)), 20662306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0), 20762306a36Sopenharmony_ciSND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0), 20862306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0), 20962306a36Sopenharmony_ciSND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0), 21062306a36Sopenharmony_ciSND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0), 21162306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0), 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga, 21462306a36Sopenharmony_ci ARRAY_SIZE(wm8974_inpga)), 21562306a36Sopenharmony_ciSND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, 21662306a36Sopenharmony_ci wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)), 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0), 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("MICN"), 22162306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("MICP"), 22262306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("AUX"), 22362306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("MONOOUT"), 22462306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKOUTP"), 22562306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKOUTN"), 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm8974_dapm_routes[] = { 22962306a36Sopenharmony_ci /* Mono output mixer */ 23062306a36Sopenharmony_ci {"Mono Mixer", "PCM Playback Switch", "DAC"}, 23162306a36Sopenharmony_ci {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 23262306a36Sopenharmony_ci {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Speaker output mixer */ 23562306a36Sopenharmony_ci {"Speaker Mixer", "PCM Playback Switch", "DAC"}, 23662306a36Sopenharmony_ci {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, 23762306a36Sopenharmony_ci {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Outputs */ 24062306a36Sopenharmony_ci {"Mono Out", NULL, "Mono Mixer"}, 24162306a36Sopenharmony_ci {"MONOOUT", NULL, "Mono Out"}, 24262306a36Sopenharmony_ci {"SpkN Out", NULL, "Speaker Mixer"}, 24362306a36Sopenharmony_ci {"SpkP Out", NULL, "Speaker Mixer"}, 24462306a36Sopenharmony_ci {"SPKOUTN", NULL, "SpkN Out"}, 24562306a36Sopenharmony_ci {"SPKOUTP", NULL, "SpkP Out"}, 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Boost Mixer */ 24862306a36Sopenharmony_ci {"ADC", NULL, "Boost Mixer"}, 24962306a36Sopenharmony_ci {"Boost Mixer", NULL, "Aux Input"}, 25062306a36Sopenharmony_ci {"Boost Mixer", "PGA Switch", "Input PGA"}, 25162306a36Sopenharmony_ci {"Boost Mixer", NULL, "MICP"}, 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Input PGA */ 25462306a36Sopenharmony_ci {"Input PGA", "Aux Switch", "Aux Input"}, 25562306a36Sopenharmony_ci {"Input PGA", "MicN Switch", "MICN"}, 25662306a36Sopenharmony_ci {"Input PGA", "MicP Switch", "MICP"}, 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Inputs */ 25962306a36Sopenharmony_ci {"Aux Input", NULL, "AUX"}, 26062306a36Sopenharmony_ci}; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistruct pll_ { 26362306a36Sopenharmony_ci unsigned int pre_div:1; 26462306a36Sopenharmony_ci unsigned int n:4; 26562306a36Sopenharmony_ci unsigned int k; 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* The size in bits of the pll divide multiplied by 10 26962306a36Sopenharmony_ci * to allow rounding later */ 27062306a36Sopenharmony_ci#define FIXED_PLL_SIZE ((1 << 24) * 10) 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void pll_factors(struct pll_ *pll_div, 27362306a36Sopenharmony_ci unsigned int target, unsigned int source) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci unsigned long long Kpart; 27662306a36Sopenharmony_ci unsigned int K, Ndiv, Nmod; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* There is a fixed divide by 4 in the output path */ 27962306a36Sopenharmony_ci target *= 4; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci Ndiv = target / source; 28262306a36Sopenharmony_ci if (Ndiv < 6) { 28362306a36Sopenharmony_ci source /= 2; 28462306a36Sopenharmony_ci pll_div->pre_div = 1; 28562306a36Sopenharmony_ci Ndiv = target / source; 28662306a36Sopenharmony_ci } else 28762306a36Sopenharmony_ci pll_div->pre_div = 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if ((Ndiv < 6) || (Ndiv > 12)) 29062306a36Sopenharmony_ci printk(KERN_WARNING 29162306a36Sopenharmony_ci "WM8974 N value %u outwith recommended range!\n", 29262306a36Sopenharmony_ci Ndiv); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci pll_div->n = Ndiv; 29562306a36Sopenharmony_ci Nmod = target % source; 29662306a36Sopenharmony_ci Kpart = FIXED_PLL_SIZE * (long long)Nmod; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci do_div(Kpart, source); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci K = Kpart & 0xFFFFFFFF; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Check if we need to round */ 30362306a36Sopenharmony_ci if ((K % 10) >= 5) 30462306a36Sopenharmony_ci K += 5; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Move down to proper range now rounding is done */ 30762306a36Sopenharmony_ci K /= 10; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci pll_div->k = K; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 31362306a36Sopenharmony_ci int source, unsigned int freq_in, unsigned int freq_out) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 31662306a36Sopenharmony_ci struct pll_ pll_div; 31762306a36Sopenharmony_ci u16 reg; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (freq_in == 0 || freq_out == 0) { 32062306a36Sopenharmony_ci /* Clock CODEC directly from MCLK */ 32162306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8974_CLOCK); 32262306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_CLOCK, reg & 0x0ff); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Turn off PLL */ 32562306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8974_POWER1); 32662306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER1, reg & 0x1df); 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci pll_factors(&pll_div, freq_out, freq_in); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n); 33362306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_PLLK1, pll_div.k >> 18); 33462306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff); 33562306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_PLLK3, pll_div.k & 0x1ff); 33662306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8974_POWER1); 33762306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER1, reg | 0x020); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Run CODEC from PLL instead of MCLK */ 34062306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8974_CLOCK); 34162306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_CLOCK, reg | 0x100); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* 34762306a36Sopenharmony_ci * Configure WM8974 clock dividers. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_cistatic int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 35062306a36Sopenharmony_ci int div_id, int div) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 35362306a36Sopenharmony_ci u16 reg; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci switch (div_id) { 35662306a36Sopenharmony_ci case WM8974_OPCLKDIV: 35762306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8974_GPIO) & 0x1cf; 35862306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_GPIO, reg | div); 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case WM8974_MCLKDIV: 36162306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8974_CLOCK) & 0x11f; 36262306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_CLOCK, reg | div); 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci case WM8974_BCLKDIV: 36562306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8974_CLOCK) & 0x1e3; 36662306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_CLOCK, reg | div); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci default: 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out, 37662306a36Sopenharmony_ci int *mclkdiv) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci unsigned int ratio = 2 * f_in / f_out; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (ratio <= 2) { 38162306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_1; 38262306a36Sopenharmony_ci ratio = 2; 38362306a36Sopenharmony_ci } else if (ratio == 3) { 38462306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_1_5; 38562306a36Sopenharmony_ci } else if (ratio == 4) { 38662306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_2; 38762306a36Sopenharmony_ci } else if (ratio <= 6) { 38862306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_3; 38962306a36Sopenharmony_ci ratio = 6; 39062306a36Sopenharmony_ci } else if (ratio <= 8) { 39162306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_4; 39262306a36Sopenharmony_ci ratio = 8; 39362306a36Sopenharmony_ci } else if (ratio <= 12) { 39462306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_6; 39562306a36Sopenharmony_ci ratio = 12; 39662306a36Sopenharmony_ci } else if (ratio <= 16) { 39762306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_8; 39862306a36Sopenharmony_ci ratio = 16; 39962306a36Sopenharmony_ci } else { 40062306a36Sopenharmony_ci *mclkdiv = WM8974_MCLKDIV_12; 40162306a36Sopenharmony_ci ratio = 24; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return f_out * ratio / 2; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int wm8974_update_clocks(struct snd_soc_dai *dai) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 41062306a36Sopenharmony_ci struct wm8974_priv *priv = snd_soc_component_get_drvdata(component); 41162306a36Sopenharmony_ci unsigned int fs256; 41262306a36Sopenharmony_ci unsigned int fpll = 0; 41362306a36Sopenharmony_ci unsigned int f; 41462306a36Sopenharmony_ci int mclkdiv; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (!priv->mclk || !priv->fs) 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci fs256 = 256 * priv->fs; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (f != priv->mclk) { 42462306a36Sopenharmony_ci /* The PLL performs best around 90MHz */ 42562306a36Sopenharmony_ci fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll); 42962306a36Sopenharmony_ci wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 43562306a36Sopenharmony_ci unsigned int freq, int dir) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 43862306a36Sopenharmony_ci struct wm8974_priv *priv = snd_soc_component_get_drvdata(component); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (dir != SND_SOC_CLOCK_IN) 44162306a36Sopenharmony_ci return -EINVAL; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci priv->mclk = freq; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return wm8974_update_clocks(dai); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, 44962306a36Sopenharmony_ci unsigned int fmt) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 45262306a36Sopenharmony_ci u16 iface = 0; 45362306a36Sopenharmony_ci u16 clk = snd_soc_component_read(component, WM8974_CLOCK) & 0x1fe; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* set master/slave audio interface */ 45662306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 45762306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 45862306a36Sopenharmony_ci clk |= 0x0001; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci default: 46362306a36Sopenharmony_ci return -EINVAL; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* interface format */ 46762306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 46862306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 46962306a36Sopenharmony_ci iface |= 0x0010; 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 47462306a36Sopenharmony_ci iface |= 0x0008; 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 47762306a36Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_INV_MASK) == SND_SOC_DAIFMT_IB_IF || 47862306a36Sopenharmony_ci (fmt & SND_SOC_DAIFMT_INV_MASK) == SND_SOC_DAIFMT_NB_IF) { 47962306a36Sopenharmony_ci return -EINVAL; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci iface |= 0x00018; 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci default: 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* clock inversion */ 48862306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 48962306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 49262306a36Sopenharmony_ci iface |= 0x0180; 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 49562306a36Sopenharmony_ci iface |= 0x0100; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 49862306a36Sopenharmony_ci iface |= 0x0080; 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci default: 50162306a36Sopenharmony_ci return -EINVAL; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_IFACE, iface); 50562306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_CLOCK, clk); 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, 51062306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 51162306a36Sopenharmony_ci struct snd_soc_dai *dai) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 51462306a36Sopenharmony_ci struct wm8974_priv *priv = snd_soc_component_get_drvdata(component); 51562306a36Sopenharmony_ci u16 iface = snd_soc_component_read(component, WM8974_IFACE) & 0x19f; 51662306a36Sopenharmony_ci u16 adn = snd_soc_component_read(component, WM8974_ADD) & 0x1f1; 51762306a36Sopenharmony_ci int err; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci priv->fs = params_rate(params); 52062306a36Sopenharmony_ci err = wm8974_update_clocks(dai); 52162306a36Sopenharmony_ci if (err) 52262306a36Sopenharmony_ci return err; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* bit size */ 52562306a36Sopenharmony_ci switch (params_width(params)) { 52662306a36Sopenharmony_ci case 16: 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci case 20: 52962306a36Sopenharmony_ci iface |= 0x0020; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci case 24: 53262306a36Sopenharmony_ci iface |= 0x0040; 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci case 32: 53562306a36Sopenharmony_ci iface |= 0x0060; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* filter coefficient */ 54062306a36Sopenharmony_ci switch (params_rate(params)) { 54162306a36Sopenharmony_ci case 8000: 54262306a36Sopenharmony_ci adn |= 0x5 << 1; 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci case 11025: 54562306a36Sopenharmony_ci adn |= 0x4 << 1; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case 16000: 54862306a36Sopenharmony_ci adn |= 0x3 << 1; 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci case 22050: 55162306a36Sopenharmony_ci adn |= 0x2 << 1; 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case 32000: 55462306a36Sopenharmony_ci adn |= 0x1 << 1; 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci case 44100: 55762306a36Sopenharmony_ci case 48000: 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_IFACE, iface); 56262306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_ADD, adn); 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int wm8974_mute(struct snd_soc_dai *dai, int mute, int direction) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 56962306a36Sopenharmony_ci u16 mute_reg = snd_soc_component_read(component, WM8974_DAC) & 0xffbf; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (mute) 57262306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_DAC, mute_reg | 0x40); 57362306a36Sopenharmony_ci else 57462306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_DAC, mute_reg); 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* liam need to make this lower power with dapm */ 57962306a36Sopenharmony_cistatic int wm8974_set_bias_level(struct snd_soc_component *component, 58062306a36Sopenharmony_ci enum snd_soc_bias_level level) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci u16 power1 = snd_soc_component_read(component, WM8974_POWER1) & ~0x3; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci switch (level) { 58562306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 58662306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 58762306a36Sopenharmony_ci power1 |= 0x1; /* VMID 50k */ 58862306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER1, power1); 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 59262306a36Sopenharmony_ci power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 59562306a36Sopenharmony_ci regcache_sync(dev_get_regmap(component->dev, NULL)); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Initial cap charge at VMID 5k */ 59862306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER1, power1 | 0x3); 59962306a36Sopenharmony_ci mdelay(100); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci power1 |= 0x2; /* VMID 500k */ 60362306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER1, power1); 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 60762306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER1, 0); 60862306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER2, 0); 60962306a36Sopenharmony_ci snd_soc_component_write(component, WM8974_POWER3, 0); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000) 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 61962306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic const struct snd_soc_dai_ops wm8974_ops = { 62262306a36Sopenharmony_ci .hw_params = wm8974_pcm_hw_params, 62362306a36Sopenharmony_ci .mute_stream = wm8974_mute, 62462306a36Sopenharmony_ci .set_fmt = wm8974_set_dai_fmt, 62562306a36Sopenharmony_ci .set_clkdiv = wm8974_set_dai_clkdiv, 62662306a36Sopenharmony_ci .set_pll = wm8974_set_dai_pll, 62762306a36Sopenharmony_ci .set_sysclk = wm8974_set_dai_sysclk, 62862306a36Sopenharmony_ci .no_capture_mute = 1, 62962306a36Sopenharmony_ci}; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic struct snd_soc_dai_driver wm8974_dai = { 63262306a36Sopenharmony_ci .name = "wm8974-hifi", 63362306a36Sopenharmony_ci .playback = { 63462306a36Sopenharmony_ci .stream_name = "Playback", 63562306a36Sopenharmony_ci .channels_min = 1, 63662306a36Sopenharmony_ci .channels_max = 2, /* Only 1 channel of data */ 63762306a36Sopenharmony_ci .rates = WM8974_RATES, 63862306a36Sopenharmony_ci .formats = WM8974_FORMATS,}, 63962306a36Sopenharmony_ci .capture = { 64062306a36Sopenharmony_ci .stream_name = "Capture", 64162306a36Sopenharmony_ci .channels_min = 1, 64262306a36Sopenharmony_ci .channels_max = 2, /* Only 1 channel of data */ 64362306a36Sopenharmony_ci .rates = WM8974_RATES, 64462306a36Sopenharmony_ci .formats = WM8974_FORMATS,}, 64562306a36Sopenharmony_ci .ops = &wm8974_ops, 64662306a36Sopenharmony_ci .symmetric_rate = 1, 64762306a36Sopenharmony_ci}; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic const struct regmap_config wm8974_regmap = { 65062306a36Sopenharmony_ci .reg_bits = 7, 65162306a36Sopenharmony_ci .val_bits = 9, 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci .max_register = WM8974_MONOMIX, 65462306a36Sopenharmony_ci .reg_defaults = wm8974_reg_defaults, 65562306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults), 65662306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 65762306a36Sopenharmony_ci}; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int wm8974_probe(struct snd_soc_component *component) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci int ret = 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ret = wm8974_reset(component); 66462306a36Sopenharmony_ci if (ret < 0) { 66562306a36Sopenharmony_ci dev_err(component->dev, "Failed to issue reset\n"); 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm8974 = { 67362306a36Sopenharmony_ci .probe = wm8974_probe, 67462306a36Sopenharmony_ci .set_bias_level = wm8974_set_bias_level, 67562306a36Sopenharmony_ci .controls = wm8974_snd_controls, 67662306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(wm8974_snd_controls), 67762306a36Sopenharmony_ci .dapm_widgets = wm8974_dapm_widgets, 67862306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets), 67962306a36Sopenharmony_ci .dapm_routes = wm8974_dapm_routes, 68062306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes), 68162306a36Sopenharmony_ci .suspend_bias_off = 1, 68262306a36Sopenharmony_ci .idle_bias_on = 1, 68362306a36Sopenharmony_ci .use_pmdown_time = 1, 68462306a36Sopenharmony_ci .endianness = 1, 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic int wm8974_i2c_probe(struct i2c_client *i2c) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct wm8974_priv *priv; 69062306a36Sopenharmony_ci struct regmap *regmap; 69162306a36Sopenharmony_ci int ret; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); 69462306a36Sopenharmony_ci if (!priv) 69562306a36Sopenharmony_ci return -ENOMEM; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci i2c_set_clientdata(i2c, priv); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap); 70062306a36Sopenharmony_ci if (IS_ERR(regmap)) 70162306a36Sopenharmony_ci return PTR_ERR(regmap); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&i2c->dev, 70462306a36Sopenharmony_ci &soc_component_dev_wm8974, &wm8974_dai, 1); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return ret; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic const struct i2c_device_id wm8974_i2c_id[] = { 71062306a36Sopenharmony_ci { "wm8974", 0 }, 71162306a36Sopenharmony_ci { } 71262306a36Sopenharmony_ci}; 71362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic const struct of_device_id wm8974_of_match[] = { 71662306a36Sopenharmony_ci { .compatible = "wlf,wm8974", }, 71762306a36Sopenharmony_ci { } 71862306a36Sopenharmony_ci}; 71962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wm8974_of_match); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic struct i2c_driver wm8974_i2c_driver = { 72262306a36Sopenharmony_ci .driver = { 72362306a36Sopenharmony_ci .name = "wm8974", 72462306a36Sopenharmony_ci .of_match_table = wm8974_of_match, 72562306a36Sopenharmony_ci }, 72662306a36Sopenharmony_ci .probe = wm8974_i2c_probe, 72762306a36Sopenharmony_ci .id_table = wm8974_i2c_id, 72862306a36Sopenharmony_ci}; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cimodule_i2c_driver(wm8974_i2c_driver); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM8974 driver"); 73362306a36Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood"); 73462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 735