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> 335a0e3ad6STejun Heo #include <linux/slab.h> 340b5e92c5SJonathan Cameron #include <sound/core.h> 350b5e92c5SJonathan Cameron #include <sound/pcm.h> 360b5e92c5SJonathan Cameron #include <sound/pcm_params.h> 370b5e92c5SJonathan Cameron #include <sound/soc.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; 45f0fba2adSLiam Girdwood enum snd_soc_control_type control_type; 460b5e92c5SJonathan Cameron }; 470b5e92c5SJonathan Cameron 48788b6e8eSAxel Lin static int wm8940_volatile_register(struct snd_soc_codec *codec, 49788b6e8eSAxel Lin unsigned int reg) 50788b6e8eSAxel Lin { 51788b6e8eSAxel Lin switch (reg) { 52788b6e8eSAxel Lin case WM8940_SOFTRESET: 53788b6e8eSAxel Lin return 1; 54788b6e8eSAxel Lin default: 55788b6e8eSAxel Lin return 0; 56788b6e8eSAxel Lin } 57788b6e8eSAxel Lin } 58788b6e8eSAxel Lin 590b5e92c5SJonathan Cameron static u16 wm8940_reg_defaults[] = { 600b5e92c5SJonathan Cameron 0x8940, /* Soft Reset */ 610b5e92c5SJonathan Cameron 0x0000, /* Power 1 */ 620b5e92c5SJonathan Cameron 0x0000, /* Power 2 */ 630b5e92c5SJonathan Cameron 0x0000, /* Power 3 */ 640b5e92c5SJonathan Cameron 0x0010, /* Interface Control */ 650b5e92c5SJonathan Cameron 0x0000, /* Companding Control */ 660b5e92c5SJonathan Cameron 0x0140, /* Clock Control */ 670b5e92c5SJonathan Cameron 0x0000, /* Additional Controls */ 680b5e92c5SJonathan Cameron 0x0000, /* GPIO Control */ 690b5e92c5SJonathan Cameron 0x0002, /* Auto Increment Control */ 700b5e92c5SJonathan Cameron 0x0000, /* DAC Control */ 710b5e92c5SJonathan Cameron 0x00FF, /* DAC Volume */ 720b5e92c5SJonathan Cameron 0, 730b5e92c5SJonathan Cameron 0, 740b5e92c5SJonathan Cameron 0x0100, /* ADC Control */ 750b5e92c5SJonathan Cameron 0x00FF, /* ADC Volume */ 760b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 1 Control 1 */ 770b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 1 Control 2 */ 780b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 2 Control 1 */ 790b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 2 Control 2 */ 800b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 3 Control 1 */ 810b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 3 Control 2 */ 820b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 4 Control 1 */ 830b5e92c5SJonathan Cameron 0x0000, /* Notch Filter 4 Control 2 */ 840b5e92c5SJonathan Cameron 0x0032, /* DAC Limit Control 1 */ 850b5e92c5SJonathan Cameron 0x0000, /* DAC Limit Control 2 */ 860b5e92c5SJonathan Cameron 0, 870b5e92c5SJonathan Cameron 0, 880b5e92c5SJonathan Cameron 0, 890b5e92c5SJonathan Cameron 0, 900b5e92c5SJonathan Cameron 0, 910b5e92c5SJonathan Cameron 0, 920b5e92c5SJonathan Cameron 0x0038, /* ALC Control 1 */ 930b5e92c5SJonathan Cameron 0x000B, /* ALC Control 2 */ 940b5e92c5SJonathan Cameron 0x0032, /* ALC Control 3 */ 950b5e92c5SJonathan Cameron 0x0000, /* Noise Gate */ 960b5e92c5SJonathan Cameron 0x0041, /* PLLN */ 970b5e92c5SJonathan Cameron 0x000C, /* PLLK1 */ 980b5e92c5SJonathan Cameron 0x0093, /* PLLK2 */ 990b5e92c5SJonathan Cameron 0x00E9, /* PLLK3 */ 1000b5e92c5SJonathan Cameron 0, 1010b5e92c5SJonathan Cameron 0, 1020b5e92c5SJonathan Cameron 0x0030, /* ALC Control 4 */ 1030b5e92c5SJonathan Cameron 0, 1040b5e92c5SJonathan Cameron 0x0002, /* Input Control */ 1050b5e92c5SJonathan Cameron 0x0050, /* PGA Gain */ 1060b5e92c5SJonathan Cameron 0, 1070b5e92c5SJonathan Cameron 0x0002, /* ADC Boost Control */ 1080b5e92c5SJonathan Cameron 0, 1090b5e92c5SJonathan Cameron 0x0002, /* Output Control */ 1100b5e92c5SJonathan Cameron 0x0000, /* Speaker Mixer Control */ 1110b5e92c5SJonathan Cameron 0, 1120b5e92c5SJonathan Cameron 0, 1130b5e92c5SJonathan Cameron 0, 1140b5e92c5SJonathan Cameron 0x0079, /* Speaker Volume */ 1150b5e92c5SJonathan Cameron 0, 1160b5e92c5SJonathan Cameron 0x0000, /* Mono Mixer Control */ 1170b5e92c5SJonathan Cameron }; 1180b5e92c5SJonathan Cameron 1190b5e92c5SJonathan Cameron static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; 1200b5e92c5SJonathan Cameron static const struct soc_enum wm8940_adc_companding_enum 1210b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding); 1220b5e92c5SJonathan Cameron static const struct soc_enum wm8940_dac_companding_enum 1230b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding); 1240b5e92c5SJonathan Cameron 1250b5e92c5SJonathan Cameron static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"}; 1260b5e92c5SJonathan Cameron static const struct soc_enum wm8940_alc_mode_enum 1270b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text); 1280b5e92c5SJonathan Cameron 1290b5e92c5SJonathan Cameron static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"}; 1300b5e92c5SJonathan Cameron static const struct soc_enum wm8940_mic_bias_level_enum 1310b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text); 1320b5e92c5SJonathan Cameron 1330b5e92c5SJonathan Cameron static const char *wm8940_filter_mode_text[] = {"Audio", "Application"}; 1340b5e92c5SJonathan Cameron static const struct soc_enum wm8940_filter_mode_enum 1350b5e92c5SJonathan Cameron = SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text); 1360b5e92c5SJonathan Cameron 1376be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); 1386be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); 1396be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0); 1406be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0); 1416be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0); 1426be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0); 1436be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0); 1446be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0); 1456be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1); 1466be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0); 1470b5e92c5SJonathan Cameron 1480b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_snd_controls[] = { 1490b5e92c5SJonathan Cameron SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL, 1500b5e92c5SJonathan Cameron 6, 1, 0), 1510b5e92c5SJonathan Cameron SOC_ENUM("DAC Companding", wm8940_dac_companding_enum), 1520b5e92c5SJonathan Cameron SOC_ENUM("ADC Companding", wm8940_adc_companding_enum), 1530b5e92c5SJonathan Cameron 1540b5e92c5SJonathan Cameron SOC_ENUM("ALC Mode", wm8940_alc_mode_enum), 1550b5e92c5SJonathan Cameron SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0), 1560b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1, 1570b5e92c5SJonathan Cameron 3, 7, 1, wm8940_alc_max_tlv), 1580b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1, 1590b5e92c5SJonathan Cameron 0, 7, 0, wm8940_alc_min_tlv), 1600b5e92c5SJonathan Cameron SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2, 1610b5e92c5SJonathan Cameron 0, 14, 0, wm8940_alc_tar_tlv), 1620b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0), 1630b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0), 1640b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0), 1650b5e92c5SJonathan Cameron SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0), 1660b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE, 1670b5e92c5SJonathan Cameron 3, 1, 0), 1680b5e92c5SJonathan Cameron SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE, 1690b5e92c5SJonathan Cameron 0, 7, 0), 1700b5e92c5SJonathan Cameron 1710b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0), 1720b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0), 1730b5e92c5SJonathan Cameron SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0), 1740b5e92c5SJonathan Cameron SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2, 1750b5e92c5SJonathan Cameron 4, 9, 1, wm8940_lim_thresh_tlv), 1760b5e92c5SJonathan Cameron SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2, 1770b5e92c5SJonathan Cameron 0, 12, 0, wm8940_lim_boost_tlv), 1780b5e92c5SJonathan Cameron 1790b5e92c5SJonathan Cameron SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0), 1800b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN, 1810b5e92c5SJonathan Cameron 0, 63, 0, wm8940_pga_vol_tlv), 1820b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL, 1830b5e92c5SJonathan Cameron 0, 255, 0, wm8940_adc_tlv), 1840b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL, 1850b5e92c5SJonathan Cameron 0, 255, 0, wm8940_adc_tlv), 1860b5e92c5SJonathan Cameron SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum), 1870b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST, 1880b5e92c5SJonathan Cameron 8, 1, 0, wm8940_capture_boost_vol_tlv), 1890b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL, 1900b5e92c5SJonathan Cameron 0, 63, 0, wm8940_spk_vol_tlv), 1910b5e92c5SJonathan Cameron SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL, 6, 1, 1), 1920b5e92c5SJonathan Cameron 1930b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL, 1940b5e92c5SJonathan Cameron 8, 1, 1, wm8940_att_tlv), 1950b5e92c5SJonathan Cameron SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0), 1960b5e92c5SJonathan Cameron 1970b5e92c5SJonathan Cameron SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1), 1980b5e92c5SJonathan Cameron SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX, 1990b5e92c5SJonathan Cameron 7, 1, 1, wm8940_att_tlv), 2000b5e92c5SJonathan Cameron 2010b5e92c5SJonathan Cameron SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0), 2020b5e92c5SJonathan Cameron SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum), 2030b5e92c5SJonathan Cameron SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0), 2040b5e92c5SJonathan Cameron SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0), 2050b5e92c5SJonathan Cameron SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0), 2060b5e92c5SJonathan Cameron SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0), 2070b5e92c5SJonathan Cameron SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0), 2080b5e92c5SJonathan Cameron }; 2090b5e92c5SJonathan Cameron 2100b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = { 2110b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0), 2120b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0), 2130b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0), 2140b5e92c5SJonathan Cameron }; 2150b5e92c5SJonathan Cameron 2160b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = { 2170b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0), 2180b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0), 2190b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0), 2200b5e92c5SJonathan Cameron }; 2210b5e92c5SJonathan Cameron 2226be01cfbSMark Brown static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1); 2230b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_input_boost_controls[] = { 2240b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1), 2250b5e92c5SJonathan Cameron SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST, 2260b5e92c5SJonathan Cameron 0, 7, 0, wm8940_boost_vol_tlv), 2270b5e92c5SJonathan Cameron SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST, 2280b5e92c5SJonathan Cameron 4, 7, 0, wm8940_boost_vol_tlv), 2290b5e92c5SJonathan Cameron }; 2300b5e92c5SJonathan Cameron 2310b5e92c5SJonathan Cameron static const struct snd_kcontrol_new wm8940_micpga_controls[] = { 2320b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0), 2330b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0), 2340b5e92c5SJonathan Cameron SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0), 2350b5e92c5SJonathan Cameron }; 2360b5e92c5SJonathan Cameron 2370b5e92c5SJonathan Cameron static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = { 2380b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0, 2390b5e92c5SJonathan Cameron &wm8940_speaker_mixer_controls[0], 2400b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_speaker_mixer_controls)), 2410b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0, 2420b5e92c5SJonathan Cameron &wm8940_mono_mixer_controls[0], 2430b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_mono_mixer_controls)), 2440b5e92c5SJonathan Cameron SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0), 2450b5e92c5SJonathan Cameron 2460b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0), 2470b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0), 2480b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0), 2490b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("MONOOUT"), 2500b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("SPKOUTP"), 2510b5e92c5SJonathan Cameron SND_SOC_DAPM_OUTPUT("SPKOUTN"), 2520b5e92c5SJonathan Cameron 2530b5e92c5SJonathan Cameron SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0), 2540b5e92c5SJonathan Cameron SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0), 2550b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0, 2560b5e92c5SJonathan Cameron &wm8940_micpga_controls[0], 2570b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_micpga_controls)), 2580b5e92c5SJonathan Cameron SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0, 2590b5e92c5SJonathan Cameron &wm8940_input_boost_controls[0], 2600b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_input_boost_controls)), 2610b5e92c5SJonathan Cameron SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0), 2620b5e92c5SJonathan Cameron 2630b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("MICN"), 2640b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("MICP"), 2650b5e92c5SJonathan Cameron SND_SOC_DAPM_INPUT("AUX"), 2660b5e92c5SJonathan Cameron }; 2670b5e92c5SJonathan Cameron 2680b5e92c5SJonathan Cameron static const struct snd_soc_dapm_route audio_map[] = { 2690b5e92c5SJonathan Cameron /* Mono output mixer */ 2700b5e92c5SJonathan Cameron {"Mono Mixer", "PCM Playback Switch", "DAC"}, 2710b5e92c5SJonathan Cameron {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 2720b5e92c5SJonathan Cameron {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, 2730b5e92c5SJonathan Cameron 2740b5e92c5SJonathan Cameron /* Speaker output mixer */ 2750b5e92c5SJonathan Cameron {"Speaker Mixer", "PCM Playback Switch", "DAC"}, 2760b5e92c5SJonathan Cameron {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, 2770b5e92c5SJonathan Cameron {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, 2780b5e92c5SJonathan Cameron 2790b5e92c5SJonathan Cameron /* Outputs */ 2800b5e92c5SJonathan Cameron {"Mono Out", NULL, "Mono Mixer"}, 2810b5e92c5SJonathan Cameron {"MONOOUT", NULL, "Mono Out"}, 2820b5e92c5SJonathan Cameron {"SpkN Out", NULL, "Speaker Mixer"}, 2830b5e92c5SJonathan Cameron {"SpkP Out", NULL, "Speaker Mixer"}, 2840b5e92c5SJonathan Cameron {"SPKOUTN", NULL, "SpkN Out"}, 2850b5e92c5SJonathan Cameron {"SPKOUTP", NULL, "SpkP Out"}, 2860b5e92c5SJonathan Cameron 2870b5e92c5SJonathan Cameron /* Microphone PGA */ 2880b5e92c5SJonathan Cameron {"Mic PGA", "MICN Switch", "MICN"}, 2890b5e92c5SJonathan Cameron {"Mic PGA", "MICP Switch", "MICP"}, 2900b5e92c5SJonathan Cameron {"Mic PGA", "AUX Switch", "AUX"}, 2910b5e92c5SJonathan Cameron 2920b5e92c5SJonathan Cameron /* Boost Mixer */ 2930b5e92c5SJonathan Cameron {"Boost Mixer", "Mic PGA Switch", "Mic PGA"}, 2940b5e92c5SJonathan Cameron {"Boost Mixer", "Mic Volume", "MICP"}, 2950b5e92c5SJonathan Cameron {"Boost Mixer", "Aux Volume", "Aux Input"}, 2960b5e92c5SJonathan Cameron 2970b5e92c5SJonathan Cameron {"ADC", NULL, "Boost Mixer"}, 2980b5e92c5SJonathan Cameron }; 2990b5e92c5SJonathan Cameron 3000b5e92c5SJonathan Cameron static int wm8940_add_widgets(struct snd_soc_codec *codec) 3010b5e92c5SJonathan Cameron { 302ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 3030b5e92c5SJonathan Cameron int ret; 3040b5e92c5SJonathan Cameron 305ce6120ccSLiam Girdwood ret = snd_soc_dapm_new_controls(dapm, wm8940_dapm_widgets, 3060b5e92c5SJonathan Cameron ARRAY_SIZE(wm8940_dapm_widgets)); 3070b5e92c5SJonathan Cameron if (ret) 3080b5e92c5SJonathan Cameron goto error_ret; 309ce6120ccSLiam Girdwood ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); 3100b5e92c5SJonathan Cameron 3110b5e92c5SJonathan Cameron error_ret: 3120b5e92c5SJonathan Cameron return ret; 3130b5e92c5SJonathan Cameron } 3140b5e92c5SJonathan Cameron 3158d50e447SMark Brown #define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0); 3160b5e92c5SJonathan Cameron 3170b5e92c5SJonathan Cameron static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, 3180b5e92c5SJonathan Cameron unsigned int fmt) 3190b5e92c5SJonathan Cameron { 3200b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 3218d50e447SMark Brown u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67; 3228d50e447SMark Brown u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe; 3230b5e92c5SJonathan Cameron 3240b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3250b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_CBM_CFM: 3260b5e92c5SJonathan Cameron clk |= 1; 3270b5e92c5SJonathan Cameron break; 3280b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_CBS_CFS: 3290b5e92c5SJonathan Cameron break; 3300b5e92c5SJonathan Cameron default: 3310b5e92c5SJonathan Cameron return -EINVAL; 3320b5e92c5SJonathan Cameron } 3338d50e447SMark Brown snd_soc_write(codec, WM8940_CLOCK, clk); 3340b5e92c5SJonathan Cameron 3350b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 3360b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_I2S: 3370b5e92c5SJonathan Cameron iface |= (2 << 3); 3380b5e92c5SJonathan Cameron break; 3390b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_LEFT_J: 3400b5e92c5SJonathan Cameron iface |= (1 << 3); 3410b5e92c5SJonathan Cameron break; 3420b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_RIGHT_J: 3430b5e92c5SJonathan Cameron break; 3440b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_DSP_A: 3450b5e92c5SJonathan Cameron iface |= (3 << 3); 3460b5e92c5SJonathan Cameron break; 3470b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_DSP_B: 3480b5e92c5SJonathan Cameron iface |= (3 << 3) | (1 << 7); 3490b5e92c5SJonathan Cameron break; 3500b5e92c5SJonathan Cameron } 3510b5e92c5SJonathan Cameron 3520b5e92c5SJonathan Cameron switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 3530b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_NB_NF: 3540b5e92c5SJonathan Cameron break; 3550b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_NB_IF: 3560b5e92c5SJonathan Cameron iface |= (1 << 7); 3570b5e92c5SJonathan Cameron break; 3580b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_IB_NF: 3590b5e92c5SJonathan Cameron iface |= (1 << 8); 3600b5e92c5SJonathan Cameron break; 3610b5e92c5SJonathan Cameron case SND_SOC_DAIFMT_IB_IF: 3620b5e92c5SJonathan Cameron iface |= (1 << 8) | (1 << 7); 3630b5e92c5SJonathan Cameron break; 3640b5e92c5SJonathan Cameron } 3650b5e92c5SJonathan Cameron 3668d50e447SMark Brown snd_soc_write(codec, WM8940_IFACE, iface); 3670b5e92c5SJonathan Cameron 3680b5e92c5SJonathan Cameron return 0; 3690b5e92c5SJonathan Cameron } 3700b5e92c5SJonathan Cameron 3710b5e92c5SJonathan Cameron static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream, 3720b5e92c5SJonathan Cameron struct snd_pcm_hw_params *params, 3730b5e92c5SJonathan Cameron struct snd_soc_dai *dai) 3740b5e92c5SJonathan Cameron { 3750b5e92c5SJonathan Cameron struct snd_soc_pcm_runtime *rtd = substream->private_data; 376f0fba2adSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 3778d50e447SMark Brown u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F; 3788d50e447SMark Brown u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1; 3798d50e447SMark Brown u16 companding = snd_soc_read(codec, 3800b5e92c5SJonathan Cameron WM8940_COMPANDINGCTL) & 0xFFDF; 3810b5e92c5SJonathan Cameron int ret; 3820b5e92c5SJonathan Cameron 3830b5e92c5SJonathan Cameron /* LoutR control */ 3840b5e92c5SJonathan Cameron if (substream->stream == SNDRV_PCM_STREAM_CAPTURE 3850b5e92c5SJonathan Cameron && params_channels(params) == 2) 3860b5e92c5SJonathan Cameron iface |= (1 << 9); 3870b5e92c5SJonathan Cameron 3880b5e92c5SJonathan Cameron switch (params_rate(params)) { 389b3172f22SGuennadi Liakhovetski case 8000: 3900b5e92c5SJonathan Cameron addcntrl |= (0x5 << 1); 3910b5e92c5SJonathan Cameron break; 392b3172f22SGuennadi Liakhovetski case 11025: 3930b5e92c5SJonathan Cameron addcntrl |= (0x4 << 1); 3940b5e92c5SJonathan Cameron break; 395b3172f22SGuennadi Liakhovetski case 16000: 3960b5e92c5SJonathan Cameron addcntrl |= (0x3 << 1); 3970b5e92c5SJonathan Cameron break; 398b3172f22SGuennadi Liakhovetski case 22050: 3990b5e92c5SJonathan Cameron addcntrl |= (0x2 << 1); 4000b5e92c5SJonathan Cameron break; 401b3172f22SGuennadi Liakhovetski case 32000: 4020b5e92c5SJonathan Cameron addcntrl |= (0x1 << 1); 4030b5e92c5SJonathan Cameron break; 404b3172f22SGuennadi Liakhovetski case 44100: 405b3172f22SGuennadi Liakhovetski case 48000: 4060b5e92c5SJonathan Cameron break; 4070b5e92c5SJonathan Cameron } 4088d50e447SMark Brown ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl); 4090b5e92c5SJonathan Cameron if (ret) 4100b5e92c5SJonathan Cameron goto error_ret; 4110b5e92c5SJonathan Cameron 4120b5e92c5SJonathan Cameron switch (params_format(params)) { 4130b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S8: 4140b5e92c5SJonathan Cameron companding = companding | (1 << 5); 4150b5e92c5SJonathan Cameron break; 4160b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S16_LE: 4170b5e92c5SJonathan Cameron break; 4180b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S20_3LE: 4190b5e92c5SJonathan Cameron iface |= (1 << 5); 4200b5e92c5SJonathan Cameron break; 4210b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S24_LE: 4220b5e92c5SJonathan Cameron iface |= (2 << 5); 4230b5e92c5SJonathan Cameron break; 4240b5e92c5SJonathan Cameron case SNDRV_PCM_FORMAT_S32_LE: 4250b5e92c5SJonathan Cameron iface |= (3 << 5); 4260b5e92c5SJonathan Cameron break; 4270b5e92c5SJonathan Cameron } 4288d50e447SMark Brown ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding); 4290b5e92c5SJonathan Cameron if (ret) 4300b5e92c5SJonathan Cameron goto error_ret; 4318d50e447SMark Brown ret = snd_soc_write(codec, WM8940_IFACE, iface); 4320b5e92c5SJonathan Cameron 4330b5e92c5SJonathan Cameron error_ret: 4340b5e92c5SJonathan Cameron return ret; 4350b5e92c5SJonathan Cameron } 4360b5e92c5SJonathan Cameron 4370b5e92c5SJonathan Cameron static int wm8940_mute(struct snd_soc_dai *dai, int mute) 4380b5e92c5SJonathan Cameron { 4390b5e92c5SJonathan Cameron struct snd_soc_codec *codec = dai->codec; 4408d50e447SMark Brown u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf; 4410b5e92c5SJonathan Cameron 4420b5e92c5SJonathan Cameron if (mute) 4430b5e92c5SJonathan Cameron mute_reg |= 0x40; 4440b5e92c5SJonathan Cameron 4458d50e447SMark Brown return snd_soc_write(codec, WM8940_DAC, mute_reg); 4460b5e92c5SJonathan Cameron } 4470b5e92c5SJonathan Cameron 4480b5e92c5SJonathan Cameron static int wm8940_set_bias_level(struct snd_soc_codec *codec, 4490b5e92c5SJonathan Cameron enum snd_soc_bias_level level) 4500b5e92c5SJonathan Cameron { 4510b5e92c5SJonathan Cameron u16 val; 4528d50e447SMark Brown u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0; 4530b5e92c5SJonathan Cameron int ret = 0; 4540b5e92c5SJonathan Cameron 4550b5e92c5SJonathan Cameron switch (level) { 4560b5e92c5SJonathan Cameron case SND_SOC_BIAS_ON: 4570b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4580b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 4590b5e92c5SJonathan Cameron /* Enable thermal shutdown */ 4608d50e447SMark Brown val = snd_soc_read(codec, WM8940_OUTPUTCTL); 4618d50e447SMark Brown ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2); 4620b5e92c5SJonathan Cameron if (ret) 4630b5e92c5SJonathan Cameron break; 4640b5e92c5SJonathan Cameron /* set vmid to 75k */ 4658d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1); 4660b5e92c5SJonathan Cameron break; 4670b5e92c5SJonathan Cameron case SND_SOC_BIAS_PREPARE: 4680b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4690b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 4708d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1); 4710b5e92c5SJonathan Cameron break; 4720b5e92c5SJonathan Cameron case SND_SOC_BIAS_STANDBY: 473788b6e8eSAxel Lin if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 474788b6e8eSAxel Lin ret = snd_soc_cache_sync(codec); 475788b6e8eSAxel Lin if (ret < 0) { 476788b6e8eSAxel Lin dev_err(codec->dev, "Failed to sync cache: %d\n", ret); 477788b6e8eSAxel Lin return ret; 478788b6e8eSAxel Lin } 479788b6e8eSAxel Lin } 480788b6e8eSAxel Lin 4810b5e92c5SJonathan Cameron /* ensure bufioen and biasen */ 4820b5e92c5SJonathan Cameron pwr_reg |= (1 << 2) | (1 << 3); 4830b5e92c5SJonathan Cameron /* set vmid to 300k for standby */ 4848d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2); 4850b5e92c5SJonathan Cameron break; 4860b5e92c5SJonathan Cameron case SND_SOC_BIAS_OFF: 4878d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg); 4880b5e92c5SJonathan Cameron break; 4890b5e92c5SJonathan Cameron } 4900b5e92c5SJonathan Cameron 4915927f947SAxel Lin codec->dapm.bias_level = level; 4925927f947SAxel Lin 4930b5e92c5SJonathan Cameron return ret; 4940b5e92c5SJonathan Cameron } 4950b5e92c5SJonathan Cameron 4960b5e92c5SJonathan Cameron struct pll_ { 4970b5e92c5SJonathan Cameron unsigned int pre_scale:2; 4980b5e92c5SJonathan Cameron unsigned int n:4; 4990b5e92c5SJonathan Cameron unsigned int k; 5000b5e92c5SJonathan Cameron }; 5010b5e92c5SJonathan Cameron 5020b5e92c5SJonathan Cameron static struct pll_ pll_div; 5030b5e92c5SJonathan Cameron 5040b5e92c5SJonathan Cameron /* The size in bits of the pll divide multiplied by 10 5050b5e92c5SJonathan Cameron * to allow rounding later */ 5060b5e92c5SJonathan Cameron #define FIXED_PLL_SIZE ((1 << 24) * 10) 5070b5e92c5SJonathan Cameron static void pll_factors(unsigned int target, unsigned int source) 5080b5e92c5SJonathan Cameron { 5090b5e92c5SJonathan Cameron unsigned long long Kpart; 5100b5e92c5SJonathan Cameron unsigned int K, Ndiv, Nmod; 5110b5e92c5SJonathan Cameron /* The left shift ist to avoid accuracy loss when right shifting */ 5120b5e92c5SJonathan Cameron Ndiv = target / source; 5130b5e92c5SJonathan Cameron 5140b5e92c5SJonathan Cameron if (Ndiv > 12) { 5150b5e92c5SJonathan Cameron source <<= 1; 5160b5e92c5SJonathan Cameron /* Multiply by 2 */ 5170b5e92c5SJonathan Cameron pll_div.pre_scale = 0; 5180b5e92c5SJonathan Cameron Ndiv = target / source; 5190b5e92c5SJonathan Cameron } else if (Ndiv < 3) { 5200b5e92c5SJonathan Cameron source >>= 2; 5210b5e92c5SJonathan Cameron /* Divide by 4 */ 5220b5e92c5SJonathan Cameron pll_div.pre_scale = 3; 5230b5e92c5SJonathan Cameron Ndiv = target / source; 5240b5e92c5SJonathan Cameron } else if (Ndiv < 6) { 5250b5e92c5SJonathan Cameron source >>= 1; 5260b5e92c5SJonathan Cameron /* divide by 2 */ 5270b5e92c5SJonathan Cameron pll_div.pre_scale = 2; 5280b5e92c5SJonathan Cameron Ndiv = target / source; 5290b5e92c5SJonathan Cameron } else 5300b5e92c5SJonathan Cameron pll_div.pre_scale = 1; 5310b5e92c5SJonathan Cameron 5320b5e92c5SJonathan Cameron if ((Ndiv < 6) || (Ndiv > 12)) 5330b5e92c5SJonathan Cameron printk(KERN_WARNING 5340b5e92c5SJonathan Cameron "WM8940 N value %d outwith recommended range!d\n", 5350b5e92c5SJonathan Cameron Ndiv); 5360b5e92c5SJonathan Cameron 5370b5e92c5SJonathan Cameron pll_div.n = Ndiv; 5380b5e92c5SJonathan Cameron Nmod = target % source; 5390b5e92c5SJonathan Cameron Kpart = FIXED_PLL_SIZE * (long long)Nmod; 5400b5e92c5SJonathan Cameron 5410b5e92c5SJonathan Cameron do_div(Kpart, source); 5420b5e92c5SJonathan Cameron 5430b5e92c5SJonathan Cameron K = Kpart & 0xFFFFFFFF; 5440b5e92c5SJonathan Cameron 5450b5e92c5SJonathan Cameron /* Check if we need to round */ 5460b5e92c5SJonathan Cameron if ((K % 10) >= 5) 5470b5e92c5SJonathan Cameron K += 5; 5480b5e92c5SJonathan Cameron 5490b5e92c5SJonathan Cameron /* Move down to proper range now rounding is done */ 5500b5e92c5SJonathan Cameron K /= 10; 5510b5e92c5SJonathan Cameron 5520b5e92c5SJonathan Cameron pll_div.k = K; 5530b5e92c5SJonathan Cameron } 5540b5e92c5SJonathan Cameron 5550b5e92c5SJonathan Cameron /* Untested at the moment */ 55685488037SMark Brown static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 55785488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 5580b5e92c5SJonathan Cameron { 5590b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 5600b5e92c5SJonathan Cameron u16 reg; 5610b5e92c5SJonathan Cameron 5620b5e92c5SJonathan Cameron /* Turn off PLL */ 5638d50e447SMark Brown reg = snd_soc_read(codec, WM8940_POWER1); 5648d50e447SMark Brown snd_soc_write(codec, WM8940_POWER1, reg & 0x1df); 5650b5e92c5SJonathan Cameron 5660b5e92c5SJonathan Cameron if (freq_in == 0 || freq_out == 0) { 5670b5e92c5SJonathan Cameron /* Clock CODEC directly from MCLK */ 5688d50e447SMark Brown reg = snd_soc_read(codec, WM8940_CLOCK); 5698d50e447SMark Brown snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff); 5700b5e92c5SJonathan Cameron /* Pll power down */ 5718d50e447SMark Brown snd_soc_write(codec, WM8940_PLLN, (1 << 7)); 5720b5e92c5SJonathan Cameron return 0; 5730b5e92c5SJonathan Cameron } 5740b5e92c5SJonathan Cameron 5750b5e92c5SJonathan Cameron /* Pll is followed by a frequency divide by 4 */ 5760b5e92c5SJonathan Cameron pll_factors(freq_out*4, freq_in); 5770b5e92c5SJonathan Cameron if (pll_div.k) 5788d50e447SMark Brown snd_soc_write(codec, WM8940_PLLN, 5790b5e92c5SJonathan Cameron (pll_div.pre_scale << 4) | pll_div.n | (1 << 6)); 5800b5e92c5SJonathan Cameron else /* No factional component */ 5818d50e447SMark Brown snd_soc_write(codec, WM8940_PLLN, 5820b5e92c5SJonathan Cameron (pll_div.pre_scale << 4) | pll_div.n); 5838d50e447SMark Brown snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18); 5848d50e447SMark Brown snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff); 5858d50e447SMark Brown snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff); 5860b5e92c5SJonathan Cameron /* Enable the PLL */ 5878d50e447SMark Brown reg = snd_soc_read(codec, WM8940_POWER1); 5888d50e447SMark Brown snd_soc_write(codec, WM8940_POWER1, reg | 0x020); 5890b5e92c5SJonathan Cameron 5900b5e92c5SJonathan Cameron /* Run CODEC from PLL instead of MCLK */ 5918d50e447SMark Brown reg = snd_soc_read(codec, WM8940_CLOCK); 5928d50e447SMark Brown snd_soc_write(codec, WM8940_CLOCK, reg | 0x100); 5930b5e92c5SJonathan Cameron 5940b5e92c5SJonathan Cameron return 0; 5950b5e92c5SJonathan Cameron } 5960b5e92c5SJonathan Cameron 5970b5e92c5SJonathan Cameron static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai, 5980b5e92c5SJonathan Cameron int clk_id, unsigned int freq, int dir) 5990b5e92c5SJonathan Cameron { 6000b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 601b2c812e2SMark Brown struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec); 6020b5e92c5SJonathan Cameron 6030b5e92c5SJonathan Cameron switch (freq) { 6040b5e92c5SJonathan Cameron case 11289600: 6050b5e92c5SJonathan Cameron case 12000000: 6060b5e92c5SJonathan Cameron case 12288000: 6070b5e92c5SJonathan Cameron case 16934400: 6080b5e92c5SJonathan Cameron case 18432000: 6090b5e92c5SJonathan Cameron wm8940->sysclk = freq; 6100b5e92c5SJonathan Cameron return 0; 6110b5e92c5SJonathan Cameron } 6120b5e92c5SJonathan Cameron return -EINVAL; 6130b5e92c5SJonathan Cameron } 6140b5e92c5SJonathan Cameron 6150b5e92c5SJonathan Cameron static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 6160b5e92c5SJonathan Cameron int div_id, int div) 6170b5e92c5SJonathan Cameron { 6180b5e92c5SJonathan Cameron struct snd_soc_codec *codec = codec_dai->codec; 6190b5e92c5SJonathan Cameron u16 reg; 6200b5e92c5SJonathan Cameron int ret = 0; 6210b5e92c5SJonathan Cameron 6220b5e92c5SJonathan Cameron switch (div_id) { 6230b5e92c5SJonathan Cameron case WM8940_BCLKDIV: 624*b272cc76SAxel Lin reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFE3; 6258d50e447SMark Brown ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2)); 6260b5e92c5SJonathan Cameron break; 6270b5e92c5SJonathan Cameron case WM8940_MCLKDIV: 6288d50e447SMark Brown reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F; 6298d50e447SMark Brown ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5)); 6300b5e92c5SJonathan Cameron break; 6310b5e92c5SJonathan Cameron case WM8940_OPCLKDIV: 6328d50e447SMark Brown reg = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFCF; 6338d50e447SMark Brown ret = snd_soc_write(codec, WM8940_ADDCNTRL, reg | (div << 4)); 6340b5e92c5SJonathan Cameron break; 6350b5e92c5SJonathan Cameron } 6360b5e92c5SJonathan Cameron return ret; 6370b5e92c5SJonathan Cameron } 6380b5e92c5SJonathan Cameron 6390b5e92c5SJonathan Cameron #define WM8940_RATES SNDRV_PCM_RATE_8000_48000 6400b5e92c5SJonathan Cameron 6410b5e92c5SJonathan Cameron #define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 6420b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S16_LE | \ 6430b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S20_3LE | \ 6440b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S24_LE | \ 6450b5e92c5SJonathan Cameron SNDRV_PCM_FMTBIT_S32_LE) 6460b5e92c5SJonathan Cameron 6470b5e92c5SJonathan Cameron static struct snd_soc_dai_ops wm8940_dai_ops = { 6480b5e92c5SJonathan Cameron .hw_params = wm8940_i2s_hw_params, 6490b5e92c5SJonathan Cameron .set_sysclk = wm8940_set_dai_sysclk, 6500b5e92c5SJonathan Cameron .digital_mute = wm8940_mute, 6510b5e92c5SJonathan Cameron .set_fmt = wm8940_set_dai_fmt, 6520b5e92c5SJonathan Cameron .set_clkdiv = wm8940_set_dai_clkdiv, 6530b5e92c5SJonathan Cameron .set_pll = wm8940_set_dai_pll, 6540b5e92c5SJonathan Cameron }; 6550b5e92c5SJonathan Cameron 656f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8940_dai = { 657f0fba2adSLiam Girdwood .name = "wm8940-hifi", 6580b5e92c5SJonathan Cameron .playback = { 6590b5e92c5SJonathan Cameron .stream_name = "Playback", 6600b5e92c5SJonathan Cameron .channels_min = 1, 6610b5e92c5SJonathan Cameron .channels_max = 2, 6620b5e92c5SJonathan Cameron .rates = WM8940_RATES, 6630b5e92c5SJonathan Cameron .formats = WM8940_FORMATS, 6640b5e92c5SJonathan Cameron }, 6650b5e92c5SJonathan Cameron .capture = { 6660b5e92c5SJonathan Cameron .stream_name = "Capture", 6670b5e92c5SJonathan Cameron .channels_min = 1, 6680b5e92c5SJonathan Cameron .channels_max = 2, 6690b5e92c5SJonathan Cameron .rates = WM8940_RATES, 6700b5e92c5SJonathan Cameron .formats = WM8940_FORMATS, 6710b5e92c5SJonathan Cameron }, 6720b5e92c5SJonathan Cameron .ops = &wm8940_dai_ops, 6730b5e92c5SJonathan Cameron .symmetric_rates = 1, 6740b5e92c5SJonathan Cameron }; 6750b5e92c5SJonathan Cameron 676f0fba2adSLiam Girdwood static int wm8940_suspend(struct snd_soc_codec *codec, pm_message_t state) 6770b5e92c5SJonathan Cameron { 6780b5e92c5SJonathan Cameron return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF); 6790b5e92c5SJonathan Cameron } 6800b5e92c5SJonathan Cameron 681f0fba2adSLiam Girdwood static int wm8940_resume(struct snd_soc_codec *codec) 6820b5e92c5SJonathan Cameron { 683788b6e8eSAxel Lin wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 684788b6e8eSAxel Lin return 0; 6850b5e92c5SJonathan Cameron } 6860b5e92c5SJonathan Cameron 687f0fba2adSLiam Girdwood static int wm8940_probe(struct snd_soc_codec *codec) 6880b5e92c5SJonathan Cameron { 689f0fba2adSLiam Girdwood struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec); 690f0fba2adSLiam Girdwood struct wm8940_setup_data *pdata = codec->dev->platform_data; 6910b5e92c5SJonathan Cameron int ret; 6920b5e92c5SJonathan Cameron u16 reg; 6930b5e92c5SJonathan Cameron 694f0fba2adSLiam Girdwood ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8940->control_type); 695e655a435SJonathan Cameron if (ret < 0) { 6968d50e447SMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 6978d50e447SMark Brown return ret; 6988d50e447SMark Brown } 6998d50e447SMark Brown 7000b5e92c5SJonathan Cameron ret = wm8940_reset(codec); 7010b5e92c5SJonathan Cameron if (ret < 0) { 7020b5e92c5SJonathan Cameron dev_err(codec->dev, "Failed to issue reset\n"); 7030b5e92c5SJonathan Cameron return ret; 7040b5e92c5SJonathan Cameron } 7050b5e92c5SJonathan Cameron 7060b5e92c5SJonathan Cameron wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 7070b5e92c5SJonathan Cameron 7088d50e447SMark Brown ret = snd_soc_write(codec, WM8940_POWER1, 0x180); 7090b5e92c5SJonathan Cameron if (ret < 0) 7100b5e92c5SJonathan Cameron return ret; 7110b5e92c5SJonathan Cameron 7120b5e92c5SJonathan Cameron if (!pdata) 7130b5e92c5SJonathan Cameron dev_warn(codec->dev, "No platform data supplied\n"); 7140b5e92c5SJonathan Cameron else { 7158d50e447SMark Brown reg = snd_soc_read(codec, WM8940_OUTPUTCTL); 7168d50e447SMark Brown ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi); 7170b5e92c5SJonathan Cameron if (ret < 0) 7180b5e92c5SJonathan Cameron return ret; 7190b5e92c5SJonathan Cameron } 7200b5e92c5SJonathan Cameron 721f0fba2adSLiam Girdwood ret = snd_soc_add_controls(codec, wm8940_snd_controls, 722f0fba2adSLiam Girdwood ARRAY_SIZE(wm8940_snd_controls)); 723f0fba2adSLiam Girdwood if (ret) 7240b5e92c5SJonathan Cameron return ret; 725f0fba2adSLiam Girdwood ret = wm8940_add_widgets(codec); 726f0fba2adSLiam Girdwood return ret; 7270b5e92c5SJonathan Cameron } 7280b5e92c5SJonathan Cameron 729f0fba2adSLiam Girdwood static int wm8940_remove(struct snd_soc_codec *codec) 730f0fba2adSLiam Girdwood { 731f0fba2adSLiam Girdwood wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF); 7320b5e92c5SJonathan Cameron return 0; 7330b5e92c5SJonathan Cameron } 7340b5e92c5SJonathan Cameron 735f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8940 = { 736f0fba2adSLiam Girdwood .probe = wm8940_probe, 737f0fba2adSLiam Girdwood .remove = wm8940_remove, 738f0fba2adSLiam Girdwood .suspend = wm8940_suspend, 739f0fba2adSLiam Girdwood .resume = wm8940_resume, 740f0fba2adSLiam Girdwood .set_bias_level = wm8940_set_bias_level, 741e5eec34cSDimitris Papastamos .reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults), 742f0fba2adSLiam Girdwood .reg_word_size = sizeof(u16), 743f0fba2adSLiam Girdwood .reg_cache_default = wm8940_reg_defaults, 744788b6e8eSAxel Lin .volatile_register = wm8940_volatile_register, 745f0fba2adSLiam Girdwood }; 7460b5e92c5SJonathan Cameron 747f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 748f0fba2adSLiam Girdwood static __devinit int wm8940_i2c_probe(struct i2c_client *i2c, 7490b5e92c5SJonathan Cameron const struct i2c_device_id *id) 7500b5e92c5SJonathan Cameron { 7510b5e92c5SJonathan Cameron struct wm8940_priv *wm8940; 752f0fba2adSLiam Girdwood int ret; 7530b5e92c5SJonathan Cameron 754f0fba2adSLiam Girdwood wm8940 = kzalloc(sizeof(struct wm8940_priv), GFP_KERNEL); 7550b5e92c5SJonathan Cameron if (wm8940 == NULL) 7560b5e92c5SJonathan Cameron return -ENOMEM; 7570b5e92c5SJonathan Cameron 7580b5e92c5SJonathan Cameron i2c_set_clientdata(i2c, wm8940); 7597f984b55SLars-Peter Clausen wm8940->control_type = SND_SOC_I2C; 7600b5e92c5SJonathan Cameron 761f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 762f0fba2adSLiam Girdwood &soc_codec_dev_wm8940, &wm8940_dai, 1); 763db1e18deSAxel Lin if (ret < 0) 764db1e18deSAxel Lin kfree(wm8940); 765db1e18deSAxel Lin return ret; 7660b5e92c5SJonathan Cameron } 7670b5e92c5SJonathan Cameron 768f0fba2adSLiam Girdwood static __devexit int wm8940_i2c_remove(struct i2c_client *client) 7690b5e92c5SJonathan Cameron { 770f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 771f0fba2adSLiam Girdwood kfree(i2c_get_clientdata(client)); 7720b5e92c5SJonathan Cameron return 0; 7730b5e92c5SJonathan Cameron } 7740b5e92c5SJonathan Cameron 7750b5e92c5SJonathan Cameron static const struct i2c_device_id wm8940_i2c_id[] = { 7760b5e92c5SJonathan Cameron { "wm8940", 0 }, 7770b5e92c5SJonathan Cameron { } 7780b5e92c5SJonathan Cameron }; 7790b5e92c5SJonathan Cameron MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); 7800b5e92c5SJonathan Cameron 7810b5e92c5SJonathan Cameron static struct i2c_driver wm8940_i2c_driver = { 7820b5e92c5SJonathan Cameron .driver = { 783f0fba2adSLiam Girdwood .name = "wm8940-codec", 7840b5e92c5SJonathan Cameron .owner = THIS_MODULE, 7850b5e92c5SJonathan Cameron }, 7860b5e92c5SJonathan Cameron .probe = wm8940_i2c_probe, 7870b5e92c5SJonathan Cameron .remove = __devexit_p(wm8940_i2c_remove), 7880b5e92c5SJonathan Cameron .id_table = wm8940_i2c_id, 7890b5e92c5SJonathan Cameron }; 790f0fba2adSLiam Girdwood #endif 7910b5e92c5SJonathan Cameron 7920b5e92c5SJonathan Cameron static int __init wm8940_modinit(void) 7930b5e92c5SJonathan Cameron { 794f0fba2adSLiam Girdwood int ret = 0; 795f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 7960b5e92c5SJonathan Cameron ret = i2c_add_driver(&wm8940_i2c_driver); 797f0fba2adSLiam Girdwood if (ret != 0) { 798f0fba2adSLiam Girdwood printk(KERN_ERR "Failed to register wm8940 I2C driver: %d\n", 7990b5e92c5SJonathan Cameron ret); 800f0fba2adSLiam Girdwood } 801f0fba2adSLiam Girdwood #endif 8020b5e92c5SJonathan Cameron return ret; 8030b5e92c5SJonathan Cameron } 8040b5e92c5SJonathan Cameron module_init(wm8940_modinit); 8050b5e92c5SJonathan Cameron 8060b5e92c5SJonathan Cameron static void __exit wm8940_exit(void) 8070b5e92c5SJonathan Cameron { 808f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 8090b5e92c5SJonathan Cameron i2c_del_driver(&wm8940_i2c_driver); 810f0fba2adSLiam Girdwood #endif 8110b5e92c5SJonathan Cameron } 8120b5e92c5SJonathan Cameron module_exit(wm8940_exit); 8130b5e92c5SJonathan Cameron 8140b5e92c5SJonathan Cameron MODULE_DESCRIPTION("ASoC WM8940 driver"); 8150b5e92c5SJonathan Cameron MODULE_AUTHOR("Jonathan Cameron"); 8160b5e92c5SJonathan Cameron MODULE_LICENSE("GPL"); 817