Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/ti/wl18xx/   (Linux Kernel Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 1 kB image not shown  

Quelle  wm8991.c

  Sprache: C
 

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * wm8991.c  --  WM8991 ALSA Soc Audio driver
 *
 * Copyright 2007-2010 Wolfson Microelectronics PLC.
 * Author: Graeme Gregory
 *         Graeme.Gregory@wolfsonmicro.com
 */


#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/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <asm/div64.h>

#include "wm8991.h"

struct wm8991_priv {
 struct regmap *regmap;
 unsigned int pcmclk;
};

static const struct reg_default wm8991_reg_defaults[] = {
 {  10x0000 },     /* R1  - Power Management (1) */
 {  20x6000 },     /* R2  - Power Management (2) */
 {  30x0000 },     /* R3  - Power Management (3) */
 {  40x4050 },     /* R4  - Audio Interface (1) */
 {  50x4000 },     /* R5  - Audio Interface (2) */
 {  60x01C8 },     /* R6  - Clocking (1) */
 {  70x0000 },     /* R7  - Clocking (2) */
 {  80x0040 },     /* R8  - Audio Interface (3) */
 {  90x0040 },     /* R9  - Audio Interface (4) */
 { 100x0004 },     /* R10 - DAC CTRL */
 { 110x00C0 },     /* R11 - Left DAC Digital Volume */
 { 120x00C0 },     /* R12 - Right DAC Digital Volume */
 { 130x0000 },     /* R13 - Digital Side Tone */
 { 140x0100 },     /* R14 - ADC CTRL */
 { 150x00C0 },     /* R15 - Left ADC Digital Volume */
 { 160x00C0 },     /* R16 - Right ADC Digital Volume */

 { 180x0000 },     /* R18 - GPIO CTRL 1 */
 { 190x1000 },     /* R19 - GPIO1 & GPIO2 */
 { 200x1010 },     /* R20 - GPIO3 & GPIO4 */
 { 210x1010 },     /* R21 - GPIO5 & GPIO6 */
 { 220x8000 },     /* R22 - GPIOCTRL 2 */
 { 230x0800 },     /* R23 - GPIO_POL */
 { 240x008B },     /* R24 - Left Line Input 1&2 Volume */
 { 250x008B },     /* R25 - Left Line Input 3&4 Volume */
 { 260x008B },     /* R26 - Right Line Input 1&2 Volume */
 { 270x008B },     /* R27 - Right Line Input 3&4 Volume */
 { 280x0000 },     /* R28 - Left Output Volume */
 { 290x0000 },     /* R29 - Right Output Volume */
 { 300x0066 },     /* R30 - Line Outputs Volume */
 { 310x0022 },     /* R31 - Out3/4 Volume */
 { 320x0079 },     /* R32 - Left OPGA Volume */
 { 330x0079 },     /* R33 - Right OPGA Volume */
 { 340x0003 },     /* R34 - Speaker Volume */
 { 350x0003 },     /* R35 - ClassD1 */

 { 370x0100 },     /* R37 - ClassD3 */

 { 390x0000 },     /* R39 - Input Mixer1 */
 { 400x0000 },     /* R40 - Input Mixer2 */
 { 410x0000 },     /* R41 - Input Mixer3 */
 { 420x0000 },     /* R42 - Input Mixer4 */
 { 430x0000 },     /* R43 - Input Mixer5 */
 { 440x0000 },     /* R44 - Input Mixer6 */
 { 450x0000 },     /* R45 - Output Mixer1 */
 { 460x0000 },     /* R46 - Output Mixer2 */
 { 470x0000 },     /* R47 - Output Mixer3 */
 { 480x0000 },     /* R48 - Output Mixer4 */
 { 490x0000 },     /* R49 - Output Mixer5 */
 { 500x0000 },     /* R50 - Output Mixer6 */
 { 510x0180 },     /* R51 - Out3/4 Mixer */
 { 520x0000 },     /* R52 - Line Mixer1 */
 { 530x0000 },     /* R53 - Line Mixer2 */
 { 540x0000 },     /* R54 - Speaker Mixer */
 { 550x0000 },     /* R55 - Additional Control */
 { 560x0000 },     /* R56 - AntiPOP1 */
 { 570x0000 },     /* R57 - AntiPOP2 */
 { 580x0000 },     /* R58 - MICBIAS */

 { 600x0008 },     /* R60 - PLL1 */
 { 610x0031 },     /* R61 - PLL2 */
 { 620x0026 },     /* R62 - PLL3 */
};

static bool wm8991_volatile(struct device *dev, unsigned int reg)
{
 switch (reg) {
 case WM8991_RESET:
  return true;
 default:
  return false;
 }
}

static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_pga_tlv, -16501500);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(out_mix_tlv, -21003000);
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_pga_tlv,
 0x00, 0x2f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(SNDRV_CTL_TLVD_DB_GAIN_MUTE, 01),
 0x30, 0x7f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-73001000),
);
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_dac_tlv,
 0x00, 0xbf, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-716253751),
 0xc0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(000),
);
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(in_adc_tlv,
 0x00, 0xef, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-716253751),
 0xf0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(1762500),
);
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_sidetone_tlv,
 0x00, 0x0c, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-36003000),
 0x0d, 0x0f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(000),
);

static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
          struct snd_ctl_elem_value *ucontrol)
{
 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 int reg = kcontrol->private_value & 0xff;
 int ret;
 u16 val;

