/*
* wm8940.c -- WM8940 ALSA Soc Audio driver
*
* Author: Jonathan Cameron <jic23@cam.ac.uk>
*
* Based on wm8510.c
* Copyright 2006 Wolfson Microelectronics PLC.
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Not currently handled:
* Notch filter control
* AUXMode (inverting vs mixer)
* No means to obtain current gain if alc enabled.
* No use made of gpio
* Fast VMID discharge for power down
* Soft Start
* DLR and ALR Swaps not enabled
* Digital Sidetone not supported
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "wm8940.h"
struct wm8940_priv {
unsigned int sysclk;
struct regmap *regmap;
};
static bool wm8940_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8940_SOFTRESET:
return true;
default:
return false;
}
}
static bool wm8940_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8940_SOFTRESET:
case WM8940_POWER1:
case WM8940_POWER2:
case WM8940_POWER3:
case WM8940_IFACE:
case WM8940_COMPANDINGCTL:
case WM8940_CLOCK:
case WM8940_ADDCNTRL:
case WM8940_GPIO:
case WM8940_CTLINT:
case WM8940_DAC:
case WM8940_DACVOL:
case WM8940_ADC:
case WM8940_ADCVOL:
case WM8940_NOTCH1:
case WM8940_NOTCH2:
case WM8940_NOTCH3:
case WM8940_NOTCH4:
case WM8940_NOTCH5:
case WM8940_NOTCH6:
case WM8940_NOTCH7:
case WM8940_NOTCH8:
case WM8940_DACLIM1:
case WM8940_DACLIM2:
case WM8940_ALC1:
case WM8940_ALC2:
case WM8940_ALC3:
case WM8940_NOISEGATE:
case WM8940_PLLN:
case WM8940_PLLK1:
case WM8940_PLLK2:
case WM8940_PLLK3:
case WM8940_ALC4:
case WM8940_INPUTCTL:
case WM8940_PGAGAIN:
case WM8940_ADCBOOST:
case WM8940_OUTPUTCTL:
case WM8940_SPKMIX:
case WM8940_SPKVOL:
case WM8940_MONOMIX:
return true;
default:
return false;
}
}
static const struct reg_default wm8940_reg_defaults[] = {
{ 0x1, 0x0000 }, /* Power 1 */
{ 0x2, 0x0000 }, /* Power 2 */
{ 0x3, 0x0000 }, /* Power 3 */
{ 0x4, 0x0010 }, /* Interface Control */
{ 0x5, 0x0000 }, /* Companding Control */
{ 0x6, 0x0140 }, /* Clock Control */
{ 0x7, 0x0000 }, /* Additional Controls */
{ 0x8, 0x0000 }, /* GPIO Control */
{ 0x9, 0x0002 }, /* Auto Increment Control */
{ 0xa, 0x0000 }, /* DAC Control */
{ 0xb, 0x00FF }, /* DAC Volume */
{ 0xe, 0x0100 }, /* ADC Control */
{ 0xf, 0x00FF }, /* ADC Volume */
{ 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */
{ 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */
{ 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */
{ 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */
{ 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */
{ 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */
{ 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */
{ 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */
{ 0x18, 0x0032 }, /* DAC Limit Control 1 */
{ 0x19, 0x0000 }, /* DAC Limit Control 2 */
{ 0x20, 0x0038 }, /* ALC Control 1 */
{ 0x21, 0x000B }, /* ALC Control 2 */
{ 0x22, 0x0032 }, /* ALC Control 3 */
{ 0x23, 0x0000 }, /* Noise Gate */
{ 0x24, 0x0041 }, /* PLLN */
{ 0x25, 0x000C }, /* PLLK1 */
{ 0x26, 0x0093 }, /* PLLK2 */
{ 0x27, 0x00E9 }, /* PLLK3 */
{ 0x2a, 0x0030 }, /* ALC Control 4 */
{ 0x2c, 0x0002 }, /* Input Control */
{ 0x2d, 0x0050 }, /* PGA Gain */
{ 0x2f, 0x0002 }, /* ADC Boost Control */
{ 0x31, 0x0002 }, /* Output Control */
{ 0x32, 0x0000 }, /* Speaker Mixer Control */
{ 0x36, 0x0079 }, /* Speaker Volume */
{ 0x38, 0x0000 }, /* Mono Mixer Control */
};
static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum,
WM8940_COMPANDINGCTL, 1, wm8940_companding);
static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum,
WM8940_COMPANDINGCTL, 3, wm8940_companding);
static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum,
WM8940_ALC3, 8, wm8940_alc_mode_text);
static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum,
WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text);
static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum,
WM8940_ADC, 7, wm8940_filter_mode_text);
static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0);
static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0);
static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0);
static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0);
static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0);
static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0);
static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1);
static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
static const struct snd_kcontrol_new wm8940_snd_controls[] = {
SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL,
6, 1, 0),
SOC_ENUM("DAC Companding", wm8940_dac_companding_enum),
SOC_ENUM("ADC Companding", wm8940_adc_companding_enum),
SOC_ENUM("ALC Mode", wm8940_alc_mode_enum),
SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0),
SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1,
3, 7, 1, wm8940_alc_max_tlv),
SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1,
0, 7, 0, wm8940_alc_min_tlv),
SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2,
0, 14, 0, wm8940_alc_tar_tlv),
SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0),
SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0),
SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0),
SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0),
SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE,
3, 1, 0),
SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE,
0, 7, 0),
SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0),
SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0),
SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0),
SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2,
4, 9, 1, wm8940_lim_thresh_tlv),
SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2,
0, 12, 0, wm8940_lim_boost_tlv),
SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0),
SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN,
0, 63, 0, wm8940_pga_vol_tlv),
SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL,
0, 255, 0, wm8940_adc_tlv),
SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL,
0, 255, 0, wm8940_adc_tlv),
SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum),
SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST,
8, 1, 0, wm8940_capture_boost_vol_tlv),
SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL,
0, 63, 0, wm8940_spk_vol_tlv),
SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL, 6, 1, 1),
SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL,
8, 1, 1, wm8940_att_tlv),
SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0),
SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1),
SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX,
7, 1, 1, wm8940_att_tlv),
SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0),
SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum),
SOC_SINGLE("High Pass Filter