brintos

brintos / linux-shallow public Read only

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