 ret = snd_soc_put_volsw(kcontrol, ucontrol);
 if (ret < 0)
  return ret;

 /* now hit the volume update bits (always bit 8) */
 val = snd_soc_component_read(component, reg);
 return snd_soc_component_write(component, reg, val | 0x0100);
}

static const char *wm8991_digital_sidetone[] =
{"None""Left ADC""Right ADC""Reserved"};

static SOC_ENUM_SINGLE_DECL(wm8991_left_digital_sidetone_enum,
       WM8991_DIGITAL_SIDE_TONE,
       WM8991_ADC_TO_DACL_SHIFT,
       wm8991_digital_sidetone);

static SOC_ENUM_SINGLE_DECL(wm8991_right_digital_sidetone_enum,
       WM8991_DIGITAL_SIDE_TONE,
       WM8991_ADC_TO_DACR_SHIFT,
       wm8991_digital_sidetone);

static const char *wm8991_adcmode[] =
{"Hi-fi mode""Voice mode 1""Voice mode 2""Voice mode 3"};

static SOC_ENUM_SINGLE_DECL(wm8991_right_adcmode_enum,
       WM8991_ADC_CTRL,
       WM8991_ADC_HPF_CUT_SHIFT,
       wm8991_adcmode);

static const struct snd_kcontrol_new wm8991_snd_controls[] = {
 /* INMIXL */
 SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 10),
 SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 10),
 /* INMIXR */
 SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 10),
 SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 10),

 /* LOMIX */
 SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3,
  WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
  WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
  WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5,
  WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
  WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
  WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),

 /* ROMIX */
 SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4,
  WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
  WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
  WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6,
  WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
  WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv),
 SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
  WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv),

 /* LOUT */
 SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME,
  WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv),
 SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 10),

 /* ROUT */
 SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME,
  WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv),
 SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 10),

 /* LOPGA */
 SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME,
  WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv),
 SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME,
  WM8991_LOPGAZC_BIT, 10),

 /* ROPGA */
 SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME,
  WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv),
 SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME,
  WM8991_ROPGAZC_BIT, 10),

 SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
  WM8991_LONMUTE_BIT, 10),
 SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
  WM8991_LOPMUTE_BIT, 10),
 SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
  WM8991_LOATTN_BIT, 10),
 SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
  WM8991_RONMUTE_BIT, 10),
 SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
  WM8991_ROPMUTE_BIT, 10),
 SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
  WM8991_ROATTN_BIT, 10),

 SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME,
  WM8991_OUT3MUTE_BIT, 10),
 SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME,
  WM8991_OUT3ATTN_BIT, 10),

 SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME,
  WM8991_OUT4MUTE_BIT, 10),
 SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME,
  WM8991_OUT4ATTN_BIT, 10),

 SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1,
  WM8991_CDMODE_BIT, 10),

 SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME,
  WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0),
 SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3,
  WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0),
 SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3,
  WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
  WM8991_LEFT_DAC_DIGITAL_VOLUME,
  WM8991_DACL_VOL_SHIFT,
  WM8991_DACL_VOL_MASK,
  0,
  out_dac_tlv),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
  WM8991_RIGHT_DAC_DIGITAL_VOLUME,
  WM8991_DACR_VOL_SHIFT,
  WM8991_DACR_VOL_MASK,
  0,
  out_dac_tlv),

 SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum),
 SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum),

 SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
  WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0,
  out_sidetone_tlv),
 SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
  WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0,
  out_sidetone_tlv),

 SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL,
  WM8991_ADC_HPF_ENA_BIT, 10),

 SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
  WM8991_LEFT_ADC_DIGITAL_VOLUME,
  WM8991_ADCL_VOL_SHIFT,
  WM8991_ADCL_VOL_MASK,
  0,
  in_adc_tlv),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
  WM8991_RIGHT_ADC_DIGITAL_VOLUME,
  WM8991_ADCR_VOL_SHIFT,
  WM8991_ADCR_VOL_MASK,
  0,
  in_adc_tlv),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
  WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
  WM8991_LIN12VOL_SHIFT,
  WM8991_LIN12VOL_MASK,
  0,
  in_pga_tlv),

 SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
  WM8991_LI12ZC_BIT, 10),

 SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
  WM8991_LI12MUTE_BIT, 10),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
  WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
  WM8991_LIN34VOL_SHIFT,
  WM8991_LIN34VOL_MASK,
  0,
  in_pga_tlv),

 SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
  WM8991_LI34ZC_BIT, 10),

 SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
  WM8991_LI34MUTE_BIT, 10),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
  WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
  WM8991_RIN12VOL_SHIFT,
  WM8991_RIN12VOL_MASK,
  0,
  in_pga_tlv),

 SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
  WM8991_RI12ZC_BIT, 10),

 SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
  WM8991_RI12MUTE_BIT, 10),

 SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
  WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
  WM8991_RIN34VOL_SHIFT,
  WM8991_RIN34VOL_MASK,
  0,
  in_pga_tlv),

 SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
  WM8991_RI34ZC_BIT, 10),

 SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
  WM8991_RI34MUTE_BIT, 10),
};

/*
 * _DAPM_ Controls
 */

static int outmixer_event(struct snd_soc_dapm_widget *w,
     struct snd_kcontrol *kcontrol, int event)
{
 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 u32 reg_shift = kcontrol->private_value & 0xfff;
 int ret = 0;
 u16 reg;

