cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wm8940.c (23408B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * wm8940.c  --  WM8940 ALSA Soc Audio driver
      4 *
      5 * Author: Jonathan Cameron <jic23@cam.ac.uk>
      6 *
      7 * Based on wm8510.c
      8 *    Copyright  2006 Wolfson Microelectronics PLC.
      9 *    Author:  Liam Girdwood <lrg@slimlogic.co.uk>
     10 *
     11 * Not currently handled:
     12 * Notch filter control
     13 * AUXMode (inverting vs mixer)
     14 * No means to obtain current gain if alc enabled.
     15 * No use made of gpio
     16 * Fast VMID discharge for power down
     17 * Soft Start
     18 * DLR and ALR Swaps not enabled
     19 * Digital Sidetone not supported
     20 */
     21#include <linux/module.h>
     22#include <linux/moduleparam.h>
     23#include <linux/kernel.h>
     24#include <linux/init.h>
     25#include <linux/delay.h>
     26#include <linux/pm.h>
     27#include <linux/i2c.h>
     28#include <linux/regmap.h>
     29#include <linux/slab.h>
     30#include <sound/core.h>
     31#include <sound/pcm.h>
     32#include <sound/pcm_params.h>
     33#include <sound/soc.h>
     34#include <sound/initval.h>
     35#include <sound/tlv.h>
     36
     37#include "wm8940.h"
     38
     39struct wm8940_priv {
     40	unsigned int sysclk;
     41	struct regmap *regmap;
     42};
     43
     44static bool wm8940_volatile_register(struct device *dev, unsigned int reg)
     45{
     46	switch (reg) {
     47	case WM8940_SOFTRESET:
     48		return true;
     49	default:
     50		return false;
     51	}
     52}
     53
     54static bool wm8940_readable_register(struct device *dev, unsigned int reg)
     55{
     56	switch (reg) {
     57	case WM8940_SOFTRESET:
     58	case WM8940_POWER1:
     59	case WM8940_POWER2:
     60	case WM8940_POWER3:
     61	case WM8940_IFACE:
     62	case WM8940_COMPANDINGCTL:
     63	case WM8940_CLOCK:
     64	case WM8940_ADDCNTRL:
     65	case WM8940_GPIO:
     66	case WM8940_CTLINT:
     67	case WM8940_DAC:
     68	case WM8940_DACVOL:
     69	case WM8940_ADC:
     70	case WM8940_ADCVOL:
     71	case WM8940_NOTCH1:
     72	case WM8940_NOTCH2:
     73	case WM8940_NOTCH3:
     74	case WM8940_NOTCH4:
     75	case WM8940_NOTCH5:
     76	case WM8940_NOTCH6:
     77	case WM8940_NOTCH7:
     78	case WM8940_NOTCH8:
     79	case WM8940_DACLIM1:
     80	case WM8940_DACLIM2:
     81	case WM8940_ALC1:
     82	case WM8940_ALC2:
     83	case WM8940_ALC3:
     84	case WM8940_NOISEGATE:
     85	case WM8940_PLLN:
     86	case WM8940_PLLK1:
     87	case WM8940_PLLK2:
     88	case WM8940_PLLK3:
     89	case WM8940_ALC4:
     90	case WM8940_INPUTCTL:
     91	case WM8940_PGAGAIN:
     92	case WM8940_ADCBOOST:
     93	case WM8940_OUTPUTCTL:
     94	case WM8940_SPKMIX:
     95	case WM8940_SPKVOL:
     96	case WM8940_MONOMIX:
     97		return true;
     98	default:
     99		return false;
    100	}
    101}
    102
    103static const struct reg_default wm8940_reg_defaults[] = {
    104	{  0x1, 0x0000 }, /* Power 1 */
    105	{  0x2, 0x0000 }, /* Power 2 */
    106	{  0x3, 0x0000 }, /* Power 3 */
    107	{  0x4, 0x0010 }, /* Interface Control */
    108	{  0x5, 0x0000 }, /* Companding Control */
    109	{  0x6, 0x0140 }, /* Clock Control */
    110	{  0x7, 0x0000 }, /* Additional Controls */
    111	{  0x8, 0x0000 }, /* GPIO Control */
    112	{  0x9, 0x0002 }, /* Auto Increment Control */
    113	{  0xa, 0x0000 }, /* DAC Control */
    114	{  0xb, 0x00FF }, /* DAC Volume */
    115
    116	{  0xe, 0x0100 }, /* ADC Control */
    117	{  0xf, 0x00FF }, /* ADC Volume */
    118	{ 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */
    119	{ 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */
    120	{ 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */
    121	{ 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */
    122	{ 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */
    123	{ 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */
    124	{ 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */
    125	{ 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */
    126	{ 0x18, 0x0032 }, /* DAC Limit Control 1 */
    127	{ 0x19, 0x0000 }, /* DAC Limit Control 2 */
    128
    129	{ 0x20, 0x0038 }, /* ALC Control 1 */
    130	{ 0x21, 0x000B }, /* ALC Control 2 */
    131	{ 0x22, 0x0032 }, /* ALC Control 3 */
    132	{ 0x23, 0x0000 }, /* Noise Gate */
    133	{ 0x24, 0x0041 }, /* PLLN */
    134	{ 0x25, 0x000C }, /* PLLK1 */
    135	{ 0x26, 0x0093 }, /* PLLK2 */
    136	{ 0x27, 0x00E9 }, /* PLLK3 */
    137
    138	{ 0x2a, 0x0030 }, /* ALC Control 4 */
    139
    140	{ 0x2c, 0x0002 }, /* Input Control */
    141	{ 0x2d, 0x0050 }, /* PGA Gain */
    142
    143	{ 0x2f, 0x0002 }, /* ADC Boost Control */
    144
    145	{ 0x31, 0x0002 }, /* Output Control */
    146	{ 0x32, 0x0000 }, /* Speaker Mixer Control */
    147
    148	{ 0x36, 0x0079 }, /* Speaker Volume */
    149
    150	{ 0x38, 0x0000 }, /* Mono Mixer Control */
    151};
    152
    153static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
    154static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum,
    155			    WM8940_COMPANDINGCTL, 1, wm8940_companding);
    156static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum,
    157			    WM8940_COMPANDINGCTL, 3, wm8940_companding);
    158
    159static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
    160static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum,
    161			    WM8940_ALC3, 8, wm8940_alc_mode_text);
    162
    163static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
    164static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum,
    165			    WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text);
    166
    167static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
    168static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum,
    169			    WM8940_ADC, 7, wm8940_filter_mode_text);
    170
    171static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
    172static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
    173static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0);
    174static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0);
    175static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0);
    176static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0);
    177static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0);
    178static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0);
    179static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1);
    180static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
    181
    182static const struct snd_kcontrol_new wm8940_snd_controls[] = {
    183	SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL,
    184		   6, 1, 0),
    185	SOC_ENUM("DAC Companding", wm8940_dac_companding_enum),
    186	SOC_ENUM("ADC Companding", wm8940_adc_companding_enum),
    187
    188	SOC_ENUM("ALC Mode", wm8940_alc_mode_enum),
    189	SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0),
    190	SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1,
    191		       3, 7, 1, wm8940_alc_max_tlv),
    192	SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1,
    193		       0, 7, 0, wm8940_alc_min_tlv),
    194	SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2,
    195		       0, 14, 0, wm8940_alc_tar_tlv),
    196	SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0),
    197	SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0),
    198	SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0),
    199	SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0),
    200	SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE,
    201		   3, 1, 0),
    202	SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE,
    203		   0, 7, 0),
    204
    205	SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0),
    206	SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0),
    207	SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0),
    208	SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2,
    209		       4, 9, 1, wm8940_lim_thresh_tlv),
    210	SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2,
    211		       0, 12, 0, wm8940_lim_boost_tlv),
    212
    213	SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0),
    214	SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN,
    215		       0, 63, 0, wm8940_pga_vol_tlv),
    216	SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL,
    217		       0, 255, 0, wm8940_adc_tlv),
    218	SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL,
    219		       0, 255, 0, wm8940_adc_tlv),
    220	SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum),
    221	SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST,
    222		       8, 1, 0, wm8940_capture_boost_vol_tlv),
    223	SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL,
    224		       0, 63, 0, wm8940_spk_vol_tlv),
    225	SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL,  6, 1, 1),
    226
    227	SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL,
    228		       8, 1, 1, wm8940_att_tlv),
    229	SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0),
    230
    231	SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1),
    232	SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX,
    233		       7, 1, 1, wm8940_att_tlv),
    234
    235	SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0),
    236	SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum),
    237	SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0),
    238	SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0),
    239	SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0),
    240	SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0),
    241	SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0),
    242};
    243
    244static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = {
    245	SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0),
    246	SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0),
    247	SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0),
    248};
    249
    250static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = {
    251	SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0),
    252	SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0),
    253	SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0),
    254};
    255
    256static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1);
    257static const struct snd_kcontrol_new wm8940_input_boost_controls[] = {
    258	SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1),
    259	SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST,
    260			    0, 7, 0, wm8940_boost_vol_tlv),
    261	SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST,
    262			    4, 7, 0, wm8940_boost_vol_tlv),
    263};
    264
    265static const struct snd_kcontrol_new wm8940_micpga_controls[] = {
    266	SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0),
    267	SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0),
    268	SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0),
    269};
    270
    271static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
    272	SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0,
    273			   &wm8940_speaker_mixer_controls[0],
    274			   ARRAY_SIZE(wm8940_speaker_mixer_controls)),
    275	SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0,
    276			   &wm8940_mono_mixer_controls[0],
    277			   ARRAY_SIZE(wm8940_mono_mixer_controls)),
    278	SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0),
    279
    280	SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0),
    281	SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0),
    282	SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0),
    283	SND_SOC_DAPM_OUTPUT("MONOOUT"),
    284	SND_SOC_DAPM_OUTPUT("SPKOUTP"),
    285	SND_SOC_DAPM_OUTPUT("SPKOUTN"),
    286
    287	SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0),
    288	SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0),
    289	SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0,
    290			   &wm8940_micpga_controls[0],
    291			   ARRAY_SIZE(wm8940_micpga_controls)),
    292	SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0,
    293			   &wm8940_input_boost_controls[0],
    294			   ARRAY_SIZE(wm8940_input_boost_controls)),
    295	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0),
    296
    297	SND_SOC_DAPM_INPUT("MICN"),
    298	SND_SOC_DAPM_INPUT("MICP"),
    299	SND_SOC_DAPM_INPUT("AUX"),
    300};
    301
    302static const struct snd_soc_dapm_route wm8940_dapm_routes[] = {
    303	/* Mono output mixer */
    304	{"Mono Mixer", "PCM Playback Switch", "DAC"},
    305	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
    306	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
    307
    308	/* Speaker output mixer */
    309	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
    310	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
    311	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
    312
    313	/* Outputs */
    314	{"Mono Out", NULL, "Mono Mixer"},
    315	{"MONOOUT", NULL, "Mono Out"},
    316	{"SpkN Out", NULL, "Speaker Mixer"},
    317	{"SpkP Out", NULL, "Speaker Mixer"},
    318	{"SPKOUTN", NULL, "SpkN Out"},
    319	{"SPKOUTP", NULL, "SpkP Out"},
    320
    321	/*  Microphone PGA */
    322	{"Mic PGA", "MICN Switch", "MICN"},
    323	{"Mic PGA", "MICP Switch", "MICP"},
    324	{"Mic PGA", "AUX Switch", "AUX"},
    325
    326	/* Boost Mixer */
    327	{"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
    328	{"Boost Mixer", "Mic Volume",  "MICP"},
    329	{"Boost Mixer", "Aux Volume", "Aux Input"},
    330
    331	{"ADC", NULL, "Boost Mixer"},
    332};
    333
    334#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0);
    335
    336static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
    337			      unsigned int fmt)
    338{
    339	struct snd_soc_component *component = codec_dai->component;
    340	u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFE67;
    341	u16 clk = snd_soc_component_read(component, WM8940_CLOCK) & 0x1fe;
    342
    343	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    344	case SND_SOC_DAIFMT_CBM_CFM:
    345		clk |= 1;
    346		break;
    347	case SND_SOC_DAIFMT_CBS_CFS:
    348		break;
    349	default:
    350		return -EINVAL;
    351	}
    352	snd_soc_component_write(component, WM8940_CLOCK, clk);
    353
    354	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    355	case SND_SOC_DAIFMT_I2S:
    356		iface |= (2 << 3);
    357		break;
    358	case SND_SOC_DAIFMT_LEFT_J:
    359		iface |= (1 << 3);
    360		break;
    361	case SND_SOC_DAIFMT_RIGHT_J:
    362		break;
    363	case SND_SOC_DAIFMT_DSP_A:
    364		iface |= (3 << 3);
    365		break;
    366	case SND_SOC_DAIFMT_DSP_B:
    367		iface |= (3 << 3) | (1 << 7);
    368		break;
    369	}
    370
    371	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    372	case SND_SOC_DAIFMT_NB_NF:
    373		break;
    374	case SND_SOC_DAIFMT_NB_IF:
    375		iface |= (1 << 7);
    376		break;
    377	case SND_SOC_DAIFMT_IB_NF:
    378		iface |= (1 << 8);
    379		break;
    380	case SND_SOC_DAIFMT_IB_IF:
    381		iface |= (1 << 8) | (1 << 7);
    382		break;
    383	}
    384
    385	snd_soc_component_write(component, WM8940_IFACE, iface);
    386
    387	return 0;
    388}
    389
    390static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
    391				struct snd_pcm_hw_params *params,
    392				struct snd_soc_dai *dai)
    393{
    394	struct snd_soc_component *component = dai->component;
    395	u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFD9F;
    396	u16 addcntrl = snd_soc_component_read(component, WM8940_ADDCNTRL) & 0xFFF1;
    397	u16 companding =  snd_soc_component_read(component,
    398						WM8940_COMPANDINGCTL) & 0xFFDF;
    399	int ret;
    400
    401	/* LoutR control */
    402	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
    403	    && params_channels(params) == 2)
    404		iface |= (1 << 9);
    405
    406	switch (params_rate(params)) {
    407	case 8000:
    408		addcntrl |= (0x5 << 1);
    409		break;
    410	case 11025:
    411		addcntrl |= (0x4 << 1);
    412		break;
    413	case 16000:
    414		addcntrl |= (0x3 << 1);
    415		break;
    416	case 22050:
    417		addcntrl |= (0x2 << 1);
    418		break;
    419	case 32000:
    420		addcntrl |= (0x1 << 1);
    421		break;
    422	case 44100:
    423	case 48000:
    424		break;
    425	}
    426	ret = snd_soc_component_write(component, WM8940_ADDCNTRL, addcntrl);
    427	if (ret)
    428		goto error_ret;
    429
    430	switch (params_width(params)) {
    431	case 8:
    432		companding = companding | (1 << 5);
    433		break;
    434	case 16:
    435		break;
    436	case 20:
    437		iface |= (1 << 5);
    438		break;
    439	case 24:
    440		iface |= (2 << 5);
    441		break;
    442	case 32:
    443		iface |= (3 << 5);
    444		break;
    445	}
    446	ret = snd_soc_component_write(component, WM8940_COMPANDINGCTL, companding);
    447	if (ret)
    448		goto error_ret;
    449	ret = snd_soc_component_write(component, WM8940_IFACE, iface);
    450
    451error_ret:
    452	return ret;
    453}
    454
    455static int wm8940_mute(struct snd_soc_dai *dai, int mute, int direction)
    456{
    457	struct snd_soc_component *component = dai->component;
    458	u16 mute_reg = snd_soc_component_read(component, WM8940_DAC) & 0xffbf;
    459
    460	if (mute)
    461		mute_reg |= 0x40;
    462
    463	return snd_soc_component_write(component, WM8940_DAC, mute_reg);
    464}
    465
    466static int wm8940_set_bias_level(struct snd_soc_component *component,
    467				 enum snd_soc_bias_level level)
    468{
    469	struct wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component);
    470	u16 val;
    471	u16 pwr_reg = snd_soc_component_read(component, WM8940_POWER1) & 0x1F0;
    472	int ret = 0;
    473
    474	switch (level) {
    475	case SND_SOC_BIAS_ON:
    476		/* ensure bufioen and biasen */
    477		pwr_reg |= (1 << 2) | (1 << 3);
    478		/* Enable thermal shutdown */
    479		val = snd_soc_component_read(component, WM8940_OUTPUTCTL);
    480		ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, val | 0x2);
    481		if (ret)
    482			break;
    483		/* set vmid to 75k */
    484		ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1);
    485		break;
    486	case SND_SOC_BIAS_PREPARE:
    487		/* ensure bufioen and biasen */
    488		pwr_reg |= (1 << 2) | (1 << 3);
    489		ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1);
    490		break;
    491	case SND_SOC_BIAS_STANDBY:
    492		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
    493			ret = regcache_sync(wm8940->regmap);
    494			if (ret < 0) {
    495				dev_err(component->dev, "Failed to sync cache: %d\n", ret);
    496				return ret;
    497			}
    498		}
    499
    500		/* ensure bufioen and biasen */
    501		pwr_reg |= (1 << 2) | (1 << 3);
    502		/* set vmid to 300k for standby */
    503		ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x2);
    504		break;
    505	case SND_SOC_BIAS_OFF:
    506		ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg);
    507		break;
    508	}
    509
    510	return ret;
    511}
    512
    513struct pll_ {
    514	unsigned int pre_scale:2;
    515	unsigned int n:4;
    516	unsigned int k;
    517};
    518
    519static struct pll_ pll_div;
    520
    521/* The size in bits of the pll divide multiplied by 10
    522 * to allow rounding later */
    523#define FIXED_PLL_SIZE ((1 << 24) * 10)
    524static void pll_factors(unsigned int target, unsigned int source)
    525{
    526	unsigned long long Kpart;
    527	unsigned int K, Ndiv, Nmod;
    528	/* The left shift ist to avoid accuracy loss when right shifting */
    529	Ndiv = target / source;
    530
    531	if (Ndiv > 12) {
    532		source <<= 1;
    533		/* Multiply by 2 */
    534		pll_div.pre_scale = 0;
    535		Ndiv = target / source;
    536	} else if (Ndiv < 3) {
    537		source >>= 2;
    538		/* Divide by 4 */
    539		pll_div.pre_scale = 3;
    540		Ndiv = target / source;
    541	} else if (Ndiv < 6) {
    542		source >>= 1;
    543		/* divide by 2 */
    544		pll_div.pre_scale = 2;
    545		Ndiv = target / source;
    546	} else
    547		pll_div.pre_scale = 1;
    548
    549	if ((Ndiv < 6) || (Ndiv > 12))
    550		printk(KERN_WARNING
    551			"WM8940 N value %d outwith recommended range!d\n",
    552			Ndiv);
    553
    554	pll_div.n = Ndiv;
    555	Nmod = target % source;
    556	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
    557
    558	do_div(Kpart, source);
    559
    560	K = Kpart & 0xFFFFFFFF;
    561
    562	/* Check if we need to round */
    563	if ((K % 10) >= 5)
    564		K += 5;
    565
    566	/* Move down to proper range now rounding is done */
    567	K /= 10;
    568
    569	pll_div.k = K;
    570}
    571
    572/* Untested at the moment */
    573static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
    574		int source, unsigned int freq_in, unsigned int freq_out)
    575{
    576	struct snd_soc_component *component = codec_dai->component;
    577	u16 reg;
    578
    579	/* Turn off PLL */
    580	reg = snd_soc_component_read(component, WM8940_POWER1);
    581	snd_soc_component_write(component, WM8940_POWER1, reg & 0x1df);
    582
    583	if (freq_in == 0 || freq_out == 0) {
    584		/* Clock CODEC directly from MCLK */
    585		reg = snd_soc_component_read(component, WM8940_CLOCK);
    586		snd_soc_component_write(component, WM8940_CLOCK, reg & 0x0ff);
    587		/* Pll power down */
    588		snd_soc_component_write(component, WM8940_PLLN, (1 << 7));
    589		return 0;
    590	}
    591
    592	/* Pll is followed by a frequency divide by 4 */
    593	pll_factors(freq_out*4, freq_in);
    594	if (pll_div.k)
    595		snd_soc_component_write(component, WM8940_PLLN,
    596			     (pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
    597	else /* No factional component */
    598		snd_soc_component_write(component, WM8940_PLLN,
    599			     (pll_div.pre_scale << 4) | pll_div.n);
    600	snd_soc_component_write(component, WM8940_PLLK1, pll_div.k >> 18);
    601	snd_soc_component_write(component, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
    602	snd_soc_component_write(component, WM8940_PLLK3, pll_div.k & 0x1ff);
    603	/* Enable the PLL */
    604	reg = snd_soc_component_read(component, WM8940_POWER1);
    605	snd_soc_component_write(component, WM8940_POWER1, reg | 0x020);
    606
    607	/* Run CODEC from PLL instead of MCLK */
    608	reg = snd_soc_component_read(component, WM8940_CLOCK);
    609	snd_soc_component_write(component, WM8940_CLOCK, reg | 0x100);
    610
    611	return 0;
    612}
    613
    614static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
    615				 int clk_id, unsigned int freq, int dir)
    616{
    617	struct snd_soc_component *component = codec_dai->component;
    618	struct wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component);
    619
    620	switch (freq) {
    621	case 11289600:
    622	case 12000000:
    623	case 12288000:
    624	case 16934400:
    625	case 18432000:
    626		wm8940->sysclk = freq;
    627		return 0;
    628	}
    629	return -EINVAL;
    630}
    631
    632static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
    633				 int div_id, int div)
    634{
    635	struct snd_soc_component *component = codec_dai->component;
    636	u16 reg;
    637	int ret = 0;
    638
    639	switch (div_id) {
    640	case WM8940_BCLKDIV:
    641		reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFFE3;
    642		ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 2));
    643		break;
    644	case WM8940_MCLKDIV:
    645		reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFF1F;
    646		ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 5));
    647		break;
    648	case WM8940_OPCLKDIV:
    649		reg = snd_soc_component_read(component, WM8940_GPIO) & 0xFFCF;
    650		ret = snd_soc_component_write(component, WM8940_GPIO, reg | (div << 4));
    651		break;
    652	}
    653	return ret;
    654}
    655
    656#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
    657
    658#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 |				\
    659			SNDRV_PCM_FMTBIT_S16_LE |			\
    660			SNDRV_PCM_FMTBIT_S20_3LE |			\
    661			SNDRV_PCM_FMTBIT_S24_LE |			\
    662			SNDRV_PCM_FMTBIT_S32_LE)
    663
    664static const struct snd_soc_dai_ops wm8940_dai_ops = {
    665	.hw_params = wm8940_i2s_hw_params,
    666	.set_sysclk = wm8940_set_dai_sysclk,
    667	.mute_stream = wm8940_mute,
    668	.set_fmt = wm8940_set_dai_fmt,
    669	.set_clkdiv = wm8940_set_dai_clkdiv,
    670	.set_pll = wm8940_set_dai_pll,
    671	.no_capture_mute = 1,
    672};
    673
    674static struct snd_soc_dai_driver wm8940_dai = {
    675	.name = "wm8940-hifi",
    676	.playback = {
    677		.stream_name = "Playback",
    678		.channels_min = 1,
    679		.channels_max = 2,
    680		.rates = WM8940_RATES,
    681		.formats = WM8940_FORMATS,
    682	},
    683	.capture = {
    684		.stream_name = "Capture",
    685		.channels_min = 1,
    686		.channels_max = 2,
    687		.rates = WM8940_RATES,
    688		.formats = WM8940_FORMATS,
    689	},
    690	.ops = &wm8940_dai_ops,
    691	.symmetric_rate = 1,
    692};
    693
    694static int wm8940_probe(struct snd_soc_component *component)
    695{
    696	struct wm8940_setup_data *pdata = component->dev->platform_data;
    697	int ret;
    698	u16 reg;
    699
    700	ret = wm8940_reset(component);
    701	if (ret < 0) {
    702		dev_err(component->dev, "Failed to issue reset\n");
    703		return ret;
    704	}
    705
    706	snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
    707
    708	ret = snd_soc_component_write(component, WM8940_POWER1, 0x180);
    709	if (ret < 0)
    710		return ret;
    711
    712	if (!pdata)
    713		dev_warn(component->dev, "No platform data supplied\n");
    714	else {
    715		reg = snd_soc_component_read(component, WM8940_OUTPUTCTL);
    716		ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, reg | pdata->vroi);
    717		if (ret < 0)
    718			return ret;
    719	}
    720
    721	return ret;
    722}
    723
    724static const struct snd_soc_component_driver soc_component_dev_wm8940 = {
    725	.probe			= wm8940_probe,
    726	.set_bias_level		= wm8940_set_bias_level,
    727	.controls		= wm8940_snd_controls,
    728	.num_controls		= ARRAY_SIZE(wm8940_snd_controls),
    729	.dapm_widgets		= wm8940_dapm_widgets,
    730	.num_dapm_widgets	= ARRAY_SIZE(wm8940_dapm_widgets),
    731	.dapm_routes		= wm8940_dapm_routes,
    732	.num_dapm_routes	= ARRAY_SIZE(wm8940_dapm_routes),
    733	.suspend_bias_off	= 1,
    734	.idle_bias_on		= 1,
    735	.use_pmdown_time	= 1,
    736	.endianness		= 1,
    737	.non_legacy_dai_naming	= 1,
    738};
    739
    740static const struct regmap_config wm8940_regmap = {
    741	.reg_bits = 8,
    742	.val_bits = 16,
    743
    744	.max_register = WM8940_MONOMIX,
    745	.reg_defaults = wm8940_reg_defaults,
    746	.num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
    747	.cache_type = REGCACHE_RBTREE,
    748
    749	.readable_reg = wm8940_readable_register,
    750	.volatile_reg = wm8940_volatile_register,
    751};
    752
    753static int wm8940_i2c_probe(struct i2c_client *i2c)
    754{
    755	struct wm8940_priv *wm8940;
    756	int ret;
    757
    758	wm8940 = devm_kzalloc(&i2c->dev, sizeof(struct wm8940_priv),
    759			      GFP_KERNEL);
    760	if (wm8940 == NULL)
    761		return -ENOMEM;
    762
    763	wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap);
    764	if (IS_ERR(wm8940->regmap))
    765		return PTR_ERR(wm8940->regmap);
    766
    767	i2c_set_clientdata(i2c, wm8940);
    768
    769	ret = devm_snd_soc_register_component(&i2c->dev,
    770			&soc_component_dev_wm8940, &wm8940_dai, 1);
    771
    772	return ret;
    773}
    774
    775static const struct i2c_device_id wm8940_i2c_id[] = {
    776	{ "wm8940", 0 },
    777	{ }
    778};
    779MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
    780
    781static const struct of_device_id wm8940_of_match[] = {
    782	{ .compatible = "wlf,wm8940", },
    783	{ }
    784};
    785MODULE_DEVICE_TABLE(of, wm8940_of_match);
    786
    787static struct i2c_driver wm8940_i2c_driver = {
    788	.driver = {
    789		.name = "wm8940",
    790		.of_match_table = wm8940_of_match,
    791	},
    792	.probe_new = wm8940_i2c_probe,
    793	.id_table = wm8940_i2c_id,
    794};
    795
    796module_i2c_driver(wm8940_i2c_driver);
    797
    798MODULE_DESCRIPTION("ASoC WM8940 driver");
    799MODULE_AUTHOR("Jonathan Cameron");
    800MODULE_LICENSE("GPL");