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