 switch (reg_shift) {
 case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8):
  reg = snd_soc_component_read(component, WM8991_OUTPUT_MIXER1);
  if (reg & WM8991_LDLO) {
   printk(KERN_WARNING
          "Cannot set as Output Mixer 1 LDLO Set\n");
   ret = -1;
  }
  break;

 case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8):
  reg = snd_soc_component_read(component, WM8991_OUTPUT_MIXER2);
  if (reg & WM8991_RDRO) {
   printk(KERN_WARNING
          "Cannot set as Output Mixer 2 RDRO Set\n");
   ret = -1;
  }
  break;

 case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8):
  reg = snd_soc_component_read(component, WM8991_SPEAKER_MIXER);
  if (reg & WM8991_LDSPK) {
   printk(KERN_WARNING
          "Cannot set as Speaker Mixer LDSPK Set\n");
   ret = -1;
  }
  break;

 case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8):
  reg = snd_soc_component_read(component, WM8991_SPEAKER_MIXER);
  if (reg & WM8991_RDSPK) {
   printk(KERN_WARNING
          "Cannot set as Speaker Mixer RDSPK Set\n");
   ret = -1;
  }
  break;
 }

 return ret;
}

/* INMIX dB values */
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_mix_tlv, -12003001);

/* Left In PGA Connections */
static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
 SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 10),
 SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 10),
};

static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = {
 SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 10),
 SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 10),
};

/* Right In PGA Connections */
static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = {
 SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 10),
 SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 10),
};

static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = {
 SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 10),
 SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 10),
};

/* INMIXL */
static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = {
 SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3,
  WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv),
 SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT,
  70, in_mix_tlv),
 SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
  10),
 SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
  10),
};

/* INMIXR */
static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
 SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4,
  WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv),
 SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT,
  70, in_mix_tlv),
 SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
  10),
 SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
  10),
};

/* AINLMUX */
static const char *wm8991_ainlmux[] =
{"INMIXL Mix""RXVOICE Mix""DIFFINL Mix"};

static SOC_ENUM_SINGLE_DECL(wm8991_ainlmux_enum,
       WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
       wm8991_ainlmux);

static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
 SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);

/* DIFFINL */

/* AINRMUX */
static const char *wm8991_ainrmux[] =
{"INMIXR Mix""RXVOICE Mix""DIFFINR Mix"};

static SOC_ENUM_SINGLE_DECL(wm8991_ainrmux_enum,
       WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
       wm8991_ainrmux);

static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
 SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);

/* LOMIX */
static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = {
 SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
  WM8991_LRBLO_BIT, 10),
 SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
  WM8991_LLBLO_BIT, 10),
 SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
  WM8991_LRI3LO_BIT, 10),
 SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
  WM8991_LLI3LO_BIT, 10),
 SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
  WM8991_LR12LO_BIT, 10),
 SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
  WM8991_LL12LO_BIT, 10),
 SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1,
  WM8991_LDLO_BIT, 10),
};

/* ROMIX */
static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = {
 SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
  WM8991_RLBRO_BIT, 10),
 SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
  WM8991_RRBRO_BIT, 10),
 SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
  WM8991_RLI3RO_BIT, 10),
 SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
  WM8991_RRI3RO_BIT, 10),
 SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
  WM8991_RL12RO_BIT, 10),
 SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
  WM8991_RR12RO_BIT, 10),
 SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2,
  WM8991_RDRO_BIT, 10),
};

/* LONMIX */
static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = {
 SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
  WM8991_LLOPGALON_BIT, 10),
 SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1,
  WM8991_LROPGALON_BIT, 10),
 SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1,
  WM8991_LOPLON_BIT, 10),
};

/* LOPMIX */
static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = {
 SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1,
  WM8991_LR12LOP_BIT, 10),
 SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1,
  WM8991_LL12LOP_BIT, 10),
 SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
  WM8991_LLOPGALOP_BIT, 10),
};

/* RONMIX */
static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = {
 SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
  WM8991_RROPGARON_BIT, 10),
 SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2,
  WM8991_RLOPGARON_BIT, 10),
 SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2,
  WM8991_ROPRON_BIT, 10),
};

/* ROPMIX */
static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = {
 SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2,
  WM8991_RL12ROP_BIT, 10),
 SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2,
  WM8991_RR12ROP_BIT, 10),
 SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
  WM8991_RROPGAROP_BIT, 10),
};

/* OUT3MIX */
static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = {
 SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER,
  WM8991_LI4O3_BIT, 10),
 SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER,
  WM8991_LPGAO3_BIT, 10),
};

/* OUT4MIX */
static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = {
 SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER,
  WM8991_RPGAO4_BIT, 10),
 SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER,
  WM8991_RI4O4_BIT, 10),
};

/* SPKMIX */
static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = {
 SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
  WM8991_LI2SPK_BIT, 10),
 SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER,
  WM8991_LB2SPK_BIT, 10),
 SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER,
  WM8991_LOPGASPK_BIT, 10),
 SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER,
  WM8991_LDSPK_BIT, 10),
 SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER,
  WM8991_RDSPK_BIT, 10),
 SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER,
  WM8991_ROPGASPK_BIT, 10),
 SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER,
  WM8991_RL12ROP_BIT, 10),
 SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
  WM8991_RI2SPK_BIT, 10),
};

