// SPDX-License-Identifier: GPL-2.0-or-later
/*
* wm9712 . c - - ALSA Soc WM9712 codec support
*
* Copyright 2006 - 12 Wolfson Microelectronics PLC .
* Author : Liam Girdwood < lrg @ slimlogic . co . uk >
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mfd/wm97xx.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/ac97/codec.h>
#include <sound/ac97/compat.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#define WM9712_VENDOR_ID 0 x574d4c12
#define WM9712_VENDOR_ID_MASK 0 xffffffff
struct wm9712_priv {
struct snd_ac97 *ac97;
unsigned int hp_mixer[2 ];
struct mutex lock;
struct wm97xx_platform_data *mfd_pdata;
};
static const struct reg_default wm9712_reg_defaults[] = {
{ 0 x02, 0 x8000 },
{ 0 x04, 0 x8000 },
{ 0 x06, 0 x8000 },
{ 0 x08, 0 x0f0f },
{ 0 x0a, 0 xaaa0 },
{ 0 x0c, 0 xc008 },
{ 0 x0e, 0 x6808 },
{ 0 x10, 0 xe808 },
{ 0 x12, 0 xaaa0 },
{ 0 x14, 0 xad00 },
{ 0 x16, 0 x8000 },
{ 0 x18, 0 xe808 },
{ 0 x1a, 0 x3000 },
{ 0 x1c, 0 x8000 },
{ 0 x20, 0 x0000 },
{ 0 x22, 0 x0000 },
{ 0 x26, 0 x000f },
{ 0 x28, 0 x0605 },
{ 0 x2a, 0 x0410 },
{ 0 x2c, 0 xbb80 },
{ 0 x2e, 0 xbb80 },
{ 0 x32, 0 xbb80 },
{ 0 x34, 0 x2000 },
{ 0 x4c, 0 xf83e },
{ 0 x4e, 0 xffff },
{ 0 x50, 0 x0000 },
{ 0 x52, 0 x0000 },
{ 0 x56, 0 xf83e },
{ 0 x58, 0 x0008 },
{ 0 x5c, 0 x0000 },
{ 0 x60, 0 xb032 },
{ 0 x62, 0 x3e00 },
{ 0 x64, 0 x0000 },
{ 0 x76, 0 x0006 },
{ 0 x78, 0 x0001 },
{ 0 x7a, 0 x0000 },
};
static bool wm9712_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AC97_REC_GAIN:
return true ;
default :
return regmap_ac97_default_volatile(dev, reg);
}
}
static const struct regmap_config wm9712_regmap_config = {
.reg_bits = 16 ,
.reg_stride = 2 ,
.val_bits = 16 ,
.max_register = 0 x7e,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = wm9712_volatile_reg,
.reg_defaults = wm9712_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults),
};
#define HPL_MIXER 0 x0
#define HPR_MIXER 0 x1
static const char *wm9712_alc_select[] = {"None" , "Left" , "Right" , "Stereo" };
static const char *wm9712_alc_mux[] = {"Stereo" , "Left" , "Right" , "None" };
static const char *wm9712_out3_src[] = {"Left" , "VREF" , "Left + Right" ,
"Mono" };
static const char *wm9712_spk_src[] = {"Speaker Mix" , "Headphone Mix" };
static const char *wm9712_rec_adc[] = {"Stereo" , "Left" , "Right" , "Mute" };
static const char *wm9712_base[] = {"Linear Control" , "Adaptive Boost" };
static const char *wm9712_rec_gain[] = {"+1.5dB Steps" , "+0.75dB Steps" };
static const char *wm9712_mic[] = {"Mic 1" , "Differential" , "Mic 2" ,
"Stereo" };
static const char *wm9712_rec_sel[] = {"Mic" , "NC" , "NC" , "Speaker Mixer" ,
"Line" , "Headphone Mixer" , "Phone Mixer" , "Phone" };
static const char *wm9712_ng_type[] = {"Constant Gain" , "Mute" };
static const char *wm9712_diff_sel[] = {"Mic" , "Line" };
static const DECLARE_TLV_DB_SCALE(main_tlv, -3450 , 150 , 0 );
static const DECLARE_TLV_DB_SCALE(boost_tlv, 0 , 2000 , 0 );
static const struct soc_enum wm9712_enum[] = {
SOC_ENUM_SINGLE(AC97_PCI_SVID, 14 , 4 , wm9712_alc_select),
SOC_ENUM_SINGLE(AC97_VIDEO, 12 , 4 , wm9712_alc_mux),
SOC_ENUM_SINGLE(AC97_AUX, 9 , 4 , wm9712_out3_src),
SOC_ENUM_SINGLE(AC97_AUX, 8 , 2 , wm9712_spk_src),
SOC_ENUM_SINGLE(AC97_REC_SEL, 12 , 4 , wm9712_rec_adc),
SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15 , 2 , wm9712_base),
SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14 , 6 , 2 , wm9712_rec_gain),
SOC_ENUM_SINGLE(AC97_MIC, 5 , 4 , wm9712_mic),
SOC_ENUM_SINGLE(AC97_REC_SEL, 8 , 8 , wm9712_rec_sel),
SOC_ENUM_SINGLE(AC97_REC_SEL, 0 , 8 , wm9712_rec_sel),
SOC_ENUM_SINGLE(AC97_PCI_SVID, 5 , 2 , wm9712_ng_type),
SOC_ENUM_SINGLE(0 x5c, 8 , 2 , wm9712_diff_sel),
};
static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
SOC_DOUBLE("Speaker Playback Volume" , AC97_MASTER, 8 , 0 , 31 , 1 ),
SOC_SINGLE("Speaker Playback Switch" , AC97_MASTER, 15 , 1 , 1 ),
SOC_DOUBLE("Headphone Playback Volume" , AC97_HEADPHONE, 8 , 0 , 31 , 1 ),
SOC_SINGLE("Headphone Playback Switch" , AC97_HEADPHONE, 15 , 1 , 1 ),
SOC_DOUBLE("PCM Playback Volume" , AC97_PCM, 8 , 0 , 31 , 1 ),
SOC_SINGLE("Speaker Playback ZC Switch" , AC97_MASTER, 7 , 1 , 0 ),
SOC_SINGLE("Speaker Playback Invert Switch" , AC97_MASTER, 6 , 1 , 0 ),
SOC_SINGLE("Headphone Playback ZC Switch" , AC97_HEADPHONE, 7 , 1 , 0 ),
SOC_SINGLE("Mono Playback ZC Switch" , AC97_MASTER_MONO, 7 , 1 , 0 ),
SOC_SINGLE("Mono Playback Volume" , AC97_MASTER_MONO, 0 , 31 , 1 ),
SOC_SINGLE("Mono Playback Switch" , AC97_MASTER_MONO, 15 , 1 , 1 ),
SOC_SINGLE("ALC Target Volume" , AC97_CODEC_CLASS_REV, 12 , 15 , 0 ),
SOC_SINGLE("ALC Hold Time" , AC97_CODEC_CLASS_REV, 8 , 15 , 0 ),
SOC_SINGLE("ALC Decay Time" , AC97_CODEC_CLASS_REV, 4 , 15 , 0 ),
SOC_SINGLE("ALC Attack Time" , AC97_CODEC_CLASS_REV, 0 , 15 , 0 ),
SOC_ENUM("ALC Function" , wm9712_enum[0 ]),
SOC_SINGLE("ALC Max Volume" , AC97_PCI_SVID, 11 , 7 , 0 ),
SOC_SINGLE("ALC ZC Timeout" , AC97_PCI_SVID, 9 , 3 , 1 ),
SOC_SINGLE("ALC ZC Switch" , AC97_PCI_SVID, 8 , 1 , 0 ),
SOC_SINGLE("ALC NG Switch" , AC97_PCI_SVID, 7 , 1 , 0 ),
SOC_ENUM("ALC NG Type" , wm9712_enum[10 ]),
SOC_SINGLE("ALC NG Threshold" , AC97_PCI_SVID, 0 , 31 , 1 ),
SOC_SINGLE("Mic Headphone Volume" , AC97_VIDEO, 12 , 7 , 1 ),
SOC_SINGLE("ALC Headphone Volume" , AC97_VIDEO, 7 , 7 , 1 ),
SOC_SINGLE("Out3 Switch" , AC97_AUX, 15 , 1 , 1 ),
SOC_SINGLE("Out3 ZC Switch" , AC97_AUX, 7 , 1 , 1 ),
SOC_SINGLE("Out3 Volume" , AC97_AUX, 0 , 31 , 1 ),
SOC_SINGLE("PCBeep Bypass Headphone Volume" , AC97_PC_BEEP, 12 , 7 , 1 ),
SOC_SINGLE("PCBeep Bypass Speaker Volume" , AC97_PC_BEEP, 8 , 7 , 1 ),
SOC_SINGLE("PCBeep Bypass Phone Volume" , AC97_PC_BEEP, 4 , 7 , 1 ),
SOC_SINGLE("Aux Playback Headphone Volume" , AC97_CD, 12 , 7 , 1 ),
SOC_SINGLE("Aux Playback Speaker Volume" , AC97_CD, 8 , 7 , 1 ),
SOC_SINGLE("Aux Playback Phone Volume" , AC97_CD, 4 , 7 , 1 ),
SOC_SINGLE("Phone Volume" , AC97_PHONE, 0 , 15 , 1 ),
SOC_DOUBLE("Line Capture Volume" , AC97_LINE, 8 , 0 , 31 , 1 ),
SOC_SINGLE_TLV("Capture Boost Switch" , AC97_REC_SEL, 14 , 1 , 0 , boost_tlv),
SOC_SINGLE_TLV("Capture to Phone Boost Switch" , AC97_REC_SEL, 11 , 1 , 1 ,
boost_tlv),
SOC_SINGLE("3D Upper Cut-off Switch" , AC97_3D_CONTROL, 5 , 1 , 1 ),
SOC_SINGLE("3D Lower Cut-off Switch" , AC97_3D_CONTROL, 4 , 1 , 1 ),
SOC_SINGLE("3D Playback Volume" , AC97_3D_CONTROL, 0 , 15 , 0 ),
SOC_ENUM("Bass Control" , wm9712_enum[5 ]),
SOC_SINGLE("Bass Cut-off Switch" , AC97_MASTER_TONE, 12 , 1 , 1 ),
SOC_SINGLE("Tone Cut-off Switch" , AC97_MASTER_TONE, 4 , 1 , 1 ),
SOC_SINGLE("Playback Attenuate (-6dB) Switch" , AC97_MASTER_TONE, 6 , 1 , 0 ),
SOC_SINGLE("Bass Volume" , AC97_MASTER_TONE, 8 , 15 , 1 ),
SOC_SINGLE("Treble Volume" , AC97_MASTER_TONE, 0 , 15 , 1 ),
SOC_SINGLE("Capture Switch" , AC97_REC_GAIN, 15 , 1 , 1 ),
SOC_ENUM("Capture Volume Steps" , wm9712_enum[6 ]),
SOC_DOUBLE("Capture Volume" , AC97_REC_GAIN, 8 , 0 , 63 , 0 ),
SOC_SINGLE("Capture ZC Switch" , AC97_REC_GAIN, 7 , 1 , 0 ),
SOC_SINGLE_TLV("Mic 1 Volume" , AC97_MIC, 8 , 31 , 1 , main_tlv),
SOC_SINGLE_TLV("Mic 2 Volume" , AC97_MIC, 0 , 31 , 1 , main_tlv),
SOC_SINGLE_TLV("Mic Boost Volume" , AC97_MIC, 7 , 1 , 0 , boost_tlv),
};
static const unsigned int wm9712_mixer_mute_regs[] = {
AC97_VIDEO,
AC97_PCM,
AC97_LINE,
AC97_PHONE,
AC97_CD,
AC97_PC_BEEP,
};
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels .
* This makes it impossible to determine the audio path .
*/
static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
unsigned int val = ucontrol->value.integer.value[0 ];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
struct snd_soc_dapm_update update = {};
bool change;
mixer = mc->shift >> 8 ;
shift = mc->shift & 0 xff;
mask = 1 << shift;
mutex_lock(&wm9712->lock);
old = wm9712->hp_mixer[mixer];
if (ucontrol->value.integer.value[0 ])
wm9712->hp_mixer[mixer] |= mask;
else
wm9712->hp_mixer[mixer] &= ~mask;
change = old != wm9712->hp_mixer[mixer];
if (change) {
update.kcontrol = kcontrol;
update.reg = wm9712_mixer_mute_regs[shift];
update.mask = 0 x8000;
if ((wm9712->hp_mixer[0 ] & mask) ||
(wm9712->hp_mixer[1 ] & mask))
update.val = 0 x0;
else
update.val = 0 x8000;
snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
&update);
}
mutex_unlock(&wm9712->lock);
return change;
}
static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int shift, mixer;
mixer = mc->shift >> 8 ;
shift = mc->shift & 0 xff;
ucontrol->value.integer.value[0 ] =
(wm9712->hp_mixer[mixer] >> shift) & 1 ;
return 0 ;
}
#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) \
SOC_SINGLE_EXT(xname, SND_SOC_NOPM, ((xmixer) << 8 ) | (xshift), \
1 , 0 , wm9712_hp_mixer_get, wm9712_hp_mixer_put)
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch" , HPL_MIXER, 5 ),
WM9712_HP_MIXER_CTRL("Aux Playback Switch" , HPL_MIXER, 4 ),
WM9712_HP_MIXER_CTRL("Phone Bypass Switch" , HPL_MIXER, 3 ),
WM9712_HP_MIXER_CTRL("Line Bypass Switch" , HPL_MIXER, 2 ),
WM9712_HP_MIXER_CTRL("PCM Playback Switch" , HPL_MIXER, 1 ),
WM9712_HP_MIXER_CTRL("Mic Sidetone Switch" , HPL_MIXER, 0 ),
};
/* Right Headphone Mixers */
static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch" , HPR_MIXER, 5 ),
WM9712_HP_MIXER_CTRL("Aux Playback Switch" , HPR_MIXER, 4 ),
WM9712_HP_MIXER_CTRL("Phone Bypass Switch" , HPR_MIXER, 3 ),
WM9712_HP_MIXER_CTRL("Line Bypass Switch" , HPR_MIXER, 2 ),
WM9712_HP_MIXER_CTRL("PCM Playback Switch" , HPR_MIXER, 1 ),
WM9712_HP_MIXER_CTRL("Mic Sidetone Switch" , HPR_MIXER, 0 ),
};
/* Speaker Mixer */
static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = {
SOC_DAPM_SINGLE("PCBeep Bypass Switch" , AC97_PC_BEEP, 11 , 1 , 1 ),
SOC_DAPM_SINGLE("Aux Playback Switch" , AC97_CD, 11 , 1 , 1 ),
SOC_DAPM_SINGLE("Phone Bypass Switch" , AC97_PHONE, 14 , 1 , 1 ),
SOC_DAPM_SINGLE("Line Bypass Switch" , AC97_LINE, 14 , 1 , 1 ),
SOC_DAPM_SINGLE("PCM Playback Switch" , AC97_PCM, 14 , 1 , 1 ),
};
/* Phone Mixer */
static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = {
SOC_DAPM_SINGLE("PCBeep Bypass Switch" , AC97_PC_BEEP, 7 , 1 , 1 ),
SOC_DAPM_SINGLE("Aux Playback Switch" , AC97_CD, 7 , 1 , 1 ),
SOC_DAPM_SINGLE("Line Bypass Switch" , AC97_LINE, 13 , 1 , 1 ),
SOC_DAPM_SINGLE("PCM Playback Switch" , AC97_PCM, 13 , 1 , 1 ),
SOC_DAPM_SINGLE("Mic 1 Sidetone Switch" , AC97_MIC, 14 , 1 , 1 ),
SOC_DAPM_SINGLE("Mic 2 Sidetone Switch" , AC97_MIC, 13 , 1 , 1 ),
};
/* ALC headphone mux */
static const struct snd_kcontrol_new wm9712_alc_mux_controls =
SOC_DAPM_ENUM("Route" , wm9712_enum[1 ]);
/* out 3 mux */
static const struct snd_kcontrol_new wm9712_out3_mux_controls =
SOC_DAPM_ENUM("Route" , wm9712_enum[2 ]);
/* spk mux */
static const struct snd_kcontrol_new wm9712_spk_mux_controls =
SOC_DAPM_ENUM("Route" , wm9712_enum[3 ]);
/* Capture to Phone mux */
static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls =
SOC_DAPM_ENUM("Route" , wm9712_enum[4 ]);
/* Capture left select */
static const struct snd_kcontrol_new wm9712_capture_selectl_controls =
SOC_DAPM_ENUM("Route" , wm9712_enum[8 ]);
/* Capture right select */
static const struct snd_kcontrol_new wm9712_capture_selectr_controls =
SOC_DAPM_ENUM("Route" , wm9712_enum[9 ]);
/* Mic select */
static const struct snd_kcontrol_new wm9712_mic_src_controls =
SOC_DAPM_ENUM("Mic Source Select" , wm9712_enum[7 ]);
/* diff select */
static const struct snd_kcontrol_new wm9712_diff_sel_controls =
SOC_DAPM_ENUM("Route" , wm9712_enum[11 ]);
static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = {
SND_SOC_DAPM_MUX("ALC Sidetone Mux" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_alc_mux_controls),
SND_SOC_DAPM_MUX("Out3 Mux" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_out3_mux_controls),
SND_SOC_DAPM_MUX("Speaker Mux" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_spk_mux_controls),
SND_SOC_DAPM_MUX("Capture Phone Mux" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_capture_phone_mux_controls),
SND_SOC_DAPM_MUX("Left Capture Select" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_capture_selectl_controls),
SND_SOC_DAPM_MUX("Right Capture Select" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_capture_selectr_controls),
SND_SOC_DAPM_MUX("Left Mic Select Source" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_mic_src_controls),
SND_SOC_DAPM_MUX("Right Mic Select Source" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_mic_src_controls),
SND_SOC_DAPM_MUX("Differential Source" , SND_SOC_NOPM, 0 , 0 ,
&wm9712_diff_sel_controls),
SND_SOC_DAPM_MIXER("AC97 Mixer" , SND_SOC_NOPM, 0 , 0 , NULL, 0 ),
SND_SOC_DAPM_MIXER("Left HP Mixer" , AC97_INT_PAGING, 9 , 1 ,
&wm9712_hpl_mixer_controls[0 ], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
SND_SOC_DAPM_MIXER("Right HP Mixer" , AC97_INT_PAGING, 8 , 1 ,
&wm9712_hpr_mixer_controls[0 ], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
SND_SOC_DAPM_MIXER("Phone Mixer" , AC97_INT_PAGING, 6 , 1 ,
&wm9712_phone_mixer_controls[0 ], ARRAY_SIZE(wm9712_phone_mixer_controls)),
SND_SOC_DAPM_MIXER("Speaker Mixer" , AC97_INT_PAGING, 7 , 1 ,
&wm9712_speaker_mixer_controls[0 ],
ARRAY_SIZE(wm9712_speaker_mixer_controls)),
SND_SOC_DAPM_MIXER("Mono Mixer" , SND_SOC_NOPM, 0 , 0 , NULL, 0 ),
SND_SOC_DAPM_DAC("Left DAC" , "Left HiFi Playback" , AC97_INT_PAGING, 14 , 1 ),
SND_SOC_DAPM_DAC("Right DAC" , "Right HiFi Playback" , AC97_INT_PAGING, 13 , 1 ),
SND_SOC_DAPM_DAC("Aux DAC" , "Aux Playback" , SND_SOC_NOPM, 0 , 0 ),
SND_SOC_DAPM_ADC("Left ADC" , "Left HiFi Capture" , AC97_INT_PAGING, 12 , 1 ),
SND_SOC_DAPM_ADC("Right ADC" , "Right HiFi Capture" , AC97_INT_PAGING, 11 , 1 ),
SND_SOC_DAPM_PGA("Headphone PGA" , AC97_INT_PAGING, 4 , 1 , NULL, 0 ),
SND_SOC_DAPM_PGA("Speaker PGA" , AC97_INT_PAGING, 3 , 1 , NULL, 0 ),
SND_SOC_DAPM_PGA("Out 3 PGA" , AC97_INT_PAGING, 5 , 1 , NULL, 0 ),
SND_SOC_DAPM_PGA("Line PGA" , AC97_INT_PAGING, 2 , 1 , NULL, 0 ),
SND_SOC_DAPM_PGA("Phone PGA" , AC97_INT_PAGING, 1 , 1 , NULL, 0 ),
SND_SOC_DAPM_PGA("Mic PGA" , AC97_INT_PAGING, 0 , 1 , NULL, 0 ),
SND_SOC_DAPM_PGA("Differential Mic" , SND_SOC_NOPM, 0 , 0 , NULL, 0 ),
SND_SOC_DAPM_MICBIAS("Mic Bias" , AC97_INT_PAGING, 10 , 1 ),
SND_SOC_DAPM_OUTPUT("MONOOUT" ),
SND_SOC_DAPM_OUTPUT("HPOUTL" ),
SND_SOC_DAPM_OUTPUT("HPOUTR" ),
SND_SOC_DAPM_OUTPUT("LOUT2" ),
SND_SOC_DAPM_OUTPUT("ROUT2" ),
SND_SOC_DAPM_OUTPUT("OUT3" ),
SND_SOC_DAPM_INPUT("LINEINL" ),
SND_SOC_DAPM_INPUT("LINEINR" ),
SND_SOC_DAPM_INPUT("PHONE" ),
SND_SOC_DAPM_INPUT("PCBEEP" ),
SND_SOC_DAPM_INPUT("MIC1" ),
SND_SOC_DAPM_INPUT("MIC2" ),
};
static const struct snd_soc_dapm_route wm9712_audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer" , NULL, "Left DAC" },
{"AC97 Mixer" , NULL, "Right DAC" },
/* Left HP mixer */
{"Left HP Mixer" , "PCBeep Bypass Switch" , "PCBEEP" },
{"Left HP Mixer" , "Aux Playback Switch" , "Aux DAC" },
{"Left HP Mixer" , "Phone Bypass Switch" , "Phone PGA" },
{"Left HP Mixer" , "Line Bypass Switch" , "Line PGA" },
{"Left HP Mixer" , "PCM Playback Switch" , "Left DAC" },
{"Left HP Mixer" , "Mic Sidetone Switch" , "Mic PGA" },
{"Left HP Mixer" , NULL, "ALC Sidetone Mux" },
/* Right HP mixer */
{"Right HP Mixer" , "PCBeep Bypass Switch" , "PCBEEP" },
{"Right HP Mixer" , "Aux Playback Switch" , "Aux DAC" },
{"Right HP Mixer" , "Phone Bypass Switch" , "Phone PGA" },
{"Right HP Mixer" , "Line Bypass Switch" , "Line PGA" },
{"Right HP Mixer" , "PCM Playback Switch" , "Right DAC" },
{"Right HP Mixer" , "Mic Sidetone Switch" , "Mic PGA" },
{"Right HP Mixer" , NULL, "ALC Sidetone Mux" },
/* speaker mixer */
{"Speaker Mixer" , "PCBeep Bypass Switch" , "PCBEEP" },
{"Speaker Mixer" , "Line Bypass Switch" , "Line PGA" },
{"Speaker Mixer" , "PCM Playback Switch" , "AC97 Mixer" },
{"Speaker Mixer" , "Phone Bypass Switch" , "Phone PGA" },
{"Speaker Mixer" , "Aux Playback Switch" , "Aux DAC" },
/* Phone mixer */
{"Phone Mixer" , "PCBeep Bypass Switch" , "PCBEEP" },
{
Mixer,APlaybackSwitch ,Aux AC",
{"Phone Mixer" , "PCM Playback Switch" , "AC97 Mixer" },
{"Phone Mixer" , "Mic 1 Sidetone Switch" , "Mic PGA" },
{"Phone
/* inputs */
{"Line PGA" , NULL, "LINEINL" },
{"Line PGA" , NULL, "LINEINR" },
{"Phone PGA" , NULL, "PHONE" },
{"Mic PGA" , NULL, "MIC1" },
{"Mic PGA" , NULL, "MIC2" },
/* microphones */
{"Differential Mic" , NULL, "MIC1" },
{"Differential Mic" , NULL, "MIC2" },
{"Left Mic Select Source" , "Mic 1" , "MIC1" },
{"Left Mic Select mask = 0; /* no default/
{"Left Mic Select Source" /*
*It is yet working
{"Right Mic Select Source" , "Mic 1" , "MIC1" } constraintis based boardconfigs.
{"Right Mic Select Source" , "Mic 2" , "MIC2" },
{" Mic Select Source" "" "MIC2" java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 47
R MicSelectSource" Differential" D ",
/* left capture selector */
java.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
{Left Capture " Speaker " " Mixer" ,
{"Left Capture Select" , "Line" , "LINEINL" },
{"Left Capture Select" , "Headphone java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
{ "," Mixer"," Mixer",
{"Left Capture Select" , "Phone" constraint= &>onstraint_channels
/* right capture selector */
{"Right Capture Select" , "c>= mask;
{Right ,"peaker Mixer" , "SpeakerMixer"
{" snd_pcm_hw_constraint_list(runtime, 0java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
{"Right Capture Select" , "java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 1
{" " Phone "," hone Mixer}java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
" Capture Select" Phone" " }java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44
/
{"ALC Sidetonestruct ak4613_priv priv = snd_soc_component_get_drvdata(component);
{"ALC Sidetone Mux" , "Stereo" , "Right Capture Select" },
{
"ALC Sidetone Mux," " " Capture ",
/* ADC's */
{"Left ADC" , NULL, p-cnt+;
{"Right ADC" , NULL, "Right java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 0
/* outputs */
OOUT, Phone Mixer",
{"HPOUTL" , NULL, " int clk_id, unsigned freq, int dir)
{"Headphone PGA" , NULL, "Left HP Mixer" },
", , " eadphonePGA",
{"Headphone PGA" , NULL, "Right HP Mixer" },
/* mono mixer */
{"Mono
{Mono " NULL, " HP Mixer",
/* Out3 Mux */
{Out3 ,"" ," HPMixer" },
{"Out3 Mux" ,
{"Out3 Mux" , "Left
3 ",NULL,Out3 Mux" }
{"OUT3" , NULL
/* speaker Mux */
{"peaker " Speaker " Speaker Mixer" ,
{"Speaker Mux" , "Headphone Mix" , "Mono Mixer" },
{Speaker PGA,NULL," Mux" ,
{"LOUT2" , NULL, "Speaker PGA" },
{"unsigned int fmt;
};
ac97_preparestruct *java.lang.StringIndexOutOfBoundsException: Range [60, 61) out of bounds for length 60
SND_SOC_DAIFMT_LEFT_J
{
struct snd_soc_component *component = dai->component;
int break ;
struct snd_pcm_runtime *runtime = substreamreturn EINVAL;
snd_soc_component_update_bits(component, java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 2
if (ubstream-stream = SNDRV_PCM_STREAM_PLAYBACK)
reg = AC97_PCM_FRONT_DAC_RATE;
else case :
reg = AC97_PCM_LR_ADC_RATE;
return /*
}
static int ac97_aux_prepare ( truct substream ,
*
* " clock provider " is not yet
struct snd_soc_component * component = dai - > component ;
struct snd_pcm_runtime * runtime = java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 1
( component , AC97_EXTENDED_STATUS , 0 1 , 0 ) ;
snd_soc_component_update_bits ( component , AC97_PCI_SID , 0 x8000 snd_soc_component component dai - component ;
if ( substream - > stream unsigned int fmt priv > fmt ;
return - java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
return java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 12
}
WM9712_AC97_RATES ( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 )
static const struct wm9712_dai_ops_hifi = {
. prepare = ac97_prepare ,
} ;
static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
. prepare = ac97_aux_prepare ,
} ;
static ctrl2 java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
{
. name = " wm9712 - hifi " ,
. playback priv - > = ;
. stream_name = " HiFi FIXME
. = 1 java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
. channels_max = 2 ,
. rates = WM9712_AC97_RATES ,
formats SND_SOC_STD_AC97_FMTS , ,
. capture = {
. stream_name = * If was , use priv > trl1
. channels_min = 1 ,
. channels_max = 2 ,
. rates = WM9712_AC97_RATES ,
. formats = java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 5
. ops = & wm9712_dai_ops_hifi ,
} ,
{
. name = " wm9712 - aux " ,
. playback = {
. stream_name = " Aux Playback " ,
. channels_min = 1 ,
for i = ARRAY_SIZE ( ak4613_iface ) ; i > = ; i - ) {
. rates = WM9712_AC97_RATES ,
formats = SND_SOC_STD_AC97_FMTS , } ,
. ops = & wm9712_dai_ops_aux ,
}
} ;
static int wm9712_set_bias_level ( struct snd_soc_component * component ,
enum snd_soc_bias_level level )
{
* tdm > < > dif >
case SND_SOC_BIAS_ON :
case priv->ctrl1 ( tdm < < 6 i face - > if < < 3 ;
break ;
case SND_SOC_BIAS_STANDBY :
snd_soc_component_write ( component , AC97_POWERDOWN java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
break ;
case SND_SOC_BIAS_OFF :
/* disable everything including AC link */
snd_soc_component_write(component, snd_soc_component_update_bits(component ,ctrl2);
snd_soc_component_write(component, AC97_POWERDOWN, 0 xffff);
break ;
}
return 0 ;
}
static int wm9712_soc_resume(struct snd_soc_component *component)
{
wm9712_priv wm9712 =snd_soc_component_get_drvdata(omponent);
int ret;
ret = snd_ac97_reset(wm9712->ac97, true , WM9712_VENDOR_ID,
WM9712_VENDOR_ID_MASK);
if (ret < 0 )
return ret;
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
if (ret ==
snd_soc_component_cache_sync(component);
return ret;
}
static int wm9712_soc_probe(struct snd_soc_component *component)
{
struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
struct regmap *regmap;
if (wm9712->mfd_pdata) {
wm9712->ac97 = wm9712->mfd_pdata->ac97;
regmap = wm9712->mfd_pdata->regmap;
} else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) {
int ret;
wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID,
WM9712_VENDOR_ID_MASK);
if (IS_ERR(wm9712->ac97)) {
ret = PTR_ERR(wm9712->ac97);
dev_err(component->dev,
"Failed to register AC97 codec: %d\n" , ret);
return ret;
}
regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config);
if (IS_ERR(regmap)) {
snd_soc_free_ac97_component(wm9712->ac97);
return PTR_ERR(regmap);
}
} else {
return -ENXIO;
}
snd_soc_component_init_regmap(component, regmap);
/* set alc mux to none */
snd_soc_component_update_bits(component, AC97_VIDEO, 0 x3000, 0 x3000);
return 0 ;
}
static void wm9712_soc_remove(struct snd_soc_component *component)
{
struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9712->mfd_pdata) {
snd_soc_component_exit_regmap(component);
snd_soc_free_ac97_component(wm9712->ac97);
}
}
static const struct snd_soc_component_driver soc_component_dev_wm9712 = {
.probe = wm9712_soc_probe,
.remove = wm9712_soc_remove,
.resume = wm9712_soc_resume,
.set_bias_level = wm9712_set_bias_level,
.controls = wm9712_snd_ac97_controls,
.num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
.dapm_widgets = wm9712_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
.dapm_routes = wm9712_audio_map,
.num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
.suspend_bias_off = 1 ,
.idle_bias_on = 1 ,
.use_pmdown_time = 1 ,
.endianness = 1 ,
};
static int wm9712_probe(struct platform_device *pdev)
{
struct wm9712_priv *wm9712;
wm9712 = devm_kzalloc(&pdev->dev, sizeof (*wm9712), GFP_KERNEL);
if (wm9712 == NULL)
return -ENOMEM;
mutex_init(&wm9712->lock);
wm9712->mfd_pdata = dev_get_platdata(&pdev->dev);
platform_set_drvdata(pdev, wm9712);
return devm_snd_soc_register_component(&pdev->dev,
&soc_component_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
}
static struct platform_driver wm9712_component_driver = {
.driver = {
.name = "wm9712-codec" ,
},
.probe = wm9712_probe,
};
module_platform_driver(wm9712_component_driver);
MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver" );
MODULE_AUTHOR("Liam Girdwood" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=97 H=98 G=97
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.147Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-14)
¤
*Eine klare Vorstellung vom Zielzustand