10b5e92c5SJonathan Cameron /* 20b5e92c5SJonathan Cameron * wm8940.c -- WM8940 ALSA Soc Audio driver 30b5e92c5SJonathan Cameron * 40b5e92c5SJonathan Cameron * Author: Jonathan Cameron <jic23@cam.ac.uk> 50b5e92c5SJonathan Cameron * 60b5e92c5SJonathan Cameron * Based on wm8510.c 70b5e92c5SJonathan Cameron * Copyright 2006 Wolfson Microelectronics PLC. 80b5e92c5SJonathan Cameron * Author: Liam Girdwood <lrg@slimlogic.co.uk> 90b5e92c5SJonathan Cameron * 100b5e92c5SJonathan Cameron * This program is free software; you can redistribute it and/or modify 110b5e92c5SJonathan Cameron * it under the terms of the GNU General Public License version 2 as 120b5e92c5SJonathan Cameron * published by the Free Software Foundation. 130b5e92c5SJonathan Cameron * 140b5e92c5SJonathan Cameron * Not currently handled: 150b5e92c5SJonathan Cameron * Notch filter control 160b5e92c5SJonathan Cameron * AUXMode (inverting vs mixer) 170b5e92c5SJonathan Cameron * No means to obtain current gain if alc enabled. 180b5e92c5SJonathan Cameron * No use made of gpio 190b5e92c5SJonathan Cameron * Fast VMID discharge for power down 200b5e92c5SJonathan Cameron * Soft Start 210b5e92c5SJonathan Cameron * DLR and ALR Swaps not enabled 220b5e92c5SJonathan Cameron * Digital Sidetone not supported 230b5e92c5SJonathan Cameron */ 240b5e92c5SJonathan Cameron #include <linux/module.h> 250b5e92c5SJonathan Cameron #include <linux/moduleparam.h> 260b5e92c5SJonathan Cameron #include <linux/kernel.h> 270b5e92c5SJonathan Cameron #include <linux/init.h> 280b5e92c5SJonathan Cameron #include <linux/delay.h> 290b5e92c5SJonathan Cameron #include <linux/pm.h> 300b5e92c5SJonathan Cameron #include <linux/i2c.h> 310b5e92c5SJonathan Cameron #include <linux/platform_device.h> 320b5e92c5SJonathan Cameron #include <linux/spi/spi.h> 330b5e92c5SJonathan Cameron #include <sound/core.h> 340b5e92c5SJonathan Cameron #include <sound/pcm.h> 350b5e92c5SJonathan Cameron #include <sound/pcm_params.h> 360b5e92c5SJonathan Cameron #include <sound/soc.h> 370b5e92c5SJonathan Cameron #include <sound/soc-dapm.h> 380b5e92c5SJonathan Cameron #include <sound/initval.h> 390b5e92c5SJonathan Cameron #include <sound/tlv.h> 400b5e92c5SJonathan Cameron 410b5e92c5SJonathan Cameron #include "wm8940.h" 420b5e92c5SJonathan Cameron 430b5e92c5SJonathan Cameron struct wm8940_priv { 440b5e92c5SJonathan Cameron unsigned int sysclk; 450b5e92c5SJonathan Cameron u16 reg_cache[WM8940_CACHEREGNUM]; 460b5e92c5SJonathan Cameron struct snd_soc_codec codec; 470b5e92c5SJonathan Cameron }; 480b5e92c5SJonathan Cameron 490b5e92c5SJonathan Cameron static u16 wm8940_reg_defaults[] = { 500b5e92c5SJonathan Cameron 0x8940, /* Soft Reset */ 510b5e92c5SJonathan Cameron 0x0000, /* Power 1 */ 520b5e92c5SJonathan Cameron 0x0000, /* Power 2 */ 530b5e92c5SJonathan Cameron 0x0000, /* Power 3 */ 540b5e92c5SJonathan Cameron 0x0010, /* Interface Control */ 550b5e92c5SJonathan Cameron 0x0000, /* Companding Control */ 560b5e92c5SJonathan Cameron 0x0140, /* Clock Control */ 570b5e92c5SJonathan Cameron 0x0000, /* Additional Controls */ 580b5e92c5SJonathan Cameron 0x0000, /* GPIO Control */ 590b5e92c5SJonathan Cameron 0x0002, /* Auto Increment Control */ 600b5e92c5SJonathan Cameron 0x0000, /* DAC Control */ 610b5e92c5SJonathan Cameron 0x00FF, /* DAC Volume */ 620b5e92c5SJonathan Cameron 0, 630b5e92c5SJonathan Cameron 0, 640b5e92c5SJonathan Cameron 0x0100, /* ADC Control */ 650b5e92c5SJonathan Cameron 0x00FF, /* ADC Volume */ 660b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 1 Control 1 */ 670b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 1 Control 2 */ 680b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 2 Control 1 */ 690b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 2 Control 2 */ 700b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 3 Control 1 */ 710b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 3 Control 2 */ 720b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 4 Control 1 */ 730b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 4 Control 2 */ 740b5e92c5SJonathan Cameron 0x0032, /* DAC Limit Control 1 */ 750b5e92c5SJonathan Cameron 0x0000, /* DAC Limit Control 2 */ 760b5e92c5SJonathan Cameron 0, 770b5e92c5SJonathan Cameron 0, 780b5e92c5SJonathan Cameron 0, 790b5e92c5SJonathan Cameron 0, 800b5e92c5SJonathan Cameron 0, 810b5e92c5SJonathan Cameron 0, 820b5e92c5SJonathan Cameron 0x0038, /* ALC Control 1 */ 830b5e92c5SJonathan Cameron 0x000B, /* ALC Control 2 */ 840b5e92c5SJonathan Cameron 0x0032, /* ALC Control 3 */ 850b5e92c5SJonathan Cameron 0x0000, /* Noise Gate */ 860b5e92c5SJonathan Cameron 0x0041, /* PLLN */ 870b5e92c5SJonathan Cameron 0x000C, /* PLLK1 */ 880b5e92c5SJonathan Cameron 0x0093, /* PLLK2 */ 890b5e92c5SJonathan Cameron 0x00E9, /* PLLK3 */ 900b5e92c5SJonathan Cameron 0, 910b5e92c5SJonathan Cameron 0, 920b5e92c5SJonathan Cameron 0x0030, /* ALC Control 4 */ 930b5e92c5SJonathan Cameron 0, 940b5e92c5SJonathan Cameron 0x0002, /* Input Control */ 950b5e92c5SJonathan Cameron 0x0050, /* PGA Gain */ 960b5e92c5SJonathan Cameron 0, 970b5e92c5SJonathan Cameron 0x0002, /* ADC Boost Control */ 980b5e92c5SJonathan Cameron 0, 990b5e92c5SJonathan Cameron 0x0002, /* Output Control */ 1000b5e92c5SJonathan Cameron 0x0000, /* Speaker Mixer Control */ 1010b5e92c5SJonathan Cameron 0, 1020b5e92c5SJonathan Cameron 0, 1030b5e92c5SJonathan Cameron 0, 1040b5e92c5SJonathan Cameron 0x0079, /* Speaker Volume */ 1050b5e92c5SJonathan Cameron 0, 1060b5e92c5SJonathan Cameron 0x0000, /* Mono Mixer Control */ 1070b5e92c5SJonathan Cameron }; 1080b5e92c5SJonathan Cameron 1090b5e92c5SJonathan Cameron static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; 1100b5e92c5SJonathan Cameron static const struct soc_enum wm8940_adc_companding_enum 1110b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding); 1120b5e92c5SJonathan Cameron static const struct soc_enum wm8940_dac_companding_enum 1130b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding); 1140b5e92c5SJonathan Cameron 1150b5e92c5SJonathan Cameron static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"}; 1160b5e92c5SJonathan Cameron static const struct soc_enum wm8940_alc_mode_enum 1170b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text); 1180b5e92c5SJonathan Cameron 1190b5e92c5SJonathan Cameron static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"}; 1200b5e92c5SJonathan Cameron static const struct soc_enum wm8940_mic_bias_level_enum 1210b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text); 1220b5e92c5SJonathan Cameron 1230b5e92c5SJonathan Cameron static const char *wm8940_filter_mode_text[] = {"Audio", "Application"}; 1240b5e92c5SJonathan Cameron static const struct soc_enum wm8940_filter_mode_enum 1250b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text); 1260b5e92c5SJonathan Cameron 1276be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); 1286be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); 1296be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0); 1306be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0); 1316be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0); 1326be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0); 1336be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0); 1346be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0); 1356be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1); 1366be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0); 1370b5e92c5SJonathan Cameron 1380b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_snd_controls[] = { 1390b5e92c5SJonathan Cameron SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL, 1400b5e92c5SJonathan Cameron 6, 1, 0), 1410b5e92c5SJonathan Cameron SOC_ENUM("DAC Companding", wm8940_dac_companding_enum), 1420b5e92c5SJonathan Cameron SOC_ENUM("ADC Companding", wm8940_adc_companding_enum), 1430b5e92c5SJonathan Cameron 1440b5e92c5SJonathan Cameron SOC_ENUM("ALC Mode", wm8940_alc_mode_enum), 1450b5e92c5SJonathan Cameron SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0), 1460b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1, 1470b5e92c5SJonathan Cameron 3, 7, 1, wm8940_alc_max_tlv), 1480b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1, 1490b5e92c5SJonathan Cameron 0, 7, 0, wm8940_alc_min_tlv), 1500b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2, 1510b5e92c5SJonathan Cameron 0, 14, 0, wm8940_alc_tar_tlv), 1520b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0), 1530b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0), 1540b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0), 1550b5e92c5SJonathan Cameron SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0), 1560b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE, 1570b5e92c5SJonathan Cameron 3, 1, 0), 1580b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE, 1590b5e92c5SJonathan Cameron 0, 7, 0), 1600b5e92c5SJonathan Cameron 1610b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0), 1620b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0), 1630b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0), 1640b5e92c5SJonathan Cameron SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2, 1650b5e92c5SJonathan Cameron 4, 9, 1, wm8940_lim_thresh_tlv), 1660b5e92c5SJonathan Cameron SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2, 1670b5e92c5SJonathan Cameron 0, 12, 0, wm8940_lim_boost_tlv), 1680b5e92c5SJonathan Cameron 1690b5e92c5SJonathan Cameron SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0), 1700b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN, 1710b5e92c5SJonathan Cameron 0, 63, 0, wm8940_pga_vol_tlv), 1720b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL, 1730b5e92c5SJonathan Cameron 0, 255, 0, wm8940_adc_tlv), 1740b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL, 1750b5e92c5SJonathan Cameron 0, 255, 0, wm8940_adc_tlv), 1760b5e92c5SJonathan Cameron SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum), 1770b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST, 1780b5e92c5SJonathan Cameron 8, 1, 0, wm8940_capture_boost_vol_tlv), 1790b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL, 1800b5e92c5SJonathan Cameron 0, 63, 0, wm8940_spk_vol_tlv), 1810b5e92c5SJonathan Cameron SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL, 6, 1, 1), 1820b5e92c5SJonathan Cameron 1830b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL, 1840b5e92c5SJonathan Cameron 8, 1, 1, wm8940_att_tlv), 1850b5e92c5SJonathan Cameron SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0), 1860b5e92c5SJonathan Cameron 1870b5e92c5SJonathan Cameron SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1), 1880b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX, 1890b5e92c5SJonathan Cameron 7, 1, 1, wm8940_att_tlv), 1900b5e92c5SJonathan Cameron 1910b5e92c5SJonathan Cameron SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0), 1920b5e92c5SJonathan Cameron SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum), 1930b5e92c5SJonathan Cameron SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0), 1940b5e92c5SJonathan Cameron SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0), 1950b5e92c5SJonathan Cameron SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0), 1960b5e92c5SJonathan Cameron SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0), 1970b5e92c5SJonathan Cameron SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0), 1980b5e92c5SJonathan Cameron }; 1990b5e92c5SJonathan Cameron 2000b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = { 2010b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0), 2020b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0), 2030b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0), 2040b5e92c5SJonathan Cameron }; 2050b5e92c5SJonathan Cameron 2060b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = { 2070b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0), 2080b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0), 2090b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0), 2100b5e92c5SJonathan Cameron }; 2110b5e92c5SJonathan Cameron 2126be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1); 2130b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_input_boost_controls[] = { 2140b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1), 2150b5e92c5SJonathan Cameron SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST, 2160b5e92c5SJonathan Cameron 0, 7, 0, wm8940_boost_vol_tlv), 2170b5e92c5SJonathan Cameron SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST, 2180b5e92c5SJonathan Cameron 4, 7, 0, wm8940_boost_vol_tlv), 2190b5e92c5SJonathan Cameron }; 2200b5e92c5SJonathan Cameron 2210b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_micpga_controls[] = { 2220b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0), 2230b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0), 2240b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0), 2250b5e92c5SJonathan Cameron }; 2260b5e92c5SJonathan Cameron 2270b5e92c5SJonathan Cameron static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = { 2280b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0, 2290b5e92c5SJonathan Cameron &wm8940_speaker_mixer_controls[0], 2300b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_speaker_mixer_controls)), 2310b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0, 2320b5e92c5SJonathan Cameron &wm8940_mono_mixer_controls[0], 2330b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_mono_mixer_controls)), 2340b5e92c5SJonathan Cameron SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0), 2350b5e92c5SJonathan Cameron 2360b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0), 2370b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0), 2380b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0), 2390b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("MONOOUT"), 2400b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("SPKOUTP"), 2410b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("SPKOUTN"), 2420b5e92c5SJonathan Cameron 2430b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0), 2440b5e92c5SJonathan Cameron SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0), 2450b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0, 2460b5e92c5SJonathan Cameron &wm8940_micpga_controls[0], 2470b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_micpga_controls)), 2480b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0, 2490b5e92c5SJonathan Cameron &wm8940_input_boost_controls[0], 2500b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_input_boost_controls)), 2510b5e92c5SJonathan Cameron SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0), 2520b5e92c5SJonathan Cameron 2530b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("MICN"), 2540b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("MICP"), 2550b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("AUX"), 2560b5e92c5SJonathan Cameron }; 2570b5e92c5SJonathan Cameron 2580b5e92c5SJonathan Cameron static const struct snd_soc_dapm_route audio_map[] = { 2590b5e92c5SJonathan Cameron /* Mono output mixer */ 2600b5e92c5SJonathan Cameron {"Mono Mixer", "PCM Playback Switch", "DAC"}, 2610b5e92c5SJonathan Cameron {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 2620b5e92c5SJonathan Cameron {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, 2630b5e92c5SJonathan Cameron 2640b5e92c5SJonathan Cameron /* Speaker output mixer */ 2650b5e92c5SJonathan Cameron {"Speaker Mixer", "PCM Playback Switch", "DAC"}, 2660b5e92c5SJonathan Cameron {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, 2670b5e92c5SJonathan Cameron {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, 2680b5e92c5SJonathan Cameron 2690b5e92c5SJonathan Cameron /* Outputs */ 2700b5e92c5SJonathan Cameron {"Mono Out", NULL, "Mono Mixer"}, 2710b5e92c5SJonathan Cameron {"MONOOUT", NULL, "Mono Out"}, 2720b5e92c5SJonathan Cameron {"SpkN Out", NULL, "Speaker Mixer"}, 2730b5e92c5SJonathan Cameron {"SpkP Out", NULL, "Speaker Mixer"}, 2740b5e92c5SJonathan Cameron {"SPKOUTN", NULL, "SpkN Out"}, 2750b5e92c5SJonathan Cameron {"SPKOUTP", NULL, "SpkP Out"}, 2760b5e92c5SJonathan Cameron 2770b5e92c5SJonathan Cameron /* Microphone PGA */ 2780b5e92c5SJonathan Cameron {"Mic PGA", "MICN Switch", "MICN"}, 2790b5e92c5SJonathan Cameron {"Mic PGA", "MICP Switch", "MICP"}, 2800b5e92c5SJonathan Cameron {"Mic PGA", "AUX Switch", "AUX"}, 2810b5e92c5SJonathan Cameron 2820b5e92c5SJonathan Cameron /* Boost Mixer */ 2830b5e92c5SJonathan Cameron {"Boost Mixer", "Mic PGA Switch", "Mic PGA"}, 2840b5e92c5SJonathan Cameron {"Boost Mixer", "Mic Volume", "MICP"}, 2850b5e92c5SJonathan Cameron {"Boost Mixer", "Aux Volume", "Aux Input"}, 2860b5e92c5SJonathan Cameron 2870b5e92c5SJonathan Cameron {"ADC", NULL, "Boost Mixer"}, 2880b5e92c5SJonathan Cameron }; 2890b5e92c5SJonathan Cameron 2900b5e92c5SJonathan Cameron static int wm8940_add_widgets(struct snd_soc_codec *codec) 2910b5e92c5SJonathan Cameron { 2920b5e92c5SJonathan Cameron int ret; 2930b5e92c5SJonathan Cameron 2940b5e92c5SJonathan Cameron ret = snd_soc_dapm_new_controls(codec, wm8940_dapm_widgets, 2950b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_dapm_widgets)); 2960b5e92c5SJonathan Cameron if (ret) 2970b5e92c5SJonathan Cameron goto error_ret; 2980b5e92c5SJonathan Cameron ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 2990b5e92c5SJonathan Cameron if (ret) 3000b5e92c5SJonathan Cameron goto error_ret; 3010b5e92c5SJonathan Cameron ret = snd_soc_dapm_new_widgets(codec); 3020b5e92c5SJonathan Cameron 3030b5e92c5SJonathan Cameron error_ret: 3040b5e92c5SJonathan Cameron return ret; 3050b5e92c5SJonathan Cameron } 3060b5e92c5SJonathan Cameron 307*8d50e447SMark Brown #define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0); 3080b5e92c5SJonathan Cameron 3090b5e92c5SJonathan Cameron static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, 3100b5e92c5SJonathan Cameron unsigned int fmt) 3110b5e92c5SJonathan Cameron { 3120b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 313*8d50e447SMark Brown u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67; 314*8d50e447SMark Brown u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe; 3150b5e92c5SJonathan Cameron 3160b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3170b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_CBM_CFM: 3180b5e92c5SJonathan Cameron clk |= 1; 3190b5e92c5SJonathan Cameron break; 3200b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_CBS_CFS: 3210b5e92c5SJonathan Cameron break; 3220b5e92c5SJonathan Cameron default: 3230b5e92c5SJonathan Cameron return -EINVAL; 3240b5e92c5SJonathan Cameron } 325*8d50e447SMark Brown snd_soc_write(codec, WM8940_CLOCK, clk); 3260b5e92c5SJonathan Cameron 3270b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 3280b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_I2S: 3290b5e92c5SJonathan Cameron iface |= (2 << 3); 3300b5e92c5SJonathan Cameron break; 3310b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_LEFT_J: 3320b5e92c5SJonathan Cameron iface |= (1 << 3); 3330b5e92c5SJonathan Cameron break; 3340b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_RIGHT_J: 3350b5e92c5SJonathan Cameron break; 3360b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_DSP_A: 3370b5e92c5SJonathan Cameron iface |= (3 << 3); 3380b5e92c5SJonathan Cameron break; 3390b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_DSP_B: 3400b5e92c5SJonathan Cameron iface |= (3 << 3) | (1 << 7); 3410b5e92c5SJonathan Cameron break; 3420b5e92c5SJonathan Cameron } 3430b5e92c5SJonathan Cameron 3440b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 3450b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_NB_NF: 3460b5e92c5SJonathan Cameron break; 3470b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_NB_IF: 3480b5e92c5SJonathan Cameron iface |= (1 << 7); 3490b5e92c5SJonathan Cameron break; 3500b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_IB_NF: 3510b5e92c5SJonathan Cameron iface |= (1 << 8); 3520b5e92c5SJonathan Cameron break; 3530b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_IB_IF: 3540b5e92c5SJonathan Cameron iface |= (1 << 8) | (1 << 7); 3550b5e92c5SJonathan Cameron break; 3560b5e92c5SJonathan Cameron } 3570b5e92c5SJonathan Cameron 358*8d50e447SMark Brown snd_soc_write(codec, WM8940_IFACE, iface); 3590b5e92c5SJonathan Cameron 3600b5e92c5SJonathan Cameron return 0; 3610b5e92c5SJonathan Cameron } 3620b5e92c5SJonathan Cameron 3630b5e92c5SJonathan Cameron static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream, 3640b5e92c5SJonathan Cameron struct snd_pcm_hw_params *params, 3650b5e92c5SJonathan Cameron struct snd_soc_dai *dai) 3660b5e92c5SJonathan Cameron { 3670b5e92c5SJonathan Cameron struct snd_soc_pcm_runtime *rtd = substream->private_data; 3680b5e92c5SJonathan Cameron struct snd_soc_device *socdev = rtd->socdev; 3690b5e92c5SJonathan Cameron struct snd_soc_codec *codec = socdev->card->codec; 370*8d50e447SMark Brown u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F; 371*8d50e447SMark Brown u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1; 372*8d50e447SMark Brown u16 companding = snd_soc_read(codec, 3730b5e92c5SJonathan Cameron WM8940_COMPANDINGCTL) & 0xFFDF; 3740b5e92c5SJonathan Cameron int ret; 3750b5e92c5SJonathan Cameron 3760b5e92c5SJonathan Cameron /* LoutR control */ 3770b5e92c5SJonathan Cameron if (substream->stream == SNDRV_PCM_STREAM_CAPTURE 3780b5e92c5SJonathan Cameron && params_channels(params) == 2) 3790b5e92c5SJonathan Cameron iface |= (1 << 9); 3800b5e92c5SJonathan Cameron 3810b5e92c5SJonathan Cameron switch (params_rate(params)) { 3820b5e92c5SJonathan Cameron case SNDRV_PCM_RATE_8000: 3830b5e92c5SJonathan Cameron addcntrl |= (0x5 << 1); 3840b5e92c5SJonathan Cameron break; 3850b5e92c5SJonathan Cameron case SNDRV_PCM_RATE_11025: 3860b5e92c5SJonathan Cameron addcntrl |= (0x4 << 1); 3870b5e92c5SJonathan Cameron break; 3880b5e92c5SJonathan Cameron case SNDRV_PCM_RATE_16000: 3890b5e92c5SJonathan Cameron addcntrl |= (0x3 << 1); 3900b5e92c5SJonathan Cameron break; 3910b5e92c5SJonathan Cameron case SNDRV_PCM_RATE_22050: 3920b5e92c5SJonathan Cameron addcntrl |= (0x2 << 1); 3930b5e92c5SJonathan Cameron break; 3940b5e92c5SJonathan Cameron case SNDRV_PCM_RATE_32000: 3950b5e92c5SJonathan Cameron addcntrl |= (0x1 << 1); 3960b5e92c5SJonathan Cameron break; 3970b5e92c5SJonathan Cameron case SNDRV_PCM_RATE_44100: 3980b5e92c5SJonathan Cameron case SNDRV_PCM_RATE_48000: 3990b5e92c5SJonathan Cameron break; 4000b5e92c5SJonathan Cameron } 401*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl); 4020b5e92c5SJonathan Cameron if (ret) 4030b5e92c5SJonathan Cameron goto error_ret; 4040b5e92c5SJonathan Cameron 4050b5e92c5SJonathan Cameron switch (params_format(params)) { 4060b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S8: 4070b5e92c5SJonathan Cameron companding = companding | (1 << 5); 4080b5e92c5SJonathan Cameron break; 4090b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S16_LE: 4100b5e92c5SJonathan Cameron break; 4110b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S20_3LE: 4120b5e92c5SJonathan Cameron iface |= (1 << 5); 4130b5e92c5SJonathan Cameron break; 4140b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S24_LE: 4150b5e92c5SJonathan Cameron iface |= (2 << 5); 4160b5e92c5SJonathan Cameron break; 4170b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S32_LE: 4180b5e92c5SJonathan Cameron iface |= (3 << 5); 4190b5e92c5SJonathan Cameron break; 4200b5e92c5SJonathan Cameron } 421*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding); 4220b5e92c5SJonathan Cameron if (ret) 4230b5e92c5SJonathan Cameron goto error_ret; 424*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_IFACE, iface); 4250b5e92c5SJonathan Cameron 4260b5e92c5SJonathan Cameron error_ret: 4270b5e92c5SJonathan Cameron return ret; 4280b5e92c5SJonathan Cameron } 4290b5e92c5SJonathan Cameron 4300b5e92c5SJonathan Cameron static int wm8940_mute(struct snd_soc_dai *dai, int mute) 4310b5e92c5SJonathan Cameron { 4320b5e92c5SJonathan Cameron struct snd_soc_codec *codec = dai->codec; 433*8d50e447SMark Brown u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf; 4340b5e92c5SJonathan Cameron 4350b5e92c5SJonathan Cameron if (mute) 4360b5e92c5SJonathan Cameron mute_reg |= 0x40; 4370b5e92c5SJonathan Cameron 438*8d50e447SMark Brown return snd_soc_write(codec, WM8940_DAC, mute_reg); 4390b5e92c5SJonathan Cameron } 4400b5e92c5SJonathan Cameron 4410b5e92c5SJonathan Cameron static int wm8940_set_bias_level(struct snd_soc_codec *codec, 4420b5e92c5SJonathan Cameron enum snd_soc_bias_level level) 4430b5e92c5SJonathan Cameron { 4440b5e92c5SJonathan Cameron u16 val; 445*8d50e447SMark Brown u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0; 4460b5e92c5SJonathan Cameron int ret = 0; 4470b5e92c5SJonathan Cameron 4480b5e92c5SJonathan Cameron switch (level) { 4490b5e92c5SJonathan Cameron case SND_SOC_BIAS_ON: 4500b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4510b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 4520b5e92c5SJonathan Cameron /* Enable thermal shutdown */ 453*8d50e447SMark Brown val = snd_soc_read(codec, WM8940_OUTPUTCTL); 454*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2); 4550b5e92c5SJonathan Cameron if (ret) 4560b5e92c5SJonathan Cameron break; 4570b5e92c5SJonathan Cameron /* set vmid to 75k */ 458*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1); 4590b5e92c5SJonathan Cameron break; 4600b5e92c5SJonathan Cameron case SND_SOC_BIAS_PREPARE: 4610b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4620b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 463*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1); 4640b5e92c5SJonathan Cameron break; 4650b5e92c5SJonathan Cameron case SND_SOC_BIAS_STANDBY: 4660b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4670b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 4680b5e92c5SJonathan Cameron /* set vmid to 300k for standby */ 469*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2); 4700b5e92c5SJonathan Cameron break; 4710b5e92c5SJonathan Cameron case SND_SOC_BIAS_OFF: 472*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg); 4730b5e92c5SJonathan Cameron break; 4740b5e92c5SJonathan Cameron } 4750b5e92c5SJonathan Cameron 4760b5e92c5SJonathan Cameron return ret; 4770b5e92c5SJonathan Cameron } 4780b5e92c5SJonathan Cameron 4790b5e92c5SJonathan Cameron struct pll_ { 4800b5e92c5SJonathan Cameron unsigned int pre_scale:2; 4810b5e92c5SJonathan Cameron unsigned int n:4; 4820b5e92c5SJonathan Cameron unsigned int k; 4830b5e92c5SJonathan Cameron }; 4840b5e92c5SJonathan Cameron 4850b5e92c5SJonathan Cameron static struct pll_ pll_div; 4860b5e92c5SJonathan Cameron 4870b5e92c5SJonathan Cameron /* The size in bits of the pll divide multiplied by 10 4880b5e92c5SJonathan Cameron * to allow rounding later */ 4890b5e92c5SJonathan Cameron #define FIXED_PLL_SIZE ((1 << 24) * 10) 4900b5e92c5SJonathan Cameron static void pll_factors(unsigned int target, unsigned int source) 4910b5e92c5SJonathan Cameron { 4920b5e92c5SJonathan Cameron unsigned long long Kpart; 4930b5e92c5SJonathan Cameron unsigned int K, Ndiv, Nmod; 4940b5e92c5SJonathan Cameron /* The left shift ist to avoid accuracy loss when right shifting */ 4950b5e92c5SJonathan Cameron Ndiv = target / source; 4960b5e92c5SJonathan Cameron 4970b5e92c5SJonathan Cameron if (Ndiv > 12) { 4980b5e92c5SJonathan Cameron source <<= 1; 4990b5e92c5SJonathan Cameron /* Multiply by 2 */ 5000b5e92c5SJonathan Cameron pll_div.pre_scale = 0; 5010b5e92c5SJonathan Cameron Ndiv = target / source; 5020b5e92c5SJonathan Cameron } else if (Ndiv < 3) { 5030b5e92c5SJonathan Cameron source >>= 2; 5040b5e92c5SJonathan Cameron /* Divide by 4 */ 5050b5e92c5SJonathan Cameron pll_div.pre_scale = 3; 5060b5e92c5SJonathan Cameron Ndiv = target / source; 5070b5e92c5SJonathan Cameron } else if (Ndiv < 6) { 5080b5e92c5SJonathan Cameron source >>= 1; 5090b5e92c5SJonathan Cameron /* divide by 2 */ 5100b5e92c5SJonathan Cameron pll_div.pre_scale = 2; 5110b5e92c5SJonathan Cameron Ndiv = target / source; 5120b5e92c5SJonathan Cameron } else 5130b5e92c5SJonathan Cameron pll_div.pre_scale = 1; 5140b5e92c5SJonathan Cameron 5150b5e92c5SJonathan Cameron if ((Ndiv < 6) || (Ndiv > 12)) 5160b5e92c5SJonathan Cameron printk(KERN_WARNING 5170b5e92c5SJonathan Cameron "WM8940 N value %d outwith recommended range!d\n", 5180b5e92c5SJonathan Cameron Ndiv); 5190b5e92c5SJonathan Cameron 5200b5e92c5SJonathan Cameron pll_div.n = Ndiv; 5210b5e92c5SJonathan Cameron Nmod = target % source; 5220b5e92c5SJonathan Cameron Kpart = FIXED_PLL_SIZE * (long long)Nmod; 5230b5e92c5SJonathan Cameron 5240b5e92c5SJonathan Cameron do_div(Kpart, source); 5250b5e92c5SJonathan Cameron 5260b5e92c5SJonathan Cameron K = Kpart & 0xFFFFFFFF; 5270b5e92c5SJonathan Cameron 5280b5e92c5SJonathan Cameron /* Check if we need to round */ 5290b5e92c5SJonathan Cameron if ((K % 10) >= 5) 5300b5e92c5SJonathan Cameron K += 5; 5310b5e92c5SJonathan Cameron 5320b5e92c5SJonathan Cameron /* Move down to proper range now rounding is done */ 5330b5e92c5SJonathan Cameron K /= 10; 5340b5e92c5SJonathan Cameron 5350b5e92c5SJonathan Cameron pll_div.k = K; 5360b5e92c5SJonathan Cameron } 5370b5e92c5SJonathan Cameron 5380b5e92c5SJonathan Cameron /* Untested at the moment */ 5390b5e92c5SJonathan Cameron static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, 5400b5e92c5SJonathan Cameron int pll_id, unsigned int freq_in, unsigned int freq_out) 5410b5e92c5SJonathan Cameron { 5420b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 5430b5e92c5SJonathan Cameron u16 reg; 5440b5e92c5SJonathan Cameron 5450b5e92c5SJonathan Cameron /* Turn off PLL */ 546*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_POWER1); 547*8d50e447SMark Brown snd_soc_write(codec, WM8940_POWER1, reg & 0x1df); 5480b5e92c5SJonathan Cameron 5490b5e92c5SJonathan Cameron if (freq_in == 0 || freq_out == 0) { 5500b5e92c5SJonathan Cameron /* Clock CODEC directly from MCLK */ 551*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_CLOCK); 552*8d50e447SMark Brown snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff); 5530b5e92c5SJonathan Cameron /* Pll power down */ 554*8d50e447SMark Brown snd_soc_write(codec, WM8940_PLLN, (1 << 7)); 5550b5e92c5SJonathan Cameron return 0; 5560b5e92c5SJonathan Cameron } 5570b5e92c5SJonathan Cameron 5580b5e92c5SJonathan Cameron /* Pll is followed by a frequency divide by 4 */ 5590b5e92c5SJonathan Cameron pll_factors(freq_out*4, freq_in); 5600b5e92c5SJonathan Cameron if (pll_div.k) 561*8d50e447SMark Brown snd_soc_write(codec, WM8940_PLLN, 5620b5e92c5SJonathan Cameron (pll_div.pre_scale << 4) | pll_div.n | (1 << 6)); 5630b5e92c5SJonathan Cameron else /* No factional component */ 564*8d50e447SMark Brown snd_soc_write(codec, WM8940_PLLN, 5650b5e92c5SJonathan Cameron (pll_div.pre_scale << 4) | pll_div.n); 566*8d50e447SMark Brown snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18); 567*8d50e447SMark Brown snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff); 568*8d50e447SMark Brown snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff); 5690b5e92c5SJonathan Cameron /* Enable the PLL */ 570*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_POWER1); 571*8d50e447SMark Brown snd_soc_write(codec, WM8940_POWER1, reg | 0x020); 5720b5e92c5SJonathan Cameron 5730b5e92c5SJonathan Cameron /* Run CODEC from PLL instead of MCLK */ 574*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_CLOCK); 575*8d50e447SMark Brown snd_soc_write(codec, WM8940_CLOCK, reg | 0x100); 5760b5e92c5SJonathan Cameron 5770b5e92c5SJonathan Cameron return 0; 5780b5e92c5SJonathan Cameron } 5790b5e92c5SJonathan Cameron 5800b5e92c5SJonathan Cameron static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai, 5810b5e92c5SJonathan Cameron int clk_id, unsigned int freq, int dir) 5820b5e92c5SJonathan Cameron { 5830b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 5840b5e92c5SJonathan Cameron struct wm8940_priv *wm8940 = codec->private_data; 5850b5e92c5SJonathan Cameron 5860b5e92c5SJonathan Cameron switch (freq) { 5870b5e92c5SJonathan Cameron case 11289600: 5880b5e92c5SJonathan Cameron case 12000000: 5890b5e92c5SJonathan Cameron case 12288000: 5900b5e92c5SJonathan Cameron case 16934400: 5910b5e92c5SJonathan Cameron case 18432000: 5920b5e92c5SJonathan Cameron wm8940->sysclk = freq; 5930b5e92c5SJonathan Cameron return 0; 5940b5e92c5SJonathan Cameron } 5950b5e92c5SJonathan Cameron return -EINVAL; 5960b5e92c5SJonathan Cameron } 5970b5e92c5SJonathan Cameron 5980b5e92c5SJonathan Cameron static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 5990b5e92c5SJonathan Cameron int div_id, int div) 6000b5e92c5SJonathan Cameron { 6010b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 6020b5e92c5SJonathan Cameron u16 reg; 6030b5e92c5SJonathan Cameron int ret = 0; 6040b5e92c5SJonathan Cameron 6050b5e92c5SJonathan Cameron switch (div_id) { 6060b5e92c5SJonathan Cameron case WM8940_BCLKDIV: 607*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFEF3; 608*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2)); 6090b5e92c5SJonathan Cameron break; 6100b5e92c5SJonathan Cameron case WM8940_MCLKDIV: 611*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F; 612*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5)); 6130b5e92c5SJonathan Cameron break; 6140b5e92c5SJonathan Cameron case WM8940_OPCLKDIV: 615*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFCF; 616*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_ADDCNTRL, reg | (div << 4)); 6170b5e92c5SJonathan Cameron break; 6180b5e92c5SJonathan Cameron } 6190b5e92c5SJonathan Cameron return ret; 6200b5e92c5SJonathan Cameron } 6210b5e92c5SJonathan Cameron 6220b5e92c5SJonathan Cameron #define WM8940_RATES SNDRV_PCM_RATE_8000_48000 6230b5e92c5SJonathan Cameron 6240b5e92c5SJonathan Cameron #define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 6250b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S16_LE | \ 6260b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S20_3LE | \ 6270b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S24_LE | \ 6280b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S32_LE) 6290b5e92c5SJonathan Cameron 6300b5e92c5SJonathan Cameron static struct snd_soc_dai_ops wm8940_dai_ops = { 6310b5e92c5SJonathan Cameron .hw_params = wm8940_i2s_hw_params, 6320b5e92c5SJonathan Cameron .set_sysclk = wm8940_set_dai_sysclk, 6330b5e92c5SJonathan Cameron .digital_mute = wm8940_mute, 6340b5e92c5SJonathan Cameron .set_fmt = wm8940_set_dai_fmt, 6350b5e92c5SJonathan Cameron .set_clkdiv = wm8940_set_dai_clkdiv, 6360b5e92c5SJonathan Cameron .set_pll = wm8940_set_dai_pll, 6370b5e92c5SJonathan Cameron }; 6380b5e92c5SJonathan Cameron 6390b5e92c5SJonathan Cameron struct snd_soc_dai wm8940_dai = { 6400b5e92c5SJonathan Cameron .name = "WM8940", 6410b5e92c5SJonathan Cameron .playback = { 6420b5e92c5SJonathan Cameron .stream_name = "Playback", 6430b5e92c5SJonathan Cameron .channels_min = 1, 6440b5e92c5SJonathan Cameron .channels_max = 2, 6450b5e92c5SJonathan Cameron .rates = WM8940_RATES, 6460b5e92c5SJonathan Cameron .formats = WM8940_FORMATS, 6470b5e92c5SJonathan Cameron }, 6480b5e92c5SJonathan Cameron .capture = { 6490b5e92c5SJonathan Cameron .stream_name = "Capture", 6500b5e92c5SJonathan Cameron .channels_min = 1, 6510b5e92c5SJonathan Cameron .channels_max = 2, 6520b5e92c5SJonathan Cameron .rates = WM8940_RATES, 6530b5e92c5SJonathan Cameron .formats = WM8940_FORMATS, 6540b5e92c5SJonathan Cameron }, 6550b5e92c5SJonathan Cameron .ops = &wm8940_dai_ops, 6560b5e92c5SJonathan Cameron .symmetric_rates = 1, 6570b5e92c5SJonathan Cameron }; 6580b5e92c5SJonathan Cameron EXPORT_SYMBOL_GPL(wm8940_dai); 6590b5e92c5SJonathan Cameron 6600b5e92c5SJonathan Cameron static int wm8940_suspend(struct platform_device *pdev, pm_message_t state) 6610b5e92c5SJonathan Cameron { 6620b5e92c5SJonathan Cameron struct snd_soc_device *socdev = platform_get_drvdata(pdev); 6630b5e92c5SJonathan Cameron struct snd_soc_codec *codec = socdev->card->codec; 6640b5e92c5SJonathan Cameron 6650b5e92c5SJonathan Cameron return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF); 6660b5e92c5SJonathan Cameron } 6670b5e92c5SJonathan Cameron 6680b5e92c5SJonathan Cameron static int wm8940_resume(struct platform_device *pdev) 6690b5e92c5SJonathan Cameron { 6700b5e92c5SJonathan Cameron struct snd_soc_device *socdev = platform_get_drvdata(pdev); 6710b5e92c5SJonathan Cameron struct snd_soc_codec *codec = socdev->card->codec; 6720b5e92c5SJonathan Cameron int i; 6730b5e92c5SJonathan Cameron int ret; 6740b5e92c5SJonathan Cameron u8 data[3]; 6750b5e92c5SJonathan Cameron u16 *cache = codec->reg_cache; 6760b5e92c5SJonathan Cameron 6770b5e92c5SJonathan Cameron /* Sync reg_cache with the hardware 6780b5e92c5SJonathan Cameron * Could use auto incremented writes to speed this up 6790b5e92c5SJonathan Cameron */ 6800b5e92c5SJonathan Cameron for (i = 0; i < ARRAY_SIZE(wm8940_reg_defaults); i++) { 6810b5e92c5SJonathan Cameron data[0] = i; 6820b5e92c5SJonathan Cameron data[1] = (cache[i] & 0xFF00) >> 8; 6830b5e92c5SJonathan Cameron data[2] = cache[i] & 0x00FF; 6840b5e92c5SJonathan Cameron ret = codec->hw_write(codec->control_data, data, 3); 6850b5e92c5SJonathan Cameron if (ret < 0) 6860b5e92c5SJonathan Cameron goto error_ret; 6870b5e92c5SJonathan Cameron else if (ret != 3) { 6880b5e92c5SJonathan Cameron ret = -EIO; 6890b5e92c5SJonathan Cameron goto error_ret; 6900b5e92c5SJonathan Cameron } 6910b5e92c5SJonathan Cameron } 6920b5e92c5SJonathan Cameron ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 6930b5e92c5SJonathan Cameron if (ret) 6940b5e92c5SJonathan Cameron goto error_ret; 6950b5e92c5SJonathan Cameron ret = wm8940_set_bias_level(codec, codec->suspend_bias_level); 6960b5e92c5SJonathan Cameron 6970b5e92c5SJonathan Cameron error_ret: 6980b5e92c5SJonathan Cameron return ret; 6990b5e92c5SJonathan Cameron } 7000b5e92c5SJonathan Cameron 7010b5e92c5SJonathan Cameron static struct snd_soc_codec *wm8940_codec; 7020b5e92c5SJonathan Cameron 7030b5e92c5SJonathan Cameron static int wm8940_probe(struct platform_device *pdev) 7040b5e92c5SJonathan Cameron { 7050b5e92c5SJonathan Cameron struct snd_soc_device *socdev = platform_get_drvdata(pdev); 7060b5e92c5SJonathan Cameron struct snd_soc_codec *codec; 7070b5e92c5SJonathan Cameron 7080b5e92c5SJonathan Cameron int ret = 0; 7090b5e92c5SJonathan Cameron 7100b5e92c5SJonathan Cameron if (wm8940_codec == NULL) { 7110b5e92c5SJonathan Cameron dev_err(&pdev->dev, "Codec device not registered\n"); 7120b5e92c5SJonathan Cameron return -ENODEV; 7130b5e92c5SJonathan Cameron } 7140b5e92c5SJonathan Cameron 7150b5e92c5SJonathan Cameron socdev->card->codec = wm8940_codec; 7160b5e92c5SJonathan Cameron codec = wm8940_codec; 7170b5e92c5SJonathan Cameron 7180b5e92c5SJonathan Cameron mutex_init(&codec->mutex); 7190b5e92c5SJonathan Cameron /* register pcms */ 7200b5e92c5SJonathan Cameron ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 7210b5e92c5SJonathan Cameron if (ret < 0) { 7220b5e92c5SJonathan Cameron dev_err(codec->dev, "failed to create pcms: %d\n", ret); 7230b5e92c5SJonathan Cameron goto pcm_err; 7240b5e92c5SJonathan Cameron } 7250b5e92c5SJonathan Cameron 7260b5e92c5SJonathan Cameron ret = snd_soc_add_controls(codec, wm8940_snd_controls, 7270b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_snd_controls)); 7280b5e92c5SJonathan Cameron if (ret) 7290b5e92c5SJonathan Cameron goto error_free_pcms; 7300b5e92c5SJonathan Cameron ret = wm8940_add_widgets(codec); 7310b5e92c5SJonathan Cameron if (ret) 7320b5e92c5SJonathan Cameron goto error_free_pcms; 7330b5e92c5SJonathan Cameron 7340b5e92c5SJonathan Cameron ret = snd_soc_init_card(socdev); 7350b5e92c5SJonathan Cameron if (ret < 0) { 7360b5e92c5SJonathan Cameron dev_err(codec->dev, "failed to register card: %d\n", ret); 7370b5e92c5SJonathan Cameron goto error_free_pcms; 7380b5e92c5SJonathan Cameron } 7390b5e92c5SJonathan Cameron 7400b5e92c5SJonathan Cameron return ret; 7410b5e92c5SJonathan Cameron 7420b5e92c5SJonathan Cameron error_free_pcms: 7430b5e92c5SJonathan Cameron snd_soc_free_pcms(socdev); 7440b5e92c5SJonathan Cameron snd_soc_dapm_free(socdev); 7450b5e92c5SJonathan Cameron pcm_err: 7460b5e92c5SJonathan Cameron return ret; 7470b5e92c5SJonathan Cameron } 7480b5e92c5SJonathan Cameron 7490b5e92c5SJonathan Cameron static int wm8940_remove(struct platform_device *pdev) 7500b5e92c5SJonathan Cameron { 7510b5e92c5SJonathan Cameron struct snd_soc_device *socdev = platform_get_drvdata(pdev); 7520b5e92c5SJonathan Cameron 7530b5e92c5SJonathan Cameron snd_soc_free_pcms(socdev); 7540b5e92c5SJonathan Cameron snd_soc_dapm_free(socdev); 7550b5e92c5SJonathan Cameron 7560b5e92c5SJonathan Cameron return 0; 7570b5e92c5SJonathan Cameron } 7580b5e92c5SJonathan Cameron 7590b5e92c5SJonathan Cameron struct snd_soc_codec_device soc_codec_dev_wm8940 = { 7600b5e92c5SJonathan Cameron .probe = wm8940_probe, 7610b5e92c5SJonathan Cameron .remove = wm8940_remove, 7620b5e92c5SJonathan Cameron .suspend = wm8940_suspend, 7630b5e92c5SJonathan Cameron .resume = wm8940_resume, 7640b5e92c5SJonathan Cameron }; 7650b5e92c5SJonathan Cameron EXPORT_SYMBOL_GPL(soc_codec_dev_wm8940); 7660b5e92c5SJonathan Cameron 767*8d50e447SMark Brown static int wm8940_register(struct wm8940_priv *wm8940, 768*8d50e447SMark Brown enum snd_soc_control_type control) 7690b5e92c5SJonathan Cameron { 7700b5e92c5SJonathan Cameron struct wm8940_setup_data *pdata = wm8940->codec.dev->platform_data; 7710b5e92c5SJonathan Cameron struct snd_soc_codec *codec = &wm8940->codec; 7720b5e92c5SJonathan Cameron int ret; 7730b5e92c5SJonathan Cameron u16 reg; 7740b5e92c5SJonathan Cameron if (wm8940_codec) { 7750b5e92c5SJonathan Cameron dev_err(codec->dev, "Another WM8940 is registered\n"); 7760b5e92c5SJonathan Cameron return -EINVAL; 7770b5e92c5SJonathan Cameron } 7780b5e92c5SJonathan Cameron 7790b5e92c5SJonathan Cameron INIT_LIST_HEAD(&codec->dapm_widgets); 7800b5e92c5SJonathan Cameron INIT_LIST_HEAD(&codec->dapm_paths); 7810b5e92c5SJonathan Cameron 7820b5e92c5SJonathan Cameron codec->private_data = wm8940; 7830b5e92c5SJonathan Cameron codec->name = "WM8940"; 7840b5e92c5SJonathan Cameron codec->owner = THIS_MODULE; 7850b5e92c5SJonathan Cameron codec->bias_level = SND_SOC_BIAS_OFF; 7860b5e92c5SJonathan Cameron codec->set_bias_level = wm8940_set_bias_level; 7870b5e92c5SJonathan Cameron codec->dai = &wm8940_dai; 7880b5e92c5SJonathan Cameron codec->num_dai = 1; 7890b5e92c5SJonathan Cameron codec->reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults); 7900b5e92c5SJonathan Cameron codec->reg_cache = &wm8940->reg_cache; 7910b5e92c5SJonathan Cameron 792*8d50e447SMark Brown ret = snd_soc_codec_set_cache_io(codec, 8, 16, control); 793*8d50e447SMark Brown if (ret == 0) { 794*8d50e447SMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 795*8d50e447SMark Brown return ret; 796*8d50e447SMark Brown } 797*8d50e447SMark Brown 7980b5e92c5SJonathan Cameron memcpy(codec->reg_cache, wm8940_reg_defaults, 7990b5e92c5SJonathan Cameron sizeof(wm8940_reg_defaults)); 8000b5e92c5SJonathan Cameron 8010b5e92c5SJonathan Cameron ret = wm8940_reset(codec); 8020b5e92c5SJonathan Cameron if (ret < 0) { 8030b5e92c5SJonathan Cameron dev_err(codec->dev, "Failed to issue reset\n"); 8040b5e92c5SJonathan Cameron return ret; 8050b5e92c5SJonathan Cameron } 8060b5e92c5SJonathan Cameron 8070b5e92c5SJonathan Cameron wm8940_dai.dev = codec->dev; 8080b5e92c5SJonathan Cameron 8090b5e92c5SJonathan Cameron wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 8100b5e92c5SJonathan Cameron 811*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, 0x180); 8120b5e92c5SJonathan Cameron if (ret < 0) 8130b5e92c5SJonathan Cameron return ret; 8140b5e92c5SJonathan Cameron 8150b5e92c5SJonathan Cameron if (!pdata) 8160b5e92c5SJonathan Cameron dev_warn(codec->dev, "No platform data supplied\n"); 8170b5e92c5SJonathan Cameron else { 818*8d50e447SMark Brown reg = snd_soc_read(codec, WM8940_OUTPUTCTL); 819*8d50e447SMark Brown ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi); 8200b5e92c5SJonathan Cameron if (ret < 0) 8210b5e92c5SJonathan Cameron return ret; 8220b5e92c5SJonathan Cameron } 8230b5e92c5SJonathan Cameron 8240b5e92c5SJonathan Cameron 8250b5e92c5SJonathan Cameron wm8940_codec = codec; 8260b5e92c5SJonathan Cameron 8270b5e92c5SJonathan Cameron ret = snd_soc_register_codec(codec); 8280b5e92c5SJonathan Cameron if (ret) { 8290b5e92c5SJonathan Cameron dev_err(codec->dev, "Failed to register codec: %d\n", ret); 8300b5e92c5SJonathan Cameron return ret; 8310b5e92c5SJonathan Cameron } 8320b5e92c5SJonathan Cameron 8330b5e92c5SJonathan Cameron ret = snd_soc_register_dai(&wm8940_dai); 8340b5e92c5SJonathan Cameron if (ret) { 8350b5e92c5SJonathan Cameron dev_err(codec->dev, "Failed to register DAI: %d\n", ret); 8360b5e92c5SJonathan Cameron snd_soc_unregister_codec(codec); 8370b5e92c5SJonathan Cameron return ret; 8380b5e92c5SJonathan Cameron } 8390b5e92c5SJonathan Cameron 8400b5e92c5SJonathan Cameron return 0; 8410b5e92c5SJonathan Cameron } 8420b5e92c5SJonathan Cameron 8430b5e92c5SJonathan Cameron static void wm8940_unregister(struct wm8940_priv *wm8940) 8440b5e92c5SJonathan Cameron { 8450b5e92c5SJonathan Cameron wm8940_set_bias_level(&wm8940->codec, SND_SOC_BIAS_OFF); 8460b5e92c5SJonathan Cameron snd_soc_unregister_dai(&wm8940_dai); 8470b5e92c5SJonathan Cameron snd_soc_unregister_codec(&wm8940->codec); 8480b5e92c5SJonathan Cameron kfree(wm8940); 8490b5e92c5SJonathan Cameron wm8940_codec = NULL; 8500b5e92c5SJonathan Cameron } 8510b5e92c5SJonathan Cameron 8520b5e92c5SJonathan Cameron static int wm8940_i2c_probe(struct i2c_client *i2c, 8530b5e92c5SJonathan Cameron const struct i2c_device_id *id) 8540b5e92c5SJonathan Cameron { 8550b5e92c5SJonathan Cameron struct wm8940_priv *wm8940; 8560b5e92c5SJonathan Cameron struct snd_soc_codec *codec; 8570b5e92c5SJonathan Cameron 8580b5e92c5SJonathan Cameron wm8940 = kzalloc(sizeof *wm8940, GFP_KERNEL); 8590b5e92c5SJonathan Cameron if (wm8940 == NULL) 8600b5e92c5SJonathan Cameron return -ENOMEM; 8610b5e92c5SJonathan Cameron 8620b5e92c5SJonathan Cameron codec = &wm8940->codec; 8630b5e92c5SJonathan Cameron codec->hw_write = (hw_write_t)i2c_master_send; 8640b5e92c5SJonathan Cameron i2c_set_clientdata(i2c, wm8940); 8650b5e92c5SJonathan Cameron codec->control_data = i2c; 8660b5e92c5SJonathan Cameron codec->dev = &i2c->dev; 8670b5e92c5SJonathan Cameron 868*8d50e447SMark Brown return wm8940_register(wm8940, SND_SOC_I2C); 8690b5e92c5SJonathan Cameron } 8700b5e92c5SJonathan Cameron 8712baaec28STakashi Iwai static int __devexit wm8940_i2c_remove(struct i2c_client *client) 8720b5e92c5SJonathan Cameron { 8730b5e92c5SJonathan Cameron struct wm8940_priv *wm8940 = i2c_get_clientdata(client); 8740b5e92c5SJonathan Cameron 8750b5e92c5SJonathan Cameron wm8940_unregister(wm8940); 8760b5e92c5SJonathan Cameron 8770b5e92c5SJonathan Cameron return 0; 8780b5e92c5SJonathan Cameron } 8790b5e92c5SJonathan Cameron 880b3b50b3fSMark Brown #ifdef CONFIG_PM 881b3b50b3fSMark Brown static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg) 882b3b50b3fSMark Brown { 883b3b50b3fSMark Brown return snd_soc_suspend_device(&client->dev); 884b3b50b3fSMark Brown } 885b3b50b3fSMark Brown 886b3b50b3fSMark Brown static int wm8940_i2c_resume(struct i2c_client *client) 887b3b50b3fSMark Brown { 888b3b50b3fSMark Brown return snd_soc_resume_device(&client->dev); 889b3b50b3fSMark Brown } 890b3b50b3fSMark Brown #else 891b3b50b3fSMark Brown #define wm8940_i2c_suspend NULL 892b3b50b3fSMark Brown #define wm8940_i2c_resume NULL 893b3b50b3fSMark Brown #endif 894b3b50b3fSMark Brown 8950b5e92c5SJonathan Cameron static const struct i2c_device_id wm8940_i2c_id[] = { 8960b5e92c5SJonathan Cameron { "wm8940", 0 }, 8970b5e92c5SJonathan Cameron { } 8980b5e92c5SJonathan Cameron }; 8990b5e92c5SJonathan Cameron MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); 9000b5e92c5SJonathan Cameron 9010b5e92c5SJonathan Cameron static struct i2c_driver wm8940_i2c_driver = { 9020b5e92c5SJonathan Cameron .driver = { 9030b5e92c5SJonathan Cameron .name = "WM8940 I2C Codec", 9040b5e92c5SJonathan Cameron .owner = THIS_MODULE, 9050b5e92c5SJonathan Cameron }, 9060b5e92c5SJonathan Cameron .probe = wm8940_i2c_probe, 9070b5e92c5SJonathan Cameron .remove = __devexit_p(wm8940_i2c_remove), 908b3b50b3fSMark Brown .suspend = wm8940_i2c_suspend, 909b3b50b3fSMark Brown .resume = wm8940_i2c_resume, 9100b5e92c5SJonathan Cameron .id_table = wm8940_i2c_id, 9110b5e92c5SJonathan Cameron }; 9120b5e92c5SJonathan Cameron 9130b5e92c5SJonathan Cameron static int __init wm8940_modinit(void) 9140b5e92c5SJonathan Cameron { 9150b5e92c5SJonathan Cameron int ret; 9160b5e92c5SJonathan Cameron 9170b5e92c5SJonathan Cameron ret = i2c_add_driver(&wm8940_i2c_driver); 9180b5e92c5SJonathan Cameron if (ret) 9190b5e92c5SJonathan Cameron printk(KERN_ERR "Failed to register WM8940 I2C driver: %d\n", 9200b5e92c5SJonathan Cameron ret); 9210b5e92c5SJonathan Cameron return ret; 9220b5e92c5SJonathan Cameron } 9230b5e92c5SJonathan Cameron module_init(wm8940_modinit); 9240b5e92c5SJonathan Cameron 9250b5e92c5SJonathan Cameron static void __exit wm8940_exit(void) 9260b5e92c5SJonathan Cameron { 9270b5e92c5SJonathan Cameron i2c_del_driver(&wm8940_i2c_driver); 9280b5e92c5SJonathan Cameron } 9290b5e92c5SJonathan Cameron module_exit(wm8940_exit); 9300b5e92c5SJonathan Cameron 9310b5e92c5SJonathan Cameron MODULE_DESCRIPTION("ASoC WM8940 driver"); 9320b5e92c5SJonathan Cameron MODULE_AUTHOR("Jonathan Cameron"); 9330b5e92c5SJonathan Cameron MODULE_LICENSE("GPL"); 934