static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
 /* Input Side */
 /* Input Lines */
 SND_SOC_DAPM_INPUT("LIN1"),
 SND_SOC_DAPM_INPUT("LIN2"),
 SND_SOC_DAPM_INPUT("LIN3"),
 SND_SOC_DAPM_INPUT("LIN4RXN"),
 SND_SOC_DAPM_INPUT("RIN3"),
 SND_SOC_DAPM_INPUT("RIN4RXP"),
 SND_SOC_DAPM_INPUT("RIN1"),
 SND_SOC_DAPM_INPUT("RIN2"),
 SND_SOC_DAPM_INPUT("Internal ADC Source"),

 SND_SOC_DAPM_SUPPLY("INL", WM8991_POWER_MANAGEMENT_2,
       WM8991_AINL_ENA_BIT, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("INR", WM8991_POWER_MANAGEMENT_2,
       WM8991_AINR_ENA_BIT, 0, NULL, 0),

 /* DACs */
 SND_SOC_DAPM_ADC("Left ADC""Left Capture", WM8991_POWER_MANAGEMENT_2,
  WM8991_ADCL_ENA_BIT, 0),
 SND_SOC_DAPM_ADC("Right ADC""Right Capture", WM8991_POWER_MANAGEMENT_2,
  WM8991_ADCR_ENA_BIT, 0),

 /* Input PGAs */
 SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT,
  0, &wm8991_dapm_lin12_pga_controls[0],
  ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)),
 SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT,
  0, &wm8991_dapm_lin34_pga_controls[0],
  ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)),
 SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT,
  0, &wm8991_dapm_rin12_pga_controls[0],
  ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)),
 SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT,
  0, &wm8991_dapm_rin34_pga_controls[0],
  ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)),

 /* INMIXL */
 SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 00,
  &wm8991_dapm_inmixl_controls[0],
  ARRAY_SIZE(wm8991_dapm_inmixl_controls)),

 /* AINLMUX */
 SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 00,
  &wm8991_dapm_ainlmux_controls),

 /* INMIXR */
 SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 00,
  &wm8991_dapm_inmixr_controls[0],
  ARRAY_SIZE(wm8991_dapm_inmixr_controls)),

 /* AINRMUX */
 SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 00,
  &wm8991_dapm_ainrmux_controls),

 /* Output Side */
 /* DACs */
 SND_SOC_DAPM_DAC("Left DAC""Left Playback", WM8991_POWER_MANAGEMENT_3,
  WM8991_DACL_ENA_BIT, 0),
 SND_SOC_DAPM_DAC("Right DAC""Right Playback", WM8991_POWER_MANAGEMENT_3,
  WM8991_DACR_ENA_BIT, 0),

 /* LOMIX */
 SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT,
  0, &wm8991_dapm_lomix_controls[0],
  ARRAY_SIZE(wm8991_dapm_lomix_controls),
  outmixer_event, SND_SOC_DAPM_PRE_REG),

 /* LONMIX */
 SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0,
  &wm8991_dapm_lonmix_controls[0],
  ARRAY_SIZE(wm8991_dapm_lonmix_controls)),

 /* LOPMIX */
 SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0,
  &wm8991_dapm_lopmix_controls[0],
  ARRAY_SIZE(wm8991_dapm_lopmix_controls)),

 /* OUT3MIX */
 SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0,
  &wm8991_dapm_out3mix_controls[0],
  ARRAY_SIZE(wm8991_dapm_out3mix_controls)),

 /* SPKMIX */
 SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0,
  &wm8991_dapm_spkmix_controls[0],
  ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event,
  SND_SOC_DAPM_PRE_REG),

 /* OUT4MIX */
 SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0,
  &wm8991_dapm_out4mix_controls[0],
  ARRAY_SIZE(wm8991_dapm_out4mix_controls)),

 /* ROPMIX */
 SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0,
  &wm8991_dapm_ropmix_controls[0],
  ARRAY_SIZE(wm8991_dapm_ropmix_controls)),

 /* RONMIX */
 SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0,
  &wm8991_dapm_ronmix_controls[0],
  ARRAY_SIZE(wm8991_dapm_ronmix_controls)),

 /* ROMIX */
 SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT,
  0, &wm8991_dapm_romix_controls[0],
  ARRAY_SIZE(wm8991_dapm_romix_controls),
  outmixer_event, SND_SOC_DAPM_PRE_REG),

 /* LOUT PGA */
 SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0,
  NULL, 0),

 /* ROUT PGA */
 SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0,
  NULL, 0),

 /* LOPGA */
 SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0,
  NULL, 0),

 /* ROPGA */
 SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0,
  NULL, 0),

 /* MICBIAS */
 SND_SOC_DAPM_SUPPLY("MICBIAS", WM8991_POWER_MANAGEMENT_1,
       WM8991_MICBIAS_ENA_BIT, 0, NULL, 0),

 SND_SOC_DAPM_OUTPUT("LON"),
 SND_SOC_DAPM_OUTPUT("LOP"),
 SND_SOC_DAPM_OUTPUT("OUT3"),
 SND_SOC_DAPM_OUTPUT("LOUT"),
 SND_SOC_DAPM_OUTPUT("SPKN"),
 SND_SOC_DAPM_OUTPUT("SPKP"),
 SND_SOC_DAPM_OUTPUT("ROUT"),
 SND_SOC_DAPM_OUTPUT("OUT4"),
 SND_SOC_DAPM_OUTPUT("ROP"),
 SND_SOC_DAPM_OUTPUT("RON"),
 SND_SOC_DAPM_OUTPUT("OUT"),

 SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
};

