162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * wm8770.c -- WM8770 ALSA SoC Audio driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2010 Wolfson Microelectronics plc 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/of_device.h> 1562306a36Sopenharmony_ci#include <linux/pm.h> 1662306a36Sopenharmony_ci#include <linux/spi/spi.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <sound/core.h> 2162306a36Sopenharmony_ci#include <sound/pcm.h> 2262306a36Sopenharmony_ci#include <sound/pcm_params.h> 2362306a36Sopenharmony_ci#include <sound/soc.h> 2462306a36Sopenharmony_ci#include <sound/initval.h> 2562306a36Sopenharmony_ci#include <sound/tlv.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "wm8770.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define WM8770_NUM_SUPPLIES 3 3062306a36Sopenharmony_cistatic const char *wm8770_supply_names[WM8770_NUM_SUPPLIES] = { 3162306a36Sopenharmony_ci "AVDD1", 3262306a36Sopenharmony_ci "AVDD2", 3362306a36Sopenharmony_ci "DVDD" 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const struct reg_default wm8770_reg_defaults[] = { 3762306a36Sopenharmony_ci { 0, 0x7f }, 3862306a36Sopenharmony_ci { 1, 0x7f }, 3962306a36Sopenharmony_ci { 2, 0x7f }, 4062306a36Sopenharmony_ci { 3, 0x7f }, 4162306a36Sopenharmony_ci { 4, 0x7f }, 4262306a36Sopenharmony_ci { 5, 0x7f }, 4362306a36Sopenharmony_ci { 6, 0x7f }, 4462306a36Sopenharmony_ci { 7, 0x7f }, 4562306a36Sopenharmony_ci { 8, 0x7f }, 4662306a36Sopenharmony_ci { 9, 0xff }, 4762306a36Sopenharmony_ci { 10, 0xff }, 4862306a36Sopenharmony_ci { 11, 0xff }, 4962306a36Sopenharmony_ci { 12, 0xff }, 5062306a36Sopenharmony_ci { 13, 0xff }, 5162306a36Sopenharmony_ci { 14, 0xff }, 5262306a36Sopenharmony_ci { 15, 0xff }, 5362306a36Sopenharmony_ci { 16, 0xff }, 5462306a36Sopenharmony_ci { 17, 0xff }, 5562306a36Sopenharmony_ci { 18, 0 }, 5662306a36Sopenharmony_ci { 19, 0x90 }, 5762306a36Sopenharmony_ci { 20, 0 }, 5862306a36Sopenharmony_ci { 21, 0 }, 5962306a36Sopenharmony_ci { 22, 0x22 }, 6062306a36Sopenharmony_ci { 23, 0x22 }, 6162306a36Sopenharmony_ci { 24, 0x3e }, 6262306a36Sopenharmony_ci { 25, 0xc }, 6362306a36Sopenharmony_ci { 26, 0xc }, 6462306a36Sopenharmony_ci { 27, 0x100 }, 6562306a36Sopenharmony_ci { 28, 0x189 }, 6662306a36Sopenharmony_ci { 29, 0x189 }, 6762306a36Sopenharmony_ci { 30, 0x8770 }, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic bool wm8770_volatile_reg(struct device *dev, unsigned int reg) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci switch (reg) { 7362306a36Sopenharmony_ci case WM8770_RESET: 7462306a36Sopenharmony_ci return true; 7562306a36Sopenharmony_ci default: 7662306a36Sopenharmony_ci return false; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct wm8770_priv { 8162306a36Sopenharmony_ci struct regmap *regmap; 8262306a36Sopenharmony_ci struct regulator_bulk_data supplies[WM8770_NUM_SUPPLIES]; 8362306a36Sopenharmony_ci struct notifier_block disable_nb[WM8770_NUM_SUPPLIES]; 8462306a36Sopenharmony_ci struct snd_soc_component *component; 8562306a36Sopenharmony_ci int sysclk; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int vout12supply_event(struct snd_soc_dapm_widget *w, 8962306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event); 9062306a36Sopenharmony_cistatic int vout34supply_event(struct snd_soc_dapm_widget *w, 9162306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * We can't use the same notifier block for more than one supply and 9562306a36Sopenharmony_ci * there's no way I can see to get from a callback to the caller 9662306a36Sopenharmony_ci * except container_of(). 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci#define WM8770_REGULATOR_EVENT(n) \ 9962306a36Sopenharmony_cistatic int wm8770_regulator_event_##n(struct notifier_block *nb, \ 10062306a36Sopenharmony_ci unsigned long event, void *data) \ 10162306a36Sopenharmony_ci{ \ 10262306a36Sopenharmony_ci struct wm8770_priv *wm8770 = container_of(nb, struct wm8770_priv, \ 10362306a36Sopenharmony_ci disable_nb[n]); \ 10462306a36Sopenharmony_ci if (event & REGULATOR_EVENT_DISABLE) { \ 10562306a36Sopenharmony_ci regcache_mark_dirty(wm8770->regmap); \ 10662306a36Sopenharmony_ci } \ 10762306a36Sopenharmony_ci return 0; \ 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciWM8770_REGULATOR_EVENT(0) 11162306a36Sopenharmony_ciWM8770_REGULATOR_EVENT(1) 11262306a36Sopenharmony_ciWM8770_REGULATOR_EVENT(2) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0); 11562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(dac_dig_tlv, -12750, 50, 1); 11662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(dac_alg_tlv, -12700, 100, 1); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const char *dac_phase_text[][2] = { 11962306a36Sopenharmony_ci { "DAC1 Normal", "DAC1 Inverted" }, 12062306a36Sopenharmony_ci { "DAC2 Normal", "DAC2 Inverted" }, 12162306a36Sopenharmony_ci { "DAC3 Normal", "DAC3 Inverted" }, 12262306a36Sopenharmony_ci { "DAC4 Normal", "DAC4 Inverted" }, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic const struct soc_enum dac_phase[] = { 12662306a36Sopenharmony_ci SOC_ENUM_DOUBLE(WM8770_DACPHASE, 0, 1, 2, dac_phase_text[0]), 12762306a36Sopenharmony_ci SOC_ENUM_DOUBLE(WM8770_DACPHASE, 2, 3, 2, dac_phase_text[1]), 12862306a36Sopenharmony_ci SOC_ENUM_DOUBLE(WM8770_DACPHASE, 4, 5, 2, dac_phase_text[2]), 12962306a36Sopenharmony_ci SOC_ENUM_DOUBLE(WM8770_DACPHASE, 6, 7, 2, dac_phase_text[3]), 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8770_snd_controls[] = { 13362306a36Sopenharmony_ci /* global DAC playback controls */ 13462306a36Sopenharmony_ci SOC_SINGLE_TLV("DAC Playback Volume", WM8770_MSDIGVOL, 0, 255, 0, 13562306a36Sopenharmony_ci dac_dig_tlv), 13662306a36Sopenharmony_ci SOC_SINGLE("DAC Playback Switch", WM8770_DACMUTE, 4, 1, 1), 13762306a36Sopenharmony_ci SOC_SINGLE("DAC Playback ZC Switch", WM8770_DACCTRL1, 0, 1, 0), 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* global VOUT playback controls */ 14062306a36Sopenharmony_ci SOC_SINGLE_TLV("VOUT Playback Volume", WM8770_MSALGVOL, 0, 127, 0, 14162306a36Sopenharmony_ci dac_alg_tlv), 14262306a36Sopenharmony_ci SOC_SINGLE("VOUT Playback ZC Switch", WM8770_MSALGVOL, 7, 1, 0), 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* VOUT1/2/3/4 specific controls */ 14562306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("VOUT1 Playback Volume", WM8770_VOUT1LVOL, 14662306a36Sopenharmony_ci WM8770_VOUT1RVOL, 0, 127, 0, dac_alg_tlv), 14762306a36Sopenharmony_ci SOC_DOUBLE_R("VOUT1 Playback ZC Switch", WM8770_VOUT1LVOL, 14862306a36Sopenharmony_ci WM8770_VOUT1RVOL, 7, 1, 0), 14962306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("VOUT2 Playback Volume", WM8770_VOUT2LVOL, 15062306a36Sopenharmony_ci WM8770_VOUT2RVOL, 0, 127, 0, dac_alg_tlv), 15162306a36Sopenharmony_ci SOC_DOUBLE_R("VOUT2 Playback ZC Switch", WM8770_VOUT2LVOL, 15262306a36Sopenharmony_ci WM8770_VOUT2RVOL, 7, 1, 0), 15362306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("VOUT3 Playback Volume", WM8770_VOUT3LVOL, 15462306a36Sopenharmony_ci WM8770_VOUT3RVOL, 0, 127, 0, dac_alg_tlv), 15562306a36Sopenharmony_ci SOC_DOUBLE_R("VOUT3 Playback ZC Switch", WM8770_VOUT3LVOL, 15662306a36Sopenharmony_ci WM8770_VOUT3RVOL, 7, 1, 0), 15762306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("VOUT4 Playback Volume", WM8770_VOUT4LVOL, 15862306a36Sopenharmony_ci WM8770_VOUT4RVOL, 0, 127, 0, dac_alg_tlv), 15962306a36Sopenharmony_ci SOC_DOUBLE_R("VOUT4 Playback ZC Switch", WM8770_VOUT4LVOL, 16062306a36Sopenharmony_ci WM8770_VOUT4RVOL, 7, 1, 0), 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* DAC1/2/3/4 specific controls */ 16362306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("DAC1 Playback Volume", WM8770_DAC1LVOL, 16462306a36Sopenharmony_ci WM8770_DAC1RVOL, 0, 255, 0, dac_dig_tlv), 16562306a36Sopenharmony_ci SOC_SINGLE("DAC1 Deemphasis Switch", WM8770_DACCTRL2, 0, 1, 0), 16662306a36Sopenharmony_ci SOC_ENUM("DAC1 Phase", dac_phase[0]), 16762306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("DAC2 Playback Volume", WM8770_DAC2LVOL, 16862306a36Sopenharmony_ci WM8770_DAC2RVOL, 0, 255, 0, dac_dig_tlv), 16962306a36Sopenharmony_ci SOC_SINGLE("DAC2 Deemphasis Switch", WM8770_DACCTRL2, 1, 1, 0), 17062306a36Sopenharmony_ci SOC_ENUM("DAC2 Phase", dac_phase[1]), 17162306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("DAC3 Playback Volume", WM8770_DAC3LVOL, 17262306a36Sopenharmony_ci WM8770_DAC3RVOL, 0, 255, 0, dac_dig_tlv), 17362306a36Sopenharmony_ci SOC_SINGLE("DAC3 Deemphasis Switch", WM8770_DACCTRL2, 2, 1, 0), 17462306a36Sopenharmony_ci SOC_ENUM("DAC3 Phase", dac_phase[2]), 17562306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("DAC4 Playback Volume", WM8770_DAC4LVOL, 17662306a36Sopenharmony_ci WM8770_DAC4RVOL, 0, 255, 0, dac_dig_tlv), 17762306a36Sopenharmony_ci SOC_SINGLE("DAC4 Deemphasis Switch", WM8770_DACCTRL2, 3, 1, 0), 17862306a36Sopenharmony_ci SOC_ENUM("DAC4 Phase", dac_phase[3]), 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* ADC specific controls */ 18162306a36Sopenharmony_ci SOC_DOUBLE_R_TLV("Capture Volume", WM8770_ADCLCTRL, WM8770_ADCRCTRL, 18262306a36Sopenharmony_ci 0, 31, 0, adc_tlv), 18362306a36Sopenharmony_ci SOC_DOUBLE_R("Capture Switch", WM8770_ADCLCTRL, WM8770_ADCRCTRL, 18462306a36Sopenharmony_ci 5, 1, 1), 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* other controls */ 18762306a36Sopenharmony_ci SOC_SINGLE("ADC 128x Oversampling Switch", WM8770_MSTRCTRL, 3, 1, 0), 18862306a36Sopenharmony_ci SOC_SINGLE("ADC Highpass Filter Switch", WM8770_IFACECTRL, 8, 1, 1) 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic const char *ain_text[] = { 19262306a36Sopenharmony_ci "AIN1", "AIN2", "AIN3", "AIN4", 19362306a36Sopenharmony_ci "AIN5", "AIN6", "AIN7", "AIN8" 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic SOC_ENUM_DOUBLE_DECL(ain_enum, 19762306a36Sopenharmony_ci WM8770_ADCMUX, 0, 4, ain_text); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct snd_kcontrol_new ain_mux = 20062306a36Sopenharmony_ci SOC_DAPM_ENUM("Capture Mux", ain_enum); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const struct snd_kcontrol_new vout1_mix_controls[] = { 20362306a36Sopenharmony_ci SOC_DAPM_SINGLE("DAC1 Switch", WM8770_OUTMUX1, 0, 1, 0), 20462306a36Sopenharmony_ci SOC_DAPM_SINGLE("AUX1 Switch", WM8770_OUTMUX1, 1, 1, 0), 20562306a36Sopenharmony_ci SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 2, 1, 0) 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic const struct snd_kcontrol_new vout2_mix_controls[] = { 20962306a36Sopenharmony_ci SOC_DAPM_SINGLE("DAC2 Switch", WM8770_OUTMUX1, 3, 1, 0), 21062306a36Sopenharmony_ci SOC_DAPM_SINGLE("AUX2 Switch", WM8770_OUTMUX1, 4, 1, 0), 21162306a36Sopenharmony_ci SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 5, 1, 0) 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic const struct snd_kcontrol_new vout3_mix_controls[] = { 21562306a36Sopenharmony_ci SOC_DAPM_SINGLE("DAC3 Switch", WM8770_OUTMUX2, 0, 1, 0), 21662306a36Sopenharmony_ci SOC_DAPM_SINGLE("AUX3 Switch", WM8770_OUTMUX2, 1, 1, 0), 21762306a36Sopenharmony_ci SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 2, 1, 0) 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic const struct snd_kcontrol_new vout4_mix_controls[] = { 22162306a36Sopenharmony_ci SOC_DAPM_SINGLE("DAC4 Switch", WM8770_OUTMUX2, 3, 1, 0), 22262306a36Sopenharmony_ci SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 4, 1, 0) 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8770_dapm_widgets[] = { 22662306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AUX1"), 22762306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AUX2"), 22862306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AUX3"), 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN1"), 23162306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN2"), 23262306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN3"), 23362306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN4"), 23462306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN5"), 23562306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN6"), 23662306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN7"), 23762306a36Sopenharmony_ci SND_SOC_DAPM_INPUT("AIN8"), 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci SND_SOC_DAPM_MUX("Capture Mux", WM8770_ADCMUX, 8, 1, &ain_mux), 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci SND_SOC_DAPM_ADC("ADC", "Capture", WM8770_PWDNCTRL, 1, 1), 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci SND_SOC_DAPM_DAC("DAC1", "Playback", WM8770_PWDNCTRL, 2, 1), 24462306a36Sopenharmony_ci SND_SOC_DAPM_DAC("DAC2", "Playback", WM8770_PWDNCTRL, 3, 1), 24562306a36Sopenharmony_ci SND_SOC_DAPM_DAC("DAC3", "Playback", WM8770_PWDNCTRL, 4, 1), 24662306a36Sopenharmony_ci SND_SOC_DAPM_DAC("DAC4", "Playback", WM8770_PWDNCTRL, 5, 1), 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("VOUT12 Supply", SND_SOC_NOPM, 0, 0, 24962306a36Sopenharmony_ci vout12supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 25062306a36Sopenharmony_ci SND_SOC_DAPM_SUPPLY("VOUT34 Supply", SND_SOC_NOPM, 0, 0, 25162306a36Sopenharmony_ci vout34supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci SND_SOC_DAPM_MIXER("VOUT1 Mixer", SND_SOC_NOPM, 0, 0, 25462306a36Sopenharmony_ci vout1_mix_controls, ARRAY_SIZE(vout1_mix_controls)), 25562306a36Sopenharmony_ci SND_SOC_DAPM_MIXER("VOUT2 Mixer", SND_SOC_NOPM, 0, 0, 25662306a36Sopenharmony_ci vout2_mix_controls, ARRAY_SIZE(vout2_mix_controls)), 25762306a36Sopenharmony_ci SND_SOC_DAPM_MIXER("VOUT3 Mixer", SND_SOC_NOPM, 0, 0, 25862306a36Sopenharmony_ci vout3_mix_controls, ARRAY_SIZE(vout3_mix_controls)), 25962306a36Sopenharmony_ci SND_SOC_DAPM_MIXER("VOUT4 Mixer", SND_SOC_NOPM, 0, 0, 26062306a36Sopenharmony_ci vout4_mix_controls, ARRAY_SIZE(vout4_mix_controls)), 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("VOUT1"), 26362306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("VOUT2"), 26462306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("VOUT3"), 26562306a36Sopenharmony_ci SND_SOC_DAPM_OUTPUT("VOUT4") 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm8770_intercon[] = { 26962306a36Sopenharmony_ci { "Capture Mux", "AIN1", "AIN1" }, 27062306a36Sopenharmony_ci { "Capture Mux", "AIN2", "AIN2" }, 27162306a36Sopenharmony_ci { "Capture Mux", "AIN3", "AIN3" }, 27262306a36Sopenharmony_ci { "Capture Mux", "AIN4", "AIN4" }, 27362306a36Sopenharmony_ci { "Capture Mux", "AIN5", "AIN5" }, 27462306a36Sopenharmony_ci { "Capture Mux", "AIN6", "AIN6" }, 27562306a36Sopenharmony_ci { "Capture Mux", "AIN7", "AIN7" }, 27662306a36Sopenharmony_ci { "Capture Mux", "AIN8", "AIN8" }, 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci { "ADC", NULL, "Capture Mux" }, 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci { "VOUT1 Mixer", NULL, "VOUT12 Supply" }, 28162306a36Sopenharmony_ci { "VOUT1 Mixer", "DAC1 Switch", "DAC1" }, 28262306a36Sopenharmony_ci { "VOUT1 Mixer", "AUX1 Switch", "AUX1" }, 28362306a36Sopenharmony_ci { "VOUT1 Mixer", "Bypass Switch", "Capture Mux" }, 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci { "VOUT2 Mixer", NULL, "VOUT12 Supply" }, 28662306a36Sopenharmony_ci { "VOUT2 Mixer", "DAC2 Switch", "DAC2" }, 28762306a36Sopenharmony_ci { "VOUT2 Mixer", "AUX2 Switch", "AUX2" }, 28862306a36Sopenharmony_ci { "VOUT2 Mixer", "Bypass Switch", "Capture Mux" }, 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci { "VOUT3 Mixer", NULL, "VOUT34 Supply" }, 29162306a36Sopenharmony_ci { "VOUT3 Mixer", "DAC3 Switch", "DAC3" }, 29262306a36Sopenharmony_ci { "VOUT3 Mixer", "AUX3 Switch", "AUX3" }, 29362306a36Sopenharmony_ci { "VOUT3 Mixer", "Bypass Switch", "Capture Mux" }, 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci { "VOUT4 Mixer", NULL, "VOUT34 Supply" }, 29662306a36Sopenharmony_ci { "VOUT4 Mixer", "DAC4 Switch", "DAC4" }, 29762306a36Sopenharmony_ci { "VOUT4 Mixer", "Bypass Switch", "Capture Mux" }, 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci { "VOUT1", NULL, "VOUT1 Mixer" }, 30062306a36Sopenharmony_ci { "VOUT2", NULL, "VOUT2 Mixer" }, 30162306a36Sopenharmony_ci { "VOUT3", NULL, "VOUT3 Mixer" }, 30262306a36Sopenharmony_ci { "VOUT4", NULL, "VOUT4 Mixer" } 30362306a36Sopenharmony_ci}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int vout12supply_event(struct snd_soc_dapm_widget *w, 30662306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci switch (event) { 31162306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 31262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 31562306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0x180); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int vout34supply_event(struct snd_soc_dapm_widget *w, 32362306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci switch (event) { 32862306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 32962306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0); 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 33262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0x180); 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int wm8770_reset(struct snd_soc_component *component) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return snd_soc_component_write(component, WM8770_RESET, 0); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct snd_soc_component *component; 34762306a36Sopenharmony_ci int iface, master; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci component = dai->component; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 35262306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 35362306a36Sopenharmony_ci master = 0x100; 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 35662306a36Sopenharmony_ci master = 0; 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci default: 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci iface = 0; 36362306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 36462306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 36562306a36Sopenharmony_ci iface |= 0x2; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 37062306a36Sopenharmony_ci iface |= 0x1; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 37762306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 38062306a36Sopenharmony_ci iface |= 0xc; 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 38362306a36Sopenharmony_ci iface |= 0x8; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 38662306a36Sopenharmony_ci iface |= 0x4; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0xf, iface); 39362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x100, master); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic const int mclk_ratios[] = { 39962306a36Sopenharmony_ci 128, 40062306a36Sopenharmony_ci 192, 40162306a36Sopenharmony_ci 256, 40262306a36Sopenharmony_ci 384, 40362306a36Sopenharmony_ci 512, 40462306a36Sopenharmony_ci 768 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int wm8770_hw_params(struct snd_pcm_substream *substream, 40862306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 40962306a36Sopenharmony_ci struct snd_soc_dai *dai) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct snd_soc_component *component; 41262306a36Sopenharmony_ci struct wm8770_priv *wm8770; 41362306a36Sopenharmony_ci int i; 41462306a36Sopenharmony_ci int iface; 41562306a36Sopenharmony_ci int shift; 41662306a36Sopenharmony_ci int ratio; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci component = dai->component; 41962306a36Sopenharmony_ci wm8770 = snd_soc_component_get_drvdata(component); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci iface = 0; 42262306a36Sopenharmony_ci switch (params_width(params)) { 42362306a36Sopenharmony_ci case 16: 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case 20: 42662306a36Sopenharmony_ci iface |= 0x10; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci case 24: 42962306a36Sopenharmony_ci iface |= 0x20; 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci case 32: 43262306a36Sopenharmony_ci iface |= 0x30; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci switch (substream->stream) { 43762306a36Sopenharmony_ci case SNDRV_PCM_STREAM_PLAYBACK: 43862306a36Sopenharmony_ci i = 0; 43962306a36Sopenharmony_ci shift = 4; 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci case SNDRV_PCM_STREAM_CAPTURE: 44262306a36Sopenharmony_ci i = 2; 44362306a36Sopenharmony_ci shift = 0; 44462306a36Sopenharmony_ci break; 44562306a36Sopenharmony_ci default: 44662306a36Sopenharmony_ci return -EINVAL; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Only need to set MCLK/LRCLK ratio if we're master */ 45062306a36Sopenharmony_ci if (snd_soc_component_read(component, WM8770_MSTRCTRL) & 0x100) { 45162306a36Sopenharmony_ci for (; i < ARRAY_SIZE(mclk_ratios); ++i) { 45262306a36Sopenharmony_ci ratio = wm8770->sysclk / params_rate(params); 45362306a36Sopenharmony_ci if (ratio == mclk_ratios[i]) 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (i == ARRAY_SIZE(mclk_ratios)) { 45862306a36Sopenharmony_ci dev_err(component->dev, 45962306a36Sopenharmony_ci "Unable to configure MCLK ratio %d/%d\n", 46062306a36Sopenharmony_ci wm8770->sysclk, params_rate(params)); 46162306a36Sopenharmony_ci return -EINVAL; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x7 << shift, 46762306a36Sopenharmony_ci i << shift); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0x30, iface); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int wm8770_mute(struct snd_soc_dai *dai, int mute, int direction) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct snd_soc_component *component; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci component = dai->component; 48062306a36Sopenharmony_ci return snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10, 48162306a36Sopenharmony_ci !!mute << 4); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int wm8770_set_sysclk(struct snd_soc_dai *dai, 48562306a36Sopenharmony_ci int clk_id, unsigned int freq, int dir) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct snd_soc_component *component; 48862306a36Sopenharmony_ci struct wm8770_priv *wm8770; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci component = dai->component; 49162306a36Sopenharmony_ci wm8770 = snd_soc_component_get_drvdata(component); 49262306a36Sopenharmony_ci wm8770->sysclk = freq; 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int wm8770_set_bias_level(struct snd_soc_component *component, 49762306a36Sopenharmony_ci enum snd_soc_bias_level level) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci int ret; 50062306a36Sopenharmony_ci struct wm8770_priv *wm8770; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci wm8770 = snd_soc_component_get_drvdata(component); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci switch (level) { 50562306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 50662306a36Sopenharmony_ci break; 50762306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 51062306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 51162306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), 51262306a36Sopenharmony_ci wm8770->supplies); 51362306a36Sopenharmony_ci if (ret) { 51462306a36Sopenharmony_ci dev_err(component->dev, 51562306a36Sopenharmony_ci "Failed to enable supplies: %d\n", 51662306a36Sopenharmony_ci ret); 51762306a36Sopenharmony_ci return ret; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci regcache_sync(wm8770->regmap); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* global powerup */ 52362306a36Sopenharmony_ci snd_soc_component_write(component, WM8770_PWDNCTRL, 0); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 52762306a36Sopenharmony_ci /* global powerdown */ 52862306a36Sopenharmony_ci snd_soc_component_write(component, WM8770_PWDNCTRL, 1); 52962306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), 53062306a36Sopenharmony_ci wm8770->supplies); 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci#define WM8770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 53862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic const struct snd_soc_dai_ops wm8770_dai_ops = { 54162306a36Sopenharmony_ci .mute_stream = wm8770_mute, 54262306a36Sopenharmony_ci .hw_params = wm8770_hw_params, 54362306a36Sopenharmony_ci .set_fmt = wm8770_set_fmt, 54462306a36Sopenharmony_ci .set_sysclk = wm8770_set_sysclk, 54562306a36Sopenharmony_ci .no_capture_mute = 1, 54662306a36Sopenharmony_ci}; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic struct snd_soc_dai_driver wm8770_dai = { 54962306a36Sopenharmony_ci .name = "wm8770-hifi", 55062306a36Sopenharmony_ci .playback = { 55162306a36Sopenharmony_ci .stream_name = "Playback", 55262306a36Sopenharmony_ci .channels_min = 2, 55362306a36Sopenharmony_ci .channels_max = 2, 55462306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 55562306a36Sopenharmony_ci .formats = WM8770_FORMATS 55662306a36Sopenharmony_ci }, 55762306a36Sopenharmony_ci .capture = { 55862306a36Sopenharmony_ci .stream_name = "Capture", 55962306a36Sopenharmony_ci .channels_min = 2, 56062306a36Sopenharmony_ci .channels_max = 2, 56162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 56262306a36Sopenharmony_ci .formats = WM8770_FORMATS 56362306a36Sopenharmony_ci }, 56462306a36Sopenharmony_ci .ops = &wm8770_dai_ops, 56562306a36Sopenharmony_ci .symmetric_rate = 1 56662306a36Sopenharmony_ci}; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int wm8770_probe(struct snd_soc_component *component) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct wm8770_priv *wm8770; 57162306a36Sopenharmony_ci int ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci wm8770 = snd_soc_component_get_drvdata(component); 57462306a36Sopenharmony_ci wm8770->component = component; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), 57762306a36Sopenharmony_ci wm8770->supplies); 57862306a36Sopenharmony_ci if (ret) { 57962306a36Sopenharmony_ci dev_err(component->dev, "Failed to enable supplies: %d\n", ret); 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ret = wm8770_reset(component); 58462306a36Sopenharmony_ci if (ret < 0) { 58562306a36Sopenharmony_ci dev_err(component->dev, "Failed to issue reset: %d\n", ret); 58662306a36Sopenharmony_ci goto err_reg_enable; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* latch the volume update bits */ 59062306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_MSDIGVOL, 0x100, 0x100); 59162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_MSALGVOL, 0x100, 0x100); 59262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_VOUT1RVOL, 0x100, 0x100); 59362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_VOUT2RVOL, 0x100, 0x100); 59462306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_VOUT3RVOL, 0x100, 0x100); 59562306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_VOUT4RVOL, 0x100, 0x100); 59662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_DAC1RVOL, 0x100, 0x100); 59762306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_DAC2RVOL, 0x100, 0x100); 59862306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_DAC3RVOL, 0x100, 0x100); 59962306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_DAC4RVOL, 0x100, 0x100); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* mute all DACs */ 60262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10, 0x10); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cierr_reg_enable: 60562306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); 60662306a36Sopenharmony_ci return ret; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm8770 = { 61062306a36Sopenharmony_ci .probe = wm8770_probe, 61162306a36Sopenharmony_ci .set_bias_level = wm8770_set_bias_level, 61262306a36Sopenharmony_ci .controls = wm8770_snd_controls, 61362306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(wm8770_snd_controls), 61462306a36Sopenharmony_ci .dapm_widgets = wm8770_dapm_widgets, 61562306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets), 61662306a36Sopenharmony_ci .dapm_routes = wm8770_intercon, 61762306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm8770_intercon), 61862306a36Sopenharmony_ci .use_pmdown_time = 1, 61962306a36Sopenharmony_ci .endianness = 1, 62062306a36Sopenharmony_ci}; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic const struct of_device_id wm8770_of_match[] = { 62362306a36Sopenharmony_ci { .compatible = "wlf,wm8770", }, 62462306a36Sopenharmony_ci { } 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wm8770_of_match); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic const struct regmap_config wm8770_regmap = { 62962306a36Sopenharmony_ci .reg_bits = 7, 63062306a36Sopenharmony_ci .val_bits = 9, 63162306a36Sopenharmony_ci .max_register = WM8770_RESET, 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci .reg_defaults = wm8770_reg_defaults, 63462306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults), 63562306a36Sopenharmony_ci .cache_type = REGCACHE_MAPLE, 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci .volatile_reg = wm8770_volatile_reg, 63862306a36Sopenharmony_ci}; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int wm8770_spi_probe(struct spi_device *spi) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct wm8770_priv *wm8770; 64362306a36Sopenharmony_ci int ret, i; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci wm8770 = devm_kzalloc(&spi->dev, sizeof(struct wm8770_priv), 64662306a36Sopenharmony_ci GFP_KERNEL); 64762306a36Sopenharmony_ci if (!wm8770) 64862306a36Sopenharmony_ci return -ENOMEM; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) 65162306a36Sopenharmony_ci wm8770->supplies[i].supply = wm8770_supply_names[i]; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8770->supplies), 65462306a36Sopenharmony_ci wm8770->supplies); 65562306a36Sopenharmony_ci if (ret) { 65662306a36Sopenharmony_ci dev_err(&spi->dev, "Failed to request supplies: %d\n", ret); 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci wm8770->disable_nb[0].notifier_call = wm8770_regulator_event_0; 66162306a36Sopenharmony_ci wm8770->disable_nb[1].notifier_call = wm8770_regulator_event_1; 66262306a36Sopenharmony_ci wm8770->disable_nb[2].notifier_call = wm8770_regulator_event_2; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* This should really be moved into the regulator core */ 66562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) { 66662306a36Sopenharmony_ci ret = devm_regulator_register_notifier( 66762306a36Sopenharmony_ci wm8770->supplies[i].consumer, 66862306a36Sopenharmony_ci &wm8770->disable_nb[i]); 66962306a36Sopenharmony_ci if (ret) { 67062306a36Sopenharmony_ci dev_err(&spi->dev, 67162306a36Sopenharmony_ci "Failed to register regulator notifier: %d\n", 67262306a36Sopenharmony_ci ret); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci wm8770->regmap = devm_regmap_init_spi(spi, &wm8770_regmap); 67762306a36Sopenharmony_ci if (IS_ERR(wm8770->regmap)) 67862306a36Sopenharmony_ci return PTR_ERR(wm8770->regmap); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci spi_set_drvdata(spi, wm8770); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&spi->dev, 68362306a36Sopenharmony_ci &soc_component_dev_wm8770, &wm8770_dai, 1); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic struct spi_driver wm8770_spi_driver = { 68962306a36Sopenharmony_ci .driver = { 69062306a36Sopenharmony_ci .name = "wm8770", 69162306a36Sopenharmony_ci .of_match_table = wm8770_of_match, 69262306a36Sopenharmony_ci }, 69362306a36Sopenharmony_ci .probe = wm8770_spi_probe, 69462306a36Sopenharmony_ci}; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cimodule_spi_driver(wm8770_spi_driver); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM8770 driver"); 69962306a36Sopenharmony_ciMODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); 70062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 701