xref: /linux/sound/soc/codecs/wm8940.c (revision b272cc769ac22014c0c60f2ebac46a2ae01300bf)
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