static const struct snd_soc_dapm_route wm8991_dapm_routes[] = {
 /* Make DACs turn on when playing even if not mixed into any outputs */
 {"Internal DAC Sink", NULL, "Left DAC"},
 {"Internal DAC Sink", NULL, "Right DAC"},

 /* Make ADCs turn on when recording even if not mixed from any inputs */
 {"Left ADC", NULL, "Internal ADC Source"},
 {"Right ADC", NULL, "Internal ADC Source"},

 /* Input Side */
 {"INMIXL", NULL, "INL"},
 {"AINLMUX", NULL, "INL"},
 {"INMIXR", NULL, "INR"},
 {"AINRMUX", NULL, "INR"},
 /* LIN12 PGA */
 {"LIN12 PGA""LIN1 Switch""LIN1"},
 {"LIN12 PGA""LIN2 Switch""LIN2"},
 /* LIN34 PGA */
 {"LIN34 PGA""LIN3 Switch""LIN3"},
 {"LIN34 PGA""LIN4 Switch""LIN4RXN"},
 /* INMIXL */
 {"INMIXL""Record Left Volume""LOMIX"},
 {"INMIXL""LIN2 Volume""LIN2"},
 {"INMIXL""LINPGA12 Switch""LIN12 PGA"},
 {"INMIXL""LINPGA34 Switch""LIN34 PGA"},
 /* AINLMUX */
 {"AINLMUX""INMIXL Mix""INMIXL"},
 {"AINLMUX""DIFFINL Mix""LIN12 PGA"},
 {"AINLMUX""DIFFINL Mix""LIN34 PGA"},
 {"AINLMUX""RXVOICE Mix""LIN4RXN"},
 {"AINLMUX""RXVOICE Mix""RIN4RXP"},
 /* ADC */
 {"Left ADC", NULL, "AINLMUX"},

 /* RIN12 PGA */
 {"RIN12 PGA""RIN1 Switch""RIN1"},
 {"RIN12 PGA""RIN2 Switch""RIN2"},
 /* RIN34 PGA */
 {"RIN34 PGA""RIN3 Switch""RIN3"},
 {"RIN34 PGA""RIN4 Switch""RIN4RXP"},
 /* INMIXL */
 {"INMIXR""Record Right Volume""ROMIX"},
 {"INMIXR""RIN2 Volume""RIN2"},
 {"INMIXR""RINPGA12 Switch""RIN12 PGA"},
 {"INMIXR""RINPGA34 Switch""RIN34 PGA"},
 /* AINRMUX */
 {"AINRMUX""INMIXR Mix""INMIXR"},
 {"AINRMUX""DIFFINR Mix""RIN12 PGA"},
 {"AINRMUX""DIFFINR Mix""RIN34 PGA"},
 {"AINRMUX""RXVOICE Mix""LIN4RXN"},
 {"AINRMUX""RXVOICE Mix""RIN4RXP"},
 /* ADC */
 {"Right ADC", NULL, "AINRMUX"},

 /* LOMIX */
 {"LOMIX""LOMIX RIN3 Bypass Switch""RIN3"},
 {"LOMIX""LOMIX LIN3 Bypass Switch""LIN3"},
 {"LOMIX""LOMIX LIN12 PGA Bypass Switch""LIN12 PGA"},
 {"LOMIX""LOMIX RIN12 PGA Bypass Switch""RIN12 PGA"},
 {"LOMIX""LOMIX Right ADC Bypass Switch""AINRMUX"},
 {"LOMIX""LOMIX Left ADC Bypass Switch""AINLMUX"},
 {"LOMIX""LOMIX Left DAC Switch""Left DAC"},

 /* ROMIX */
 {"ROMIX""ROMIX RIN3 Bypass Switch""RIN3"},
 {"ROMIX""ROMIX LIN3 Bypass Switch""LIN3"},
 {"ROMIX""ROMIX LIN12 PGA Bypass Switch""LIN12 PGA"},
 {"ROMIX""ROMIX RIN12 PGA Bypass Switch""RIN12 PGA"},
 {"ROMIX""ROMIX Right ADC Bypass Switch""AINRMUX"},
 {"ROMIX""ROMIX Left ADC Bypass Switch""AINLMUX"},
 {"ROMIX""ROMIX Right DAC Switch""Right DAC"},

 /* SPKMIX */
 {"SPKMIX""SPKMIX LIN2 Bypass Switch""LIN2"},
 {"SPKMIX""SPKMIX RIN2 Bypass Switch""RIN2"},
 {"SPKMIX""SPKMIX LADC Bypass Switch""AINLMUX"},
 {"SPKMIX""SPKMIX RADC Bypass Switch""AINRMUX"},
 {"SPKMIX""SPKMIX Left Mixer PGA Switch""LOPGA"},
 {"SPKMIX""SPKMIX Right Mixer PGA Switch""ROPGA"},
 {"SPKMIX""SPKMIX Right DAC Switch""Right DAC"},
 {"SPKMIX""SPKMIX Left DAC Switch""Right DAC"},

 /* LONMIX */
 {"LONMIX""LONMIX Left Mixer PGA Switch""LOPGA"},
 {"LONMIX""LONMIX Right Mixer PGA Switch""ROPGA"},
 {"LONMIX""LONMIX Inverted LOP Switch""LOPMIX"},

 /* LOPMIX */
 {"LOPMIX""LOPMIX Right Mic Bypass Switch""RIN12 PGA"},
 {"LOPMIX""LOPMIX Left Mic Bypass Switch""LIN12 PGA"},
 {"LOPMIX""LOPMIX Left Mixer PGA Switch""LOPGA"},

 /* OUT3MIX */
 {"OUT3MIX""OUT3MIX LIN4RXN Bypass Switch""LIN4RXN"},
 {"OUT3MIX""OUT3MIX Left Out PGA Switch""LOPGA"},

 /* OUT4MIX */
 {"OUT4MIX""OUT4MIX Right Out PGA Switch""ROPGA"},
 {"OUT4MIX""OUT4MIX RIN4RXP Bypass Switch""RIN4RXP"},

 /* RONMIX */
 {"RONMIX""RONMIX Right Mixer PGA Switch""ROPGA"},
 {"RONMIX""RONMIX Left Mixer PGA Switch""LOPGA"},
 {"RONMIX""RONMIX Inverted ROP Switch""ROPMIX"},

 /* ROPMIX */
 {"ROPMIX""ROPMIX Left Mic Bypass Switch""LIN12 PGA"},
 {"ROPMIX""ROPMIX Right Mic Bypass Switch""RIN12 PGA"},
 {"ROPMIX""ROPMIX Right Mixer PGA Switch""ROPGA"},

 /* Out Mixer PGAs */
 {"LOPGA", NULL, "LOMIX"},
 {"ROPGA", NULL, "ROMIX"},

 {"LOUT PGA", NULL, "LOMIX"},
 {"ROUT PGA", NULL, "ROMIX"},

 /* Output Pins */
 {"LON", NULL, "LONMIX"},
 {"LOP", NULL, "LOPMIX"},
 {"OUT", NULL, "OUT3MIX"},
 {"LOUT", NULL, "LOUT PGA"},
 {"SPKN", NULL, "SPKMIX"},
 {"ROUT", NULL, "ROUT PGA"},
 {"OUT4", NULL, "OUT4MIX"},
 {"ROP", NULL, "ROPMIX"},
 {"RON", NULL, "RONMIX"},
};

