1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20b5e92c5SJonathan Cameron /* 30b5e92c5SJonathan Cameron * wm8940.c -- WM8940 ALSA Soc Audio driver 40b5e92c5SJonathan Cameron * 50b5e92c5SJonathan Cameron * Author: Jonathan Cameron <jic23@cam.ac.uk> 60b5e92c5SJonathan Cameron * 70b5e92c5SJonathan Cameron * Based on wm8510.c 80b5e92c5SJonathan Cameron * Copyright 2006 Wolfson Microelectronics PLC. 90b5e92c5SJonathan Cameron * Author: Liam Girdwood <lrg@slimlogic.co.uk> 100b5e92c5SJonathan Cameron * 110b5e92c5SJonathan Cameron * Not currently handled: 120b5e92c5SJonathan Cameron * Notch filter control 130b5e92c5SJonathan Cameron * AUXMode (inverting vs mixer) 140b5e92c5SJonathan Cameron * No means to obtain current gain if alc enabled. 150b5e92c5SJonathan Cameron * No use made of gpio 160b5e92c5SJonathan Cameron * Fast VMID discharge for power down 170b5e92c5SJonathan Cameron * Soft Start 180b5e92c5SJonathan Cameron * DLR and ALR Swaps not enabled 190b5e92c5SJonathan Cameron * Digital Sidetone not supported 200b5e92c5SJonathan Cameron */ 210b5e92c5SJonathan Cameron #include <linux/module.h> 220b5e92c5SJonathan Cameron #include <linux/moduleparam.h> 230b5e92c5SJonathan Cameron #include <linux/kernel.h> 240b5e92c5SJonathan Cameron #include <linux/init.h> 250b5e92c5SJonathan Cameron #include <linux/delay.h> 260b5e92c5SJonathan Cameron #include <linux/pm.h> 270b5e92c5SJonathan Cameron #include <linux/i2c.h> 28fbbf7feaSMark Brown #include <linux/regmap.h> 295a0e3ad6STejun Heo #include <linux/slab.h> 300b5e92c5SJonathan Cameron #include <sound/core.h> 310b5e92c5SJonathan Cameron #include <sound/pcm.h> 320b5e92c5SJonathan Cameron #include <sound/pcm_params.h> 330b5e92c5SJonathan Cameron #include <sound/soc.h> 340b5e92c5SJonathan Cameron #include <sound/initval.h> 350b5e92c5SJonathan Cameron #include <sound/tlv.h> 360b5e92c5SJonathan Cameron 370b5e92c5SJonathan Cameron #include "wm8940.h" 380b5e92c5SJonathan Cameron 390b5e92c5SJonathan Cameron struct wm8940_priv { 40294833fcSLukasz Majewski unsigned int mclk; 41294833fcSLukasz Majewski unsigned int fs; 42294833fcSLukasz Majewski 43fbbf7feaSMark Brown struct regmap *regmap; 440b5e92c5SJonathan Cameron }; 450b5e92c5SJonathan Cameron 46fbbf7feaSMark Brown static bool wm8940_volatile_register(struct device *dev, unsigned int reg) 47788b6e8eSAxel Lin { 48788b6e8eSAxel Lin switch (reg) { 49788b6e8eSAxel Lin case WM8940_SOFTRESET: 50fbbf7feaSMark Brown return true; 51788b6e8eSAxel Lin default: 52fbbf7feaSMark Brown return false; 53788b6e8eSAxel Lin } 54788b6e8eSAxel Lin } 55788b6e8eSAxel Lin 56fbbf7feaSMark Brown static bool wm8940_readable_register(struct device *dev, unsigned int reg) 57fbbf7feaSMark Brown { 58fbbf7feaSMark Brown switch (reg) { 59fbbf7feaSMark Brown case WM8940_SOFTRESET: 60fbbf7feaSMark Brown case WM8940_POWER1: 61fbbf7feaSMark Brown case WM8940_POWER2: 62fbbf7feaSMark Brown case WM8940_POWER3: 63fbbf7feaSMark Brown case WM8940_IFACE: 64fbbf7feaSMark Brown case WM8940_COMPANDINGCTL: 65fbbf7feaSMark Brown case WM8940_CLOCK: 66fbbf7feaSMark Brown case WM8940_ADDCNTRL: 67fbbf7feaSMark Brown case WM8940_GPIO: 68fbbf7feaSMark Brown case WM8940_CTLINT: 69fbbf7feaSMark Brown case WM8940_DAC: 70fbbf7feaSMark Brown case WM8940_DACVOL: 71fbbf7feaSMark Brown case WM8940_ADC: 72fbbf7feaSMark Brown case WM8940_ADCVOL: 73fbbf7feaSMark Brown case WM8940_NOTCH1: 74fbbf7feaSMark Brown case WM8940_NOTCH2: 75fbbf7feaSMark Brown case WM8940_NOTCH3: 76fbbf7feaSMark Brown case WM8940_NOTCH4: 77fbbf7feaSMark Brown case WM8940_NOTCH5: 78fbbf7feaSMark Brown case WM8940_NOTCH6: 79fbbf7feaSMark Brown case WM8940_NOTCH7: 80fbbf7feaSMark Brown case WM8940_NOTCH8: 81fbbf7feaSMark Brown case WM8940_DACLIM1: 82fbbf7feaSMark Brown case WM8940_DACLIM2: 83fbbf7feaSMark Brown case WM8940_ALC1: 84fbbf7feaSMark Brown case WM8940_ALC2: 85fbbf7feaSMark Brown case WM8940_ALC3: 86fbbf7feaSMark Brown case WM8940_NOISEGATE: 87fbbf7feaSMark Brown case WM8940_PLLN: 88fbbf7feaSMark Brown case WM8940_PLLK1: 89fbbf7feaSMark Brown case WM8940_PLLK2: 90fbbf7feaSMark Brown case WM8940_PLLK3: 91fbbf7feaSMark Brown case WM8940_ALC4: 92fbbf7feaSMark Brown case WM8940_INPUTCTL: 93fbbf7feaSMark Brown case WM8940_PGAGAIN: 94fbbf7feaSMark Brown case WM8940_ADCBOOST: 95fbbf7feaSMark Brown case WM8940_OUTPUTCTL: 96fbbf7feaSMark Brown case WM8940_SPKMIX: 97fbbf7feaSMark Brown case WM8940_SPKVOL: 98fbbf7feaSMark Brown case WM8940_MONOMIX: 99fbbf7feaSMark Brown return true; 100fbbf7feaSMark Brown default: 101fbbf7feaSMark Brown return false; 102fbbf7feaSMark Brown } 103fbbf7feaSMark Brown } 104fbbf7feaSMark Brown 105fbbf7feaSMark Brown static const struct reg_default wm8940_reg_defaults[] = { 106fbbf7feaSMark Brown { 0x1, 0x0000 }, /* Power 1 */ 107fbbf7feaSMark Brown { 0x2, 0x0000 }, /* Power 2 */ 108fbbf7feaSMark Brown { 0x3, 0x0000 }, /* Power 3 */ 109fbbf7feaSMark Brown { 0x4, 0x0010 }, /* Interface Control */ 110fbbf7feaSMark Brown { 0x5, 0x0000 }, /* Companding Control */ 111fbbf7feaSMark Brown { 0x6, 0x0140 }, /* Clock Control */ 112fbbf7feaSMark Brown { 0x7, 0x0000 }, /* Additional Controls */ 113fbbf7feaSMark Brown { 0x8, 0x0000 }, /* GPIO Control */ 114fbbf7feaSMark Brown { 0x9, 0x0002 }, /* Auto Increment Control */ 115fbbf7feaSMark Brown { 0xa, 0x0000 }, /* DAC Control */ 116fbbf7feaSMark Brown { 0xb, 0x00FF }, /* DAC Volume */ 117fbbf7feaSMark Brown 118fbbf7feaSMark Brown { 0xe, 0x0100 }, /* ADC Control */ 119fbbf7feaSMark Brown { 0xf, 0x00FF }, /* ADC Volume */ 120fbbf7feaSMark Brown { 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */ 121fbbf7feaSMark Brown { 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */ 122fbbf7feaSMark Brown { 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */ 123fbbf7feaSMark Brown { 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */ 124fbbf7feaSMark Brown { 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */ 125fbbf7feaSMark Brown { 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */ 126fbbf7feaSMark Brown { 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */ 127fbbf7feaSMark Brown { 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */ 128fbbf7feaSMark Brown { 0x18, 0x0032 }, /* DAC Limit Control 1 */ 129fbbf7feaSMark Brown { 0x19, 0x0000 }, /* DAC Limit Control 2 */ 130fbbf7feaSMark Brown 131fbbf7feaSMark Brown { 0x20, 0x0038 }, /* ALC Control 1 */ 132fbbf7feaSMark Brown { 0x21, 0x000B }, /* ALC Control 2 */ 133fbbf7feaSMark Brown { 0x22, 0x0032 }, /* ALC Control 3 */ 134fbbf7feaSMark Brown { 0x23, 0x0000 }, /* Noise Gate */ 135fbbf7feaSMark Brown { 0x24, 0x0041 }, /* PLLN */ 136fbbf7feaSMark Brown { 0x25, 0x000C }, /* PLLK1 */ 137fbbf7feaSMark Brown { 0x26, 0x0093 }, /* PLLK2 */ 138fbbf7feaSMark Brown { 0x27, 0x00E9 }, /* PLLK3 */ 139fbbf7feaSMark Brown 140fbbf7feaSMark Brown { 0x2a, 0x0030 }, /* ALC Control 4 */ 141fbbf7feaSMark Brown 142fbbf7feaSMark Brown { 0x2c, 0x0002 }, /* Input Control */ 143fbbf7feaSMark Brown { 0x2d, 0x0050 }, /* PGA Gain */ 144fbbf7feaSMark Brown 145fbbf7feaSMark Brown { 0x2f, 0x0002 }, /* ADC Boost Control */ 146fbbf7feaSMark Brown 147fbbf7feaSMark Brown { 0x31, 0x0002 }, /* Output Control */ 148fbbf7feaSMark Brown { 0x32, 0x0000 }, /* Speaker Mixer Control */ 149fbbf7feaSMark Brown 150fbbf7feaSMark Brown { 0x36, 0x0079 }, /* Speaker Volume */ 151fbbf7feaSMark Brown 152fbbf7feaSMark Brown { 0x38, 0x0000 }, /* Mono Mixer Control */ 1530b5e92c5SJonathan Cameron }; 1540b5e92c5SJonathan Cameron 1550b5e92c5SJonathan Cameron static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; 15647ef3427STakashi Iwai static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum, 15747ef3427STakashi Iwai WM8940_COMPANDINGCTL, 1, wm8940_companding); 15847ef3427STakashi Iwai static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum, 15947ef3427STakashi Iwai WM8940_COMPANDINGCTL, 3, wm8940_companding); 1600b5e92c5SJonathan Cameron 1610b5e92c5SJonathan Cameron static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"}; 16247ef3427STakashi Iwai static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum, 16347ef3427STakashi Iwai WM8940_ALC3, 8, wm8940_alc_mode_text); 1640b5e92c5SJonathan Cameron 1650b5e92c5SJonathan Cameron static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"}; 16647ef3427STakashi Iwai static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum, 16747ef3427STakashi Iwai WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text); 1680b5e92c5SJonathan Cameron 1690b5e92c5SJonathan Cameron static const char *wm8940_filter_mode_text[] = {"Audio", "Application"}; 17047ef3427STakashi Iwai static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum, 17147ef3427STakashi Iwai WM8940_ADC, 7, wm8940_filter_mode_text); 1720b5e92c5SJonathan Cameron 1736be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); 1746be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); 1756be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0); 1766be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0); 1776be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0); 1786be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0); 1796be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0); 1806be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0); 1816be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1); 1826be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0); 1830b5e92c5SJonathan Cameron 1840b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_snd_controls[] = { 1850b5e92c5SJonathan Cameron SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL, 1860b5e92c5SJonathan Cameron 6, 1, 0), 1870b5e92c5SJonathan Cameron SOC_ENUM("DAC Companding", wm8940_dac_companding_enum), 1880b5e92c5SJonathan Cameron SOC_ENUM("ADC Companding", wm8940_adc_companding_enum), 1890b5e92c5SJonathan Cameron 1900b5e92c5SJonathan Cameron SOC_ENUM("ALC Mode", wm8940_alc_mode_enum), 1910b5e92c5SJonathan Cameron SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0), 1920b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1, 1930b5e92c5SJonathan Cameron 3, 7, 1, wm8940_alc_max_tlv), 1940b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1, 1950b5e92c5SJonathan Cameron 0, 7, 0, wm8940_alc_min_tlv), 1960b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2, 1970b5e92c5SJonathan Cameron 0, 14, 0, wm8940_alc_tar_tlv), 1980b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0), 1990b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0), 2000b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0), 2010b5e92c5SJonathan Cameron SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0), 2020b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE, 2030b5e92c5SJonathan Cameron 3, 1, 0), 2040b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE, 2050b5e92c5SJonathan Cameron 0, 7, 0), 2060b5e92c5SJonathan Cameron 2070b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0), 2080b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0), 2090b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0), 2100b5e92c5SJonathan Cameron SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2, 2110b5e92c5SJonathan Cameron 4, 9, 1, wm8940_lim_thresh_tlv), 2120b5e92c5SJonathan Cameron SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2, 2130b5e92c5SJonathan Cameron 0, 12, 0, wm8940_lim_boost_tlv), 2140b5e92c5SJonathan Cameron 2150b5e92c5SJonathan Cameron SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0), 2160b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN, 2170b5e92c5SJonathan Cameron 0, 63, 0, wm8940_pga_vol_tlv), 2180b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL, 2190b5e92c5SJonathan Cameron 0, 255, 0, wm8940_adc_tlv), 2200b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL, 2210b5e92c5SJonathan Cameron 0, 255, 0, wm8940_adc_tlv), 2220b5e92c5SJonathan Cameron SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum), 2230b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST, 2240b5e92c5SJonathan Cameron 8, 1, 0, wm8940_capture_boost_vol_tlv), 2250b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL, 2260b5e92c5SJonathan Cameron 0, 63, 0, wm8940_spk_vol_tlv), 2270b5e92c5SJonathan Cameron SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL, 6, 1, 1), 2280b5e92c5SJonathan Cameron 2290b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL, 2300b5e92c5SJonathan Cameron 8, 1, 1, wm8940_att_tlv), 2310b5e92c5SJonathan Cameron SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0), 2320b5e92c5SJonathan Cameron 2330b5e92c5SJonathan Cameron SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1), 2340b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX, 2350b5e92c5SJonathan Cameron 7, 1, 1, wm8940_att_tlv), 2360b5e92c5SJonathan Cameron 2370b5e92c5SJonathan Cameron SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0), 2380b5e92c5SJonathan Cameron SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum), 2390b5e92c5SJonathan Cameron SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0), 2400b5e92c5SJonathan Cameron SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0), 2410b5e92c5SJonathan Cameron SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0), 2420b5e92c5SJonathan Cameron SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0), 2430b5e92c5SJonathan Cameron SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0), 2440b5e92c5SJonathan Cameron }; 2450b5e92c5SJonathan Cameron 2460b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = { 2470b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0), 2480b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0), 2490b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0), 2500b5e92c5SJonathan Cameron }; 2510b5e92c5SJonathan Cameron 2520b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = { 2530b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0), 2540b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0), 2550b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0), 2560b5e92c5SJonathan Cameron }; 2570b5e92c5SJonathan Cameron 2586be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1); 2590b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_input_boost_controls[] = { 2600b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1), 2610b5e92c5SJonathan Cameron SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST, 2620b5e92c5SJonathan Cameron 0, 7, 0, wm8940_boost_vol_tlv), 2630b5e92c5SJonathan Cameron SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST, 2640b5e92c5SJonathan Cameron 4, 7, 0, wm8940_boost_vol_tlv), 2650b5e92c5SJonathan Cameron }; 2660b5e92c5SJonathan Cameron 2670b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_micpga_controls[] = { 2680b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0), 2690b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0), 2700b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0), 2710b5e92c5SJonathan Cameron }; 2720b5e92c5SJonathan Cameron 2730b5e92c5SJonathan Cameron static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = { 2740b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0, 2750b5e92c5SJonathan Cameron &wm8940_speaker_mixer_controls[0], 2760b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_speaker_mixer_controls)), 2770b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0, 2780b5e92c5SJonathan Cameron &wm8940_mono_mixer_controls[0], 2790b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_mono_mixer_controls)), 2800b5e92c5SJonathan Cameron SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0), 2810b5e92c5SJonathan Cameron 2820b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0), 2830b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0), 2840b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0), 2850b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("MONOOUT"), 2860b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("SPKOUTP"), 2870b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("SPKOUTN"), 2880b5e92c5SJonathan Cameron 2890b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0), 2900b5e92c5SJonathan Cameron SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0), 2910b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0, 2920b5e92c5SJonathan Cameron &wm8940_micpga_controls[0], 2930b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_micpga_controls)), 2940b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0, 2950b5e92c5SJonathan Cameron &wm8940_input_boost_controls[0], 2960b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_input_boost_controls)), 2970b5e92c5SJonathan Cameron SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0), 2980b5e92c5SJonathan Cameron 2990b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("MICN"), 3000b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("MICP"), 3010b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("AUX"), 3020b5e92c5SJonathan Cameron }; 3030b5e92c5SJonathan Cameron 3046435e5beSMark Brown static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { 3050b5e92c5SJonathan Cameron /* Mono output mixer */ 3060b5e92c5SJonathan Cameron {"Mono Mixer", "PCM Playback Switch", "DAC"}, 3070b5e92c5SJonathan Cameron {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 3080b5e92c5SJonathan Cameron {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, 3090b5e92c5SJonathan Cameron 3100b5e92c5SJonathan Cameron /* Speaker output mixer */ 3110b5e92c5SJonathan Cameron {"Speaker Mixer", "PCM Playback Switch", "DAC"}, 3120b5e92c5SJonathan Cameron {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, 3130b5e92c5SJonathan Cameron {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, 3140b5e92c5SJonathan Cameron 3150b5e92c5SJonathan Cameron /* Outputs */ 3160b5e92c5SJonathan Cameron {"Mono Out", NULL, "Mono Mixer"}, 3170b5e92c5SJonathan Cameron {"MONOOUT", NULL, "Mono Out"}, 3180b5e92c5SJonathan Cameron {"SpkN Out", NULL, "Speaker Mixer"}, 3190b5e92c5SJonathan Cameron {"SpkP Out", NULL, "Speaker Mixer"}, 3200b5e92c5SJonathan Cameron {"SPKOUTN", NULL, "SpkN Out"}, 3210b5e92c5SJonathan Cameron {"SPKOUTP", NULL, "SpkP Out"}, 3220b5e92c5SJonathan Cameron 3230b5e92c5SJonathan Cameron /* Microphone PGA */ 3240b5e92c5SJonathan Cameron {"Mic PGA", "MICN Switch", "MICN"}, 3250b5e92c5SJonathan Cameron {"Mic PGA", "MICP Switch", "MICP"}, 3260b5e92c5SJonathan Cameron {"Mic PGA", "AUX Switch", "AUX"}, 3270b5e92c5SJonathan Cameron 3280b5e92c5SJonathan Cameron /* Boost Mixer */ 3290b5e92c5SJonathan Cameron {"Boost Mixer", "Mic PGA Switch", "Mic PGA"}, 3300b5e92c5SJonathan Cameron {"Boost Mixer", "Mic Volume", "MICP"}, 3310b5e92c5SJonathan Cameron {"Boost Mixer", "Aux Volume", "Aux Input"}, 3320b5e92c5SJonathan Cameron 3330b5e92c5SJonathan Cameron {"ADC", NULL, "Boost Mixer"}, 3340b5e92c5SJonathan Cameron }; 3350b5e92c5SJonathan Cameron 33678e89b82SKuninori Morimoto #define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); 3370b5e92c5SJonathan Cameron 3380b5e92c5SJonathan Cameron static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, 3390b5e92c5SJonathan Cameron unsigned int fmt) 3400b5e92c5SJonathan Cameron { 34178e89b82SKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 3426d75dfc3SKuninori Morimoto u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFE67; 3436d75dfc3SKuninori Morimoto u16 clk = snd_soc_component_read(component, WM8940_CLOCK) & 0x1fe; 3440b5e92c5SJonathan Cameron 3450b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3460b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_CBM_CFM: 3470b5e92c5SJonathan Cameron clk |= 1; 3480b5e92c5SJonathan Cameron break; 3490b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_CBS_CFS: 3500b5e92c5SJonathan Cameron break; 3510b5e92c5SJonathan Cameron default: 3520b5e92c5SJonathan Cameron return -EINVAL; 3530b5e92c5SJonathan Cameron } 35478e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_CLOCK, clk); 3550b5e92c5SJonathan Cameron 3560b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 3570b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_I2S: 3580b5e92c5SJonathan Cameron iface |= (2 << 3); 3590b5e92c5SJonathan Cameron break; 3600b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_LEFT_J: 3610b5e92c5SJonathan Cameron iface |= (1 << 3); 3620b5e92c5SJonathan Cameron break; 3630b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_RIGHT_J: 3640b5e92c5SJonathan Cameron break; 3650b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_DSP_A: 3660b5e92c5SJonathan Cameron iface |= (3 << 3); 3670b5e92c5SJonathan Cameron break; 3680b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_DSP_B: 3690b5e92c5SJonathan Cameron iface |= (3 << 3) | (1 << 7); 3700b5e92c5SJonathan Cameron break; 3710b5e92c5SJonathan Cameron } 3720b5e92c5SJonathan Cameron 3730b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 3740b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_NB_NF: 3750b5e92c5SJonathan Cameron break; 3760b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_NB_IF: 3770b5e92c5SJonathan Cameron iface |= (1 << 7); 3780b5e92c5SJonathan Cameron break; 3790b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_IB_NF: 3800b5e92c5SJonathan Cameron iface |= (1 << 8); 3810b5e92c5SJonathan Cameron break; 3820b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_IB_IF: 3830b5e92c5SJonathan Cameron iface |= (1 << 8) | (1 << 7); 3840b5e92c5SJonathan Cameron break; 3850b5e92c5SJonathan Cameron } 3860b5e92c5SJonathan Cameron 38778e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_IFACE, iface); 3880b5e92c5SJonathan Cameron 3890b5e92c5SJonathan Cameron return 0; 3900b5e92c5SJonathan Cameron } 3910b5e92c5SJonathan Cameron 392294833fcSLukasz Majewski static int wm8940_update_clocks(struct snd_soc_dai *dai); 3930b5e92c5SJonathan Cameron static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream, 3940b5e92c5SJonathan Cameron struct snd_pcm_hw_params *params, 3950b5e92c5SJonathan Cameron struct snd_soc_dai *dai) 3960b5e92c5SJonathan Cameron { 39778e89b82SKuninori Morimoto struct snd_soc_component *component = dai->component; 398294833fcSLukasz Majewski struct wm8940_priv *priv = snd_soc_component_get_drvdata(component); 3996d75dfc3SKuninori Morimoto u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFD9F; 4006d75dfc3SKuninori Morimoto u16 addcntrl = snd_soc_component_read(component, WM8940_ADDCNTRL) & 0xFFF1; 4016d75dfc3SKuninori Morimoto u16 companding = snd_soc_component_read(component, 4020b5e92c5SJonathan Cameron WM8940_COMPANDINGCTL) & 0xFFDF; 4030b5e92c5SJonathan Cameron int ret; 4040b5e92c5SJonathan Cameron 405294833fcSLukasz Majewski priv->fs = params_rate(params); 406294833fcSLukasz Majewski ret = wm8940_update_clocks(dai); 407294833fcSLukasz Majewski if (ret) 408294833fcSLukasz Majewski return ret; 409294833fcSLukasz Majewski 4100b5e92c5SJonathan Cameron /* LoutR control */ 4110b5e92c5SJonathan Cameron if (substream->stream == SNDRV_PCM_STREAM_CAPTURE 4120b5e92c5SJonathan Cameron && params_channels(params) == 2) 4130b5e92c5SJonathan Cameron iface |= (1 << 9); 4140b5e92c5SJonathan Cameron 4150b5e92c5SJonathan Cameron switch (params_rate(params)) { 416b3172f22SGuennadi Liakhovetski case 8000: 4170b5e92c5SJonathan Cameron addcntrl |= (0x5 << 1); 4180b5e92c5SJonathan Cameron break; 419b3172f22SGuennadi Liakhovetski case 11025: 4200b5e92c5SJonathan Cameron addcntrl |= (0x4 << 1); 4210b5e92c5SJonathan Cameron break; 422b3172f22SGuennadi Liakhovetski case 16000: 4230b5e92c5SJonathan Cameron addcntrl |= (0x3 << 1); 4240b5e92c5SJonathan Cameron break; 425b3172f22SGuennadi Liakhovetski case 22050: 4260b5e92c5SJonathan Cameron addcntrl |= (0x2 << 1); 4270b5e92c5SJonathan Cameron break; 428b3172f22SGuennadi Liakhovetski case 32000: 4290b5e92c5SJonathan Cameron addcntrl |= (0x1 << 1); 4300b5e92c5SJonathan Cameron break; 431b3172f22SGuennadi Liakhovetski case 44100: 432b3172f22SGuennadi Liakhovetski case 48000: 4330b5e92c5SJonathan Cameron break; 4340b5e92c5SJonathan Cameron } 43578e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_ADDCNTRL, addcntrl); 4360b5e92c5SJonathan Cameron if (ret) 4370b5e92c5SJonathan Cameron goto error_ret; 4380b5e92c5SJonathan Cameron 43974b24c38SMark Brown switch (params_width(params)) { 44074b24c38SMark Brown case 8: 4410b5e92c5SJonathan Cameron companding = companding | (1 << 5); 4420b5e92c5SJonathan Cameron break; 44374b24c38SMark Brown case 16: 4440b5e92c5SJonathan Cameron break; 44574b24c38SMark Brown case 20: 4460b5e92c5SJonathan Cameron iface |= (1 << 5); 4470b5e92c5SJonathan Cameron break; 44874b24c38SMark Brown case 24: 4490b5e92c5SJonathan Cameron iface |= (2 << 5); 4500b5e92c5SJonathan Cameron break; 45174b24c38SMark Brown case 32: 4520b5e92c5SJonathan Cameron iface |= (3 << 5); 4530b5e92c5SJonathan Cameron break; 4540b5e92c5SJonathan Cameron } 45578e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_COMPANDINGCTL, companding); 4560b5e92c5SJonathan Cameron if (ret) 4570b5e92c5SJonathan Cameron goto error_ret; 45878e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_IFACE, iface); 4590b5e92c5SJonathan Cameron 4600b5e92c5SJonathan Cameron error_ret: 4610b5e92c5SJonathan Cameron return ret; 4620b5e92c5SJonathan Cameron } 4630b5e92c5SJonathan Cameron 46426d3c16eSKuninori Morimoto static int wm8940_mute(struct snd_soc_dai *dai, int mute, int direction) 4650b5e92c5SJonathan Cameron { 46678e89b82SKuninori Morimoto struct snd_soc_component *component = dai->component; 4676d75dfc3SKuninori Morimoto u16 mute_reg = snd_soc_component_read(component, WM8940_DAC) & 0xffbf; 4680b5e92c5SJonathan Cameron 4690b5e92c5SJonathan Cameron if (mute) 4700b5e92c5SJonathan Cameron mute_reg |= 0x40; 4710b5e92c5SJonathan Cameron 47278e89b82SKuninori Morimoto return snd_soc_component_write(component, WM8940_DAC, mute_reg); 4730b5e92c5SJonathan Cameron } 4740b5e92c5SJonathan Cameron 47578e89b82SKuninori Morimoto static int wm8940_set_bias_level(struct snd_soc_component *component, 4760b5e92c5SJonathan Cameron enum snd_soc_bias_level level) 4770b5e92c5SJonathan Cameron { 47878e89b82SKuninori Morimoto struct wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component); 4790b5e92c5SJonathan Cameron u16 val; 4806d75dfc3SKuninori Morimoto u16 pwr_reg = snd_soc_component_read(component, WM8940_POWER1) & 0x1F0; 4810b5e92c5SJonathan Cameron int ret = 0; 4820b5e92c5SJonathan Cameron 4830b5e92c5SJonathan Cameron switch (level) { 4840b5e92c5SJonathan Cameron case SND_SOC_BIAS_ON: 4850b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4860b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 4870b5e92c5SJonathan Cameron /* Enable thermal shutdown */ 4886d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8940_OUTPUTCTL); 48978e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, val | 0x2); 4900b5e92c5SJonathan Cameron if (ret) 4910b5e92c5SJonathan Cameron break; 4920b5e92c5SJonathan Cameron /* set vmid to 75k */ 49378e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1); 4940b5e92c5SJonathan Cameron break; 4950b5e92c5SJonathan Cameron case SND_SOC_BIAS_PREPARE: 4960b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4970b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 49878e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1); 4990b5e92c5SJonathan Cameron break; 5000b5e92c5SJonathan Cameron case SND_SOC_BIAS_STANDBY: 50178e89b82SKuninori Morimoto if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 502fbbf7feaSMark Brown ret = regcache_sync(wm8940->regmap); 503788b6e8eSAxel Lin if (ret < 0) { 50478e89b82SKuninori Morimoto dev_err(component->dev, "Failed to sync cache: %d\n", ret); 505788b6e8eSAxel Lin return ret; 506788b6e8eSAxel Lin } 507788b6e8eSAxel Lin } 508788b6e8eSAxel Lin 5090b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 5100b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 5110b5e92c5SJonathan Cameron /* set vmid to 300k for standby */ 51278e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x2); 5130b5e92c5SJonathan Cameron break; 5140b5e92c5SJonathan Cameron case SND_SOC_BIAS_OFF: 51578e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg); 5160b5e92c5SJonathan Cameron break; 5170b5e92c5SJonathan Cameron } 5180b5e92c5SJonathan Cameron 5190b5e92c5SJonathan Cameron return ret; 5200b5e92c5SJonathan Cameron } 5210b5e92c5SJonathan Cameron 5220b5e92c5SJonathan Cameron struct pll_ { 5230b5e92c5SJonathan Cameron unsigned int pre_scale:2; 5240b5e92c5SJonathan Cameron unsigned int n:4; 5250b5e92c5SJonathan Cameron unsigned int k; 5260b5e92c5SJonathan Cameron }; 5270b5e92c5SJonathan Cameron 5280b5e92c5SJonathan Cameron static struct pll_ pll_div; 5290b5e92c5SJonathan Cameron 5300b5e92c5SJonathan Cameron /* The size in bits of the pll divide multiplied by 10 5310b5e92c5SJonathan Cameron * to allow rounding later */ 5320b5e92c5SJonathan Cameron #define FIXED_PLL_SIZE ((1 << 24) * 10) 5330b5e92c5SJonathan Cameron static void pll_factors(unsigned int target, unsigned int source) 5340b5e92c5SJonathan Cameron { 5350b5e92c5SJonathan Cameron unsigned long long Kpart; 5360b5e92c5SJonathan Cameron unsigned int K, Ndiv, Nmod; 5370b5e92c5SJonathan Cameron /* The left shift ist to avoid accuracy loss when right shifting */ 5380b5e92c5SJonathan Cameron Ndiv = target / source; 5390b5e92c5SJonathan Cameron 5400b5e92c5SJonathan Cameron if (Ndiv > 12) { 5410b5e92c5SJonathan Cameron source <<= 1; 5420b5e92c5SJonathan Cameron /* Multiply by 2 */ 5430b5e92c5SJonathan Cameron pll_div.pre_scale = 0; 5440b5e92c5SJonathan Cameron Ndiv = target / source; 5450b5e92c5SJonathan Cameron } else if (Ndiv < 3) { 5460b5e92c5SJonathan Cameron source >>= 2; 5470b5e92c5SJonathan Cameron /* Divide by 4 */ 5480b5e92c5SJonathan Cameron pll_div.pre_scale = 3; 5490b5e92c5SJonathan Cameron Ndiv = target / source; 5500b5e92c5SJonathan Cameron } else if (Ndiv < 6) { 5510b5e92c5SJonathan Cameron source >>= 1; 5520b5e92c5SJonathan Cameron /* divide by 2 */ 5530b5e92c5SJonathan Cameron pll_div.pre_scale = 2; 5540b5e92c5SJonathan Cameron Ndiv = target / source; 5550b5e92c5SJonathan Cameron } else 5560b5e92c5SJonathan Cameron pll_div.pre_scale = 1; 5570b5e92c5SJonathan Cameron 5580b5e92c5SJonathan Cameron if ((Ndiv < 6) || (Ndiv > 12)) 5590b5e92c5SJonathan Cameron printk(KERN_WARNING 5600b5e92c5SJonathan Cameron "WM8940 N value %d outwith recommended range!d\n", 5610b5e92c5SJonathan Cameron Ndiv); 5620b5e92c5SJonathan Cameron 5630b5e92c5SJonathan Cameron pll_div.n = Ndiv; 5640b5e92c5SJonathan Cameron Nmod = target % source; 5650b5e92c5SJonathan Cameron Kpart = FIXED_PLL_SIZE * (long long)Nmod; 5660b5e92c5SJonathan Cameron 5670b5e92c5SJonathan Cameron do_div(Kpart, source); 5680b5e92c5SJonathan Cameron 5690b5e92c5SJonathan Cameron K = Kpart & 0xFFFFFFFF; 5700b5e92c5SJonathan Cameron 5710b5e92c5SJonathan Cameron /* Check if we need to round */ 5720b5e92c5SJonathan Cameron if ((K % 10) >= 5) 5730b5e92c5SJonathan Cameron K += 5; 5740b5e92c5SJonathan Cameron 5750b5e92c5SJonathan Cameron /* Move down to proper range now rounding is done */ 5760b5e92c5SJonathan Cameron K /= 10; 5770b5e92c5SJonathan Cameron 5780b5e92c5SJonathan Cameron pll_div.k = K; 5790b5e92c5SJonathan Cameron } 5800b5e92c5SJonathan Cameron 5810b5e92c5SJonathan Cameron /* Untested at the moment */ 58285488037SMark Brown static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 58385488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 5840b5e92c5SJonathan Cameron { 58578e89b82SKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 5860b5e92c5SJonathan Cameron u16 reg; 5870b5e92c5SJonathan Cameron 5880b5e92c5SJonathan Cameron /* Turn off PLL */ 5896d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_POWER1); 59078e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_POWER1, reg & 0x1df); 5910b5e92c5SJonathan Cameron 5920b5e92c5SJonathan Cameron if (freq_in == 0 || freq_out == 0) { 5930b5e92c5SJonathan Cameron /* Clock CODEC directly from MCLK */ 5946d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_CLOCK); 59578e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_CLOCK, reg & 0x0ff); 5960b5e92c5SJonathan Cameron /* Pll power down */ 59778e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_PLLN, (1 << 7)); 5980b5e92c5SJonathan Cameron return 0; 5990b5e92c5SJonathan Cameron } 6000b5e92c5SJonathan Cameron 6010b5e92c5SJonathan Cameron /* Pll is followed by a frequency divide by 4 */ 6020b5e92c5SJonathan Cameron pll_factors(freq_out*4, freq_in); 6030b5e92c5SJonathan Cameron if (pll_div.k) 60478e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_PLLN, 6050b5e92c5SJonathan Cameron (pll_div.pre_scale << 4) | pll_div.n | (1 << 6)); 6060b5e92c5SJonathan Cameron else /* No factional component */ 60778e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_PLLN, 6080b5e92c5SJonathan Cameron (pll_div.pre_scale << 4) | pll_div.n); 60978e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_PLLK1, pll_div.k >> 18); 61078e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff); 61178e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_PLLK3, pll_div.k & 0x1ff); 6120b5e92c5SJonathan Cameron /* Enable the PLL */ 6136d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_POWER1); 61478e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_POWER1, reg | 0x020); 6150b5e92c5SJonathan Cameron 6160b5e92c5SJonathan Cameron /* Run CODEC from PLL instead of MCLK */ 6176d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_CLOCK); 61878e89b82SKuninori Morimoto snd_soc_component_write(component, WM8940_CLOCK, reg | 0x100); 6190b5e92c5SJonathan Cameron 6200b5e92c5SJonathan Cameron return 0; 6210b5e92c5SJonathan Cameron } 6220b5e92c5SJonathan Cameron 6230b5e92c5SJonathan Cameron static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 6240b5e92c5SJonathan Cameron int div_id, int div) 6250b5e92c5SJonathan Cameron { 62678e89b82SKuninori Morimoto struct snd_soc_component *component = codec_dai->component; 6270b5e92c5SJonathan Cameron u16 reg; 6280b5e92c5SJonathan Cameron int ret = 0; 6290b5e92c5SJonathan Cameron 6300b5e92c5SJonathan Cameron switch (div_id) { 6310b5e92c5SJonathan Cameron case WM8940_BCLKDIV: 6326d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFFE3; 63378e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 2)); 6340b5e92c5SJonathan Cameron break; 6350b5e92c5SJonathan Cameron case WM8940_MCLKDIV: 6366d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFF1F; 63778e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 5)); 6380b5e92c5SJonathan Cameron break; 6390b5e92c5SJonathan Cameron case WM8940_OPCLKDIV: 6406d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_GPIO) & 0xFFCF; 64178e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_GPIO, reg | (div << 4)); 6420b5e92c5SJonathan Cameron break; 6430b5e92c5SJonathan Cameron } 6440b5e92c5SJonathan Cameron return ret; 6450b5e92c5SJonathan Cameron } 6460b5e92c5SJonathan Cameron 647294833fcSLukasz Majewski static unsigned int wm8940_get_mclkdiv(unsigned int f_in, unsigned int f_out, 648294833fcSLukasz Majewski int *mclkdiv) 649294833fcSLukasz Majewski { 650294833fcSLukasz Majewski unsigned int ratio = 2 * f_in / f_out; 651294833fcSLukasz Majewski 652294833fcSLukasz Majewski if (ratio <= 2) { 653294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_1; 654294833fcSLukasz Majewski ratio = 2; 655294833fcSLukasz Majewski } else if (ratio == 3) { 656294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_1_5; 657294833fcSLukasz Majewski } else if (ratio == 4) { 658294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_2; 659294833fcSLukasz Majewski } else if (ratio <= 6) { 660294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_3; 661294833fcSLukasz Majewski ratio = 6; 662294833fcSLukasz Majewski } else if (ratio <= 8) { 663294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_4; 664294833fcSLukasz Majewski ratio = 8; 665294833fcSLukasz Majewski } else if (ratio <= 12) { 666294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_6; 667294833fcSLukasz Majewski ratio = 12; 668294833fcSLukasz Majewski } else if (ratio <= 16) { 669294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_8; 670294833fcSLukasz Majewski ratio = 16; 671294833fcSLukasz Majewski } else { 672294833fcSLukasz Majewski *mclkdiv = WM8940_MCLKDIV_12; 673294833fcSLukasz Majewski ratio = 24; 674294833fcSLukasz Majewski } 675294833fcSLukasz Majewski 676294833fcSLukasz Majewski return f_out * ratio / 2; 677294833fcSLukasz Majewski } 678294833fcSLukasz Majewski 679294833fcSLukasz Majewski static int wm8940_update_clocks(struct snd_soc_dai *dai) 680294833fcSLukasz Majewski { 681294833fcSLukasz Majewski struct snd_soc_component *codec = dai->component; 682294833fcSLukasz Majewski struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec); 683294833fcSLukasz Majewski unsigned int fs256; 684294833fcSLukasz Majewski unsigned int fpll = 0; 685294833fcSLukasz Majewski unsigned int f; 686294833fcSLukasz Majewski int mclkdiv; 687294833fcSLukasz Majewski 688294833fcSLukasz Majewski if (!priv->mclk || !priv->fs) 689294833fcSLukasz Majewski return 0; 690294833fcSLukasz Majewski 691294833fcSLukasz Majewski fs256 = 256 * priv->fs; 692294833fcSLukasz Majewski 693294833fcSLukasz Majewski f = wm8940_get_mclkdiv(priv->mclk, fs256, &mclkdiv); 694294833fcSLukasz Majewski if (f != priv->mclk) { 695294833fcSLukasz Majewski /* The PLL performs best around 90MHz */ 696294833fcSLukasz Majewski fpll = wm8940_get_mclkdiv(22500000, fs256, &mclkdiv); 697294833fcSLukasz Majewski } 698294833fcSLukasz Majewski 699294833fcSLukasz Majewski wm8940_set_dai_pll(dai, 0, 0, priv->mclk, fpll); 700294833fcSLukasz Majewski wm8940_set_dai_clkdiv(dai, WM8940_MCLKDIV, mclkdiv); 701294833fcSLukasz Majewski 702294833fcSLukasz Majewski return 0; 703294833fcSLukasz Majewski } 704294833fcSLukasz Majewski 705294833fcSLukasz Majewski static int wm8940_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 706294833fcSLukasz Majewski unsigned int freq, int dir) 707294833fcSLukasz Majewski { 708294833fcSLukasz Majewski struct snd_soc_component *codec = dai->component; 709294833fcSLukasz Majewski struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec); 710294833fcSLukasz Majewski 711294833fcSLukasz Majewski if (dir != SND_SOC_CLOCK_IN) 712294833fcSLukasz Majewski return -EINVAL; 713294833fcSLukasz Majewski 714294833fcSLukasz Majewski priv->mclk = freq; 715294833fcSLukasz Majewski 716294833fcSLukasz Majewski return wm8940_update_clocks(dai); 717294833fcSLukasz Majewski } 718294833fcSLukasz Majewski 7190b5e92c5SJonathan Cameron #define WM8940_RATES SNDRV_PCM_RATE_8000_48000 7200b5e92c5SJonathan Cameron 7210b5e92c5SJonathan Cameron #define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 7220b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S16_LE | \ 7230b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S20_3LE | \ 7240b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S24_LE | \ 7250b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S32_LE) 7260b5e92c5SJonathan Cameron 72785e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8940_dai_ops = { 7280b5e92c5SJonathan Cameron .hw_params = wm8940_i2s_hw_params, 7290b5e92c5SJonathan Cameron .set_sysclk = wm8940_set_dai_sysclk, 73026d3c16eSKuninori Morimoto .mute_stream = wm8940_mute, 7310b5e92c5SJonathan Cameron .set_fmt = wm8940_set_dai_fmt, 7320b5e92c5SJonathan Cameron .set_clkdiv = wm8940_set_dai_clkdiv, 7330b5e92c5SJonathan Cameron .set_pll = wm8940_set_dai_pll, 73426d3c16eSKuninori Morimoto .no_capture_mute = 1, 7350b5e92c5SJonathan Cameron }; 7360b5e92c5SJonathan Cameron 737f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8940_dai = { 738f0fba2adSLiam Girdwood .name = "wm8940-hifi", 7390b5e92c5SJonathan Cameron .playback = { 7400b5e92c5SJonathan Cameron .stream_name = "Playback", 7410b5e92c5SJonathan Cameron .channels_min = 1, 7420b5e92c5SJonathan Cameron .channels_max = 2, 7430b5e92c5SJonathan Cameron .rates = WM8940_RATES, 7440b5e92c5SJonathan Cameron .formats = WM8940_FORMATS, 7450b5e92c5SJonathan Cameron }, 7460b5e92c5SJonathan Cameron .capture = { 7470b5e92c5SJonathan Cameron .stream_name = "Capture", 7480b5e92c5SJonathan Cameron .channels_min = 1, 7490b5e92c5SJonathan Cameron .channels_max = 2, 7500b5e92c5SJonathan Cameron .rates = WM8940_RATES, 7510b5e92c5SJonathan Cameron .formats = WM8940_FORMATS, 7520b5e92c5SJonathan Cameron }, 7530b5e92c5SJonathan Cameron .ops = &wm8940_dai_ops, 75407695752SKuninori Morimoto .symmetric_rate = 1, 7550b5e92c5SJonathan Cameron }; 7560b5e92c5SJonathan Cameron 75778e89b82SKuninori Morimoto static int wm8940_probe(struct snd_soc_component *component) 7580b5e92c5SJonathan Cameron { 75978e89b82SKuninori Morimoto struct wm8940_setup_data *pdata = component->dev->platform_data; 7600b5e92c5SJonathan Cameron int ret; 7610b5e92c5SJonathan Cameron u16 reg; 7620b5e92c5SJonathan Cameron 763a5c26ee5SLukasz Majewski /* 764a5c26ee5SLukasz Majewski * Check chip ID for wm8940 - value of 0x00 offset 765a5c26ee5SLukasz Majewski * SOFTWARE_RESET on write 766a5c26ee5SLukasz Majewski * CHIP_ID on read 767a5c26ee5SLukasz Majewski */ 768a5c26ee5SLukasz Majewski reg = snd_soc_component_read(component, WM8940_SOFTRESET); 769a5c26ee5SLukasz Majewski if (reg != WM8940_CHIP_ID) { 770a5c26ee5SLukasz Majewski dev_err(component->dev, "Wrong wm8940 chip ID: 0x%x\n", reg); 771a5c26ee5SLukasz Majewski return -ENODEV; 772a5c26ee5SLukasz Majewski } 773a5c26ee5SLukasz Majewski 77478e89b82SKuninori Morimoto ret = wm8940_reset(component); 7750b5e92c5SJonathan Cameron if (ret < 0) { 77678e89b82SKuninori Morimoto dev_err(component->dev, "Failed to issue reset\n"); 7770b5e92c5SJonathan Cameron return ret; 7780b5e92c5SJonathan Cameron } 7790b5e92c5SJonathan Cameron 78078e89b82SKuninori Morimoto snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); 7810b5e92c5SJonathan Cameron 78278e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_POWER1, 0x180); 7830b5e92c5SJonathan Cameron if (ret < 0) 7840b5e92c5SJonathan Cameron return ret; 7850b5e92c5SJonathan Cameron 7865dc5e76bSLukasz Majewski if (pdata) { 7876d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8940_OUTPUTCTL); 78878e89b82SKuninori Morimoto ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, reg | pdata->vroi); 7890b5e92c5SJonathan Cameron if (ret < 0) 7900b5e92c5SJonathan Cameron return ret; 7910b5e92c5SJonathan Cameron } 7920b5e92c5SJonathan Cameron 793f0fba2adSLiam Girdwood return ret; 7940b5e92c5SJonathan Cameron } 7950b5e92c5SJonathan Cameron 79678e89b82SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wm8940 = { 797f0fba2adSLiam Girdwood .probe = wm8940_probe, 798f0fba2adSLiam Girdwood .set_bias_level = wm8940_set_bias_level, 7996435e5beSMark Brown .controls = wm8940_snd_controls, 8006435e5beSMark Brown .num_controls = ARRAY_SIZE(wm8940_snd_controls), 8016435e5beSMark Brown .dapm_widgets = wm8940_dapm_widgets, 8026435e5beSMark Brown .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets), 8036435e5beSMark Brown .dapm_routes = wm8940_dapm_routes, 8046435e5beSMark Brown .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes), 80578e89b82SKuninori Morimoto .suspend_bias_off = 1, 80678e89b82SKuninori Morimoto .idle_bias_on = 1, 80778e89b82SKuninori Morimoto .use_pmdown_time = 1, 80878e89b82SKuninori Morimoto .endianness = 1, 809fbbf7feaSMark Brown }; 810fbbf7feaSMark Brown 811fbbf7feaSMark Brown static const struct regmap_config wm8940_regmap = { 812fbbf7feaSMark Brown .reg_bits = 8, 813fbbf7feaSMark Brown .val_bits = 16, 814fbbf7feaSMark Brown 815fbbf7feaSMark Brown .max_register = WM8940_MONOMIX, 816fbbf7feaSMark Brown .reg_defaults = wm8940_reg_defaults, 817fbbf7feaSMark Brown .num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults), 8189bed789cSMark Brown .cache_type = REGCACHE_MAPLE, 819fbbf7feaSMark Brown 820fbbf7feaSMark Brown .readable_reg = wm8940_readable_register, 821fbbf7feaSMark Brown .volatile_reg = wm8940_volatile_register, 822f0fba2adSLiam Girdwood }; 8230b5e92c5SJonathan Cameron 82497b0b6e3SStephen Kitt static int wm8940_i2c_probe(struct i2c_client *i2c) 8250b5e92c5SJonathan Cameron { 8260b5e92c5SJonathan Cameron struct wm8940_priv *wm8940; 827f0fba2adSLiam Girdwood int ret; 8280b5e92c5SJonathan Cameron 82942dad0d8SMark Brown wm8940 = devm_kzalloc(&i2c->dev, sizeof(struct wm8940_priv), 83042dad0d8SMark Brown GFP_KERNEL); 8310b5e92c5SJonathan Cameron if (wm8940 == NULL) 8320b5e92c5SJonathan Cameron return -ENOMEM; 8330b5e92c5SJonathan Cameron 834fbbf7feaSMark Brown wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap); 835fbbf7feaSMark Brown if (IS_ERR(wm8940->regmap)) 836fbbf7feaSMark Brown return PTR_ERR(wm8940->regmap); 837fbbf7feaSMark Brown 8380b5e92c5SJonathan Cameron i2c_set_clientdata(i2c, wm8940); 8390b5e92c5SJonathan Cameron 84078e89b82SKuninori Morimoto ret = devm_snd_soc_register_component(&i2c->dev, 84178e89b82SKuninori Morimoto &soc_component_dev_wm8940, &wm8940_dai, 1); 84242dad0d8SMark Brown 843db1e18deSAxel Lin return ret; 8440b5e92c5SJonathan Cameron } 8450b5e92c5SJonathan Cameron 8460b5e92c5SJonathan Cameron static const struct i2c_device_id wm8940_i2c_id[] = { 847*ba2a2c37SUwe Kleine-König { "wm8940" }, 8480b5e92c5SJonathan Cameron { } 8490b5e92c5SJonathan Cameron }; 8500b5e92c5SJonathan Cameron MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); 8510b5e92c5SJonathan Cameron 8523a3610aaSLukasz Majewski static const struct of_device_id wm8940_of_match[] = { 8533a3610aaSLukasz Majewski { .compatible = "wlf,wm8940", }, 8543a3610aaSLukasz Majewski { } 8553a3610aaSLukasz Majewski }; 8563a3610aaSLukasz Majewski MODULE_DEVICE_TABLE(of, wm8940_of_match); 8573a3610aaSLukasz Majewski 8580b5e92c5SJonathan Cameron static struct i2c_driver wm8940_i2c_driver = { 8590b5e92c5SJonathan Cameron .driver = { 860091edccfSMark Brown .name = "wm8940", 8613a3610aaSLukasz Majewski .of_match_table = wm8940_of_match, 8620b5e92c5SJonathan Cameron }, 8639abcd240SUwe Kleine-König .probe = wm8940_i2c_probe, 8640b5e92c5SJonathan Cameron .id_table = wm8940_i2c_id, 8650b5e92c5SJonathan Cameron }; 8660b5e92c5SJonathan Cameron 867794836b9SSachin Kamat module_i2c_driver(wm8940_i2c_driver); 8680b5e92c5SJonathan Cameron 8690b5e92c5SJonathan Cameron MODULE_DESCRIPTION("ASoC WM8940 driver"); 8700b5e92c5SJonathan Cameron MODULE_AUTHOR("Jonathan Cameron"); 8710b5e92c5SJonathan Cameron MODULE_LICENSE("GPL"); 872