/* PLL divisors */
struct _pll_div {
 u32 div2;
 u32 n;
 u32 k;
};

/* The size in bits of the pll divide multiplied by 10
 * to allow rounding later */

#define FIXED_PLL_SIZE ((1 << 16) * 10)

static void pll_factors(struct _pll_div *pll_div, unsigned int target,
   unsigned int source)
{
 u64 Kpart;
 unsigned int K, Ndiv, Nmod;


 Ndiv = target / source;
 if (Ndiv < 6) {
  source >>= 1;
  pll_div->div2 = 1;
  Ndiv = target / source;
 } else
  pll_div->div2 = 0;

 if ((Ndiv < 6) || (Ndiv > 12))
  printk(KERN_WARNING
         "WM8991 N value outwith recommended range! N = %d\n", Ndiv);

 pll_div->n = Ndiv;
 Nmod = target % source;
 Kpart = FIXED_PLL_SIZE * (long long)Nmod;

 do_div(Kpart, source);

 K = Kpart & 0xFFFFFFFF;

 /* Check if we need to round */
 if ((K % 10) >= 5)
  K += 5;

 /* Move down to proper range now rounding is done */
 K /= 10;

 pll_div->k = K;
}

static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai,
         int pll_id, int src, unsigned int freq_in, unsigned int freq_out)
{
 u16 reg;
 struct snd_soc_component *component = codec_dai->component;
 struct _pll_div pll_div;

 if (freq_in && freq_out) {
  pll_factors(&pll_div, freq_out * 4, freq_in);

  /* Turn on PLL */
  reg = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_2);
  reg |= WM8991_PLL_ENA;
  snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_2, reg);

  /* sysclk comes from PLL */
  reg = snd_soc_component_read(component, WM8991_CLOCKING_2);
  snd_soc_component_write(component, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC);

  /* set up N , fractional mode and pre-divisor if necessary */
  snd_soc_component_write(component, WM8991_PLL1, pll_div.n | WM8991_SDM |
         (pll_div.div2 ? WM8991_PRESCALE : 0));
  snd_soc_component_write(component, WM8991_PLL2, (u8)(pll_div.k>>8));
  snd_soc_component_write(component, WM8991_PLL3, (u8)(pll_div.k & 0xFF));
 } else {
  /* Turn on PLL */
  reg = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_2);
  reg &= ~WM8991_PLL_ENA;
  snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_2, reg);
 }
 return 0;
}

/*
 * Set's ADC and Voice DAC format.
 */

static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
         unsigned int fmt)
{
 struct snd_soc_component *component = codec_dai->component;
 u16 audio1, audio3;

 audio1 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_1);
 audio3 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_3);

 /* set master/slave audio interface */
 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 case SND_SOC_DAIFMT_CBC_CFC:
  audio3 &= ~WM8991_AIF_MSTR1;
  break;
 case SND_SOC_DAIFMT_CBP_CFP:
  audio3 |= WM8991_AIF_MSTR1;
  break;
 default:
  return -EINVAL;
 }

 audio1 &= ~WM8991_AIF_FMT_MASK;

 /* interface format */
 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 case SND_SOC_DAIFMT_I2S:
  audio1 |= WM8991_AIF_TMF_I2S;
  audio1 &= ~WM8991_AIF_LRCLK_INV;
  break;
 case SND_SOC_DAIFMT_RIGHT_J:
  audio1 |= WM8991_AIF_TMF_RIGHTJ;
  audio1 &= ~WM8991_AIF_LRCLK_INV;
  break;
 case SND_SOC_DAIFMT_LEFT_J:
  audio1 |= WM8991_AIF_TMF_LEFTJ;
  audio1 &= ~WM8991_AIF_LRCLK_INV;
  break;
 case SND_SOC_DAIFMT_DSP_A:
  audio1 |= WM8991_AIF_TMF_DSP;
  audio1 &= ~WM8991_AIF_LRCLK_INV;
  break;
 case SND_SOC_DAIFMT_DSP_B:
  audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV;
  break;
 default:
  return -EINVAL;
 }

 snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_1, audio1);
 snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_3, audio3);
 return 0;
}

static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
     int div_id, int div)
{
 struct snd_soc_component *component = codec_dai->component;
 u16 reg;

 switch (div_id) {
 case WM8991_MCLK_DIV:
  reg = snd_soc_component_read(component, WM8991_CLOCKING_2) &
        ~WM8991_MCLK_DIV_MASK;
  snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div);
  break;
 case WM8991_DACCLK_DIV:
  reg = snd_soc_component_read(component, WM8991_CLOCKING_2) &
        ~WM8991_DAC_CLKDIV_MASK;
  snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div);
  break;
 case WM8991_ADCCLK_DIV:
  reg = snd_soc_component_read(component, WM8991_CLOCKING_2) &
        ~WM8991_ADC_CLKDIV_MASK;
  snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div);
  break;
 case WM8991_BCLK_DIV:
  reg = snd_soc_component_read(component, WM8991_CLOCKING_1) &
        ~WM8991_BCLK_DIV_MASK;
  snd_soc_component_write(component, WM8991_CLOCKING_1, reg | div);
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

/*
 * Set PCM DAI bit size and sample rate.
 */

static int wm8991_hw_params(struct snd_pcm_substream *substream,
       struct snd_pcm_hw_params *params,
       struct snd_soc_dai *dai)
{
 struct snd_soc_component *component = dai->component;
 u16 audio1 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_1);

 audio1 &= ~WM8991_AIF_WL_MASK;
 /* bit size */
 switch (params_width(params)) {
 case 16:
  break;
 case 20:
  audio1 |= WM8991_AIF_WL_20BITS;
  break;
 case 24:
  audio1 |= WM8991_AIF_WL_24BITS;
  break;
 case 32:
  audio1 |= WM8991_AIF_WL_32BITS;
  break;
 }

 snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_1, audio1);
 return 0;
}

static int wm8991_mute(struct snd_soc_dai *dai, int mute, int direction)
{
 struct snd_soc_component *component = dai->component;
 u16 val;

 val  = snd_soc_component_read(component, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
 if (mute)
  snd_soc_component_write(component, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
 else
  snd_soc_component_write(component, WM8991_DAC_CTRL, val);
 return 0;
}

static int wm8991_set_bias_level(struct snd_soc_component *component,
     enum snd_soc_bias_level level)
{
 struct wm8991_priv *wm8991 = snd_soc_component_get_drvdata(component);
 u16 val;

 switch (level) {
 case SND_SOC_BIAS_ON:
  break;

 case SND_SOC_BIAS_PREPARE:
  /* VMID=2*50k */
  val = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_1) &
        ~WM8991_VMID_MODE_MASK;
  snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, val | 0x2);
  break;

 case SND_SOC_BIAS_STANDBY:
  if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
   regcache_sync(wm8991->regmap);
   /* Enable all output discharge bits */
   snd_soc_component_write(component, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
          WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
          WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
          WM8991_DIS_ROUT);

   /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
   snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
          WM8991_BUFDCOPEN | WM8991_POBCTRL |
          WM8991_VMIDTOG);

   /* Delay to allow output caps to discharge */
   msleep(300);

   /* Disable VMIDTOG */
   snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
          WM8991_BUFDCOPEN | WM8991_POBCTRL);

   /* disable all output discharge bits */
   snd_soc_component_write(component, WM8991_ANTIPOP1, 0);

   /* Enable outputs */
   snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1b00);

   msleep(50);

   /* Enable VMID at 2x50k */
   snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f02);

   msleep(100);

   /* Enable VREF */
   snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f03);

   msleep(600);

   /* Enable BUFIOEN */
   snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
          WM8991_BUFDCOPEN | WM8991_POBCTRL |
          WM8991_BUFIOEN);

   /* Disable outputs */
   snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x3);

   /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
   snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_BUFIOEN);
  }

  /* VMID=2*250k */
  val = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_1) &
        ~WM8991_VMID_MODE_MASK;
  snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, val | 0x4);
  break;

 case SND_SOC_BIAS_OFF:
  /* Enable POBCTRL and SOFT_ST */
  snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
         WM8991_POBCTRL | WM8991_BUFIOEN);

  /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
  snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
         WM8991_BUFDCOPEN | WM8991_POBCTRL |
         WM8991_BUFIOEN);

  /* mute DAC */
  val = snd_soc_component_read(component, WM8991_DAC_CTRL);
  snd_soc_component_write(component, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);

  /* Enable any disabled outputs */
  snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f03);

  /* Disable VMID */
  snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f01);

  msleep(300);

  /* Enable all output discharge bits */
  snd_soc_component_write(component, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
         WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
         WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
         WM8991_DIS_ROUT);

  /* Disable VREF */
  snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x0);

  /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
  snd_soc_component_write(component, WM8991_ANTIPOP2, 0x0);
  regcache_mark_dirty(wm8991->regmap);
  break;
 }

 return 0;
}

#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
   SNDRV_PCM_FMTBIT_S24_LE)

static const struct snd_soc_dai_ops wm8991_ops = {
 .hw_params = wm8991_hw_params,
 .mute_stream = wm8991_mute,
 .set_fmt = wm8991_set_dai_fmt,
 .set_clkdiv = wm8991_set_dai_clkdiv,
 .set_pll = wm8991_set_dai_pll,
 .no_capture_mute = 1,
};

/*
 * The WM8991 supports 2 different and mutually exclusive DAI
 * configurations.
 *
 * 1. ADC/DAC on Primary Interface
 * 2. ADC on Primary Interface/DAC on secondary
 */

static struct snd_soc_dai_driver wm8991_dai = {
 /* ADC/DAC on primary */
 .name = "wm8991",
 .id = 1,
 .playback = {
  .stream_name = "Playback",
  .channels_min = 1,
  .channels_max = 2,
  .rates = SNDRV_PCM_RATE_8000_96000,
  .formats = WM8991_FORMATS
 },
 .capture = {
  .stream_name = "Capture",
  .channels_min = 1,
  .channels_max = 2,
  .rates = SNDRV_PCM_RATE_8000_96000,
  .formats = WM8991_FORMATS
 },
 .ops = &wm8991_ops
};

static const struct snd_soc_component_driver soc_component_dev_wm8991 = {
 .set_bias_level  = wm8991_set_bias_level,
 .controls  = wm8991_snd_controls,
 .num_controls  = ARRAY_SIZE(wm8991_snd_controls),
 .dapm_widgets  = wm8991_dapm_widgets,
 .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets),
 .dapm_routes  = wm8991_dapm_routes,
 .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes),
 .suspend_bias_off = 1,
 .idle_bias_on  = 1,
 .use_pmdown_time = 1,
 .endianness  = 1,
};

static const struct regmap_config wm8991_regmap = {
 .reg_bits = 8,
 .val_bits = 16,

 .max_register = WM8991_PLL3,
 .volatile_reg = wm8991_volatile,
 .reg_defaults = wm8991_reg_defaults,
 .num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults),
 .cache_type = REGCACHE_MAPLE,
};

static int wm8991_i2c_probe(struct i2c_client *i2c)
{
 struct wm8991_priv *wm8991;
 unsigned int val;
 int ret;

 wm8991 = devm_kzalloc(&i2c->dev, sizeof(*wm8991), GFP_KERNEL);
 if (!wm8991)
  return -ENOMEM;

 wm8991->regmap = devm_regmap_init_i2c(i2c, &wm8991_regmap);
 if (IS_ERR(wm8991->regmap))
  return PTR_ERR(wm8991->regmap);

 i2c_set_clientdata(i2c, wm8991);

 ret = regmap_read(wm8991->regmap, WM8991_RESET, &val);
 if (ret != 0) {
  dev_err(&i2c->dev, "Failed to read device ID: %d\n", ret);
  return ret;
 }
 if (val != 0x8991) {
  dev_err(&i2c->dev, "Device with ID %x is not a WM8991\n", val);
  return -EINVAL;
 }

 ret = regmap_write(wm8991->regmap, WM8991_RESET, 0);
 if (ret < 0) {
  dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
  return ret;
 }

 regmap_update_bits(wm8991->regmap, WM8991_AUDIO_INTERFACE_4,
      WM8991_ALRCGPIO1, WM8991_ALRCGPIO1);

 regmap_update_bits(wm8991->regmap, WM8991_GPIO1_GPIO2,
      WM8991_GPIO1_SEL_MASK, 1);

 regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_1,
      WM8991_VREF_ENA | WM8991_VMID_MODE_MASK,
      WM8991_VREF_ENA | WM8991_VMID_MODE_MASK);

 regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_2,
      WM8991_OPCLK_ENA, WM8991_OPCLK_ENA);

 regmap_write(wm8991->regmap, WM8991_DAC_CTRL, 0);
 regmap_write(wm8991->regmap, WM8991_LEFT_OUTPUT_VOLUME,
       0x50 | (1<<8));
 regmap_write(wm8991->regmap, WM8991_RIGHT_OUTPUT_VOLUME,
       0x50 | (1<<8));

 ret = devm_snd_soc_register_component(&i2c->dev,
         &soc_component_dev_wm8991, &wm8991_dai, 1);

 return ret;
}

static const struct i2c_device_id wm8991_i2c_id[] = {
 { "wm8991" },
 { }
};
MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);

static struct i2c_driver wm8991_i2c_driver = {
 .driver = {
  .name = "wm8991",
 },
 .probe = wm8991_i2c_probe,
 .id_table = wm8991_i2c_id,
};

module_i2c_driver(wm8991_i2c_driver);

MODULE_DESCRIPTION("ASoC WM8991 driver");
MODULE_AUTHOR("Graeme Gregory");
MODULE_LICENSE("GPL");

Messung V0.5 in Prozent
C=88 H=95 G=91

¤ Dauer der Verarbeitung: 0.35 Sekunden  (vorverarbeitet am  2026-06-05) ¤

*© Formatika GbR, Deutschland






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders