Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 132813
b: refs/heads/master
c: 7393958
h: refs/heads/master
i:
  132811: 8320712
v: v3
  • Loading branch information
Peter Ujfalusi authored and Mark Brown committed Jan 29, 2009
1 parent 396e3ae commit fe8ff54
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 14 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0bf5460de96aa66abf9350333ee6711e73b3b31e
refs/heads/master: 7393958f630ac91e591e62058f2bdb61523ec60c
212 changes: 199 additions & 13 deletions trunk/sound/soc/codecs/twl4030.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_MISC_SET_2 (0x49) */
};

/* codec private data */
struct twl4030_priv {
unsigned int bypass_state;
unsigned int codec_powered;
unsigned int codec_muted;
};

/*
* read twl4030 register cache
*/
Expand Down Expand Up @@ -156,15 +163,20 @@ static int twl4030_write(struct snd_soc_codec *codec,

static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 mode;

if (enable == twl4030->codec_powered)
return;

mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
if (enable)
mode |= TWL4030_CODECPDZ;
else
mode &= ~TWL4030_CODECPDZ;

twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030->codec_powered = enable;

/* REVISIT: this delay is present in TI sample drivers */
/* but there seems to be no TRM requirement for it */
Expand All @@ -184,11 +196,82 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)

}

static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 reg_val;

if (mute == twl4030->codec_muted)
return;

if (mute) {
/* Bypass the reg_cache and mute the volumes
* Headset mute is done in it's own event handler
* Things to mute: Earpiece, PreDrivL/R, CarkitL/R
*/
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_EAR_GAIN),
TWL4030_REG_EAR_CTL);

reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PREDL_GAIN),
TWL4030_REG_PREDL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PREDR_GAIN),
TWL4030_REG_PREDL_CTL);

reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PRECKL_GAIN),
TWL4030_REG_PRECKL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PRECKL_GAIN),
TWL4030_REG_PRECKR_CTL);

/* Disable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val &= ~TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
} else {
/* Restore the volumes
* Headset mute is done in it's own event handler
* Things to restore: Earpiece, PreDrivL/R, CarkitL/R
*/
twl4030_write(codec, TWL4030_REG_EAR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL));

twl4030_write(codec, TWL4030_REG_PREDL_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL));
twl4030_write(codec, TWL4030_REG_PREDR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL));

twl4030_write(codec, TWL4030_REG_PRECKL_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL));
twl4030_write(codec, TWL4030_REG_PRECKR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL));

/* Enable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val |= TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
}

twl4030->codec_muted = mute;
}

static void twl4030_power_up(struct snd_soc_codec *codec)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 anamicl, regmisc1, byte;
int i = 0;

if (twl4030->codec_powered)
return;

/* set CODECPDZ to turn on codec */
twl4030_codec_enable(codec, 1);

Expand Down Expand Up @@ -220,6 +303,9 @@ static void twl4030_power_up(struct snd_soc_codec *codec)
twl4030_codec_enable(codec, 1);
}

/*
* Unconditional power down
*/
static void twl4030_power_down(struct snd_soc_codec *codec)
{
/* power down */
Expand Down Expand Up @@ -402,6 +488,22 @@ static const struct soc_enum twl4030_micpathtx2_enum =
static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);

/* Analog bypass for AudioR1 */
static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0);

/* Analog bypass for AudioL1 */
static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0);

/* Analog bypass for AudioR2 */
static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0);

/* Analog bypass for AudioL2 */
static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0);

static int micpath_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
Expand Down Expand Up @@ -497,6 +599,31 @@ static int headsetl_event(struct snd_soc_dapm_widget *w,
return 0;
}

static int bypass_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct soc_mixer_control *m =
(struct soc_mixer_control *)w->kcontrols->private_value;
struct twl4030_priv *twl4030 = w->codec->private_data;
unsigned char reg;

reg = twl4030_read_reg_cache(w->codec, m->reg);
if (reg & (1 << m->shift))
twl4030->bypass_state |=
(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
else
twl4030->bypass_state &=
~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));

if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
if (twl4030->bypass_state)
twl4030_codec_mute(w->codec, 0);
else
twl4030_codec_mute(w->codec, 1);
}
return 0;
}

/*
* Some of the gain controls in TWL (mostly those which are associated with
* the outputs) are implemented in an interesting way:
Expand Down Expand Up @@ -775,13 +902,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {

/* DACs */
SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
TWL4030_REG_AVDAC_CTL, 0, 0),
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
TWL4030_REG_AVDAC_CTL, 1, 0),
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
TWL4030_REG_AVDAC_CTL, 2, 0),
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
TWL4030_REG_AVDAC_CTL, 3, 0),
SND_SOC_NOPM, 0, 0),

/* Analog PGAs */
SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
Expand All @@ -793,6 +920,29 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
0, 0, NULL, 0),

/* Analog bypasses */
SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr1_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl1_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr2_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl2_control,
bypass_event, SND_SOC_DAPM_POST_REG),

SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
1, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
2, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
3, 0, NULL, 0),

/* Output MUX controls */
/* Earpiece */
SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0,
Expand Down Expand Up @@ -863,13 +1013,19 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0),

};

static const struct snd_soc_dapm_route intercon[] = {
{"ARXL1_APGA", NULL, "DAC Left1"},
{"ARXR1_APGA", NULL, "DAC Right1"},
{"ARXL2_APGA", NULL, "DAC Left2"},
{"ARXR2_APGA", NULL, "DAC Right2"},
{"Analog L1 Playback Mixer", NULL, "DAC Left1"},
{"Analog R1 Playback Mixer", NULL, "DAC Right1"},
{"Analog L2 Playback Mixer", NULL, "DAC Left2"},
{"Analog R2 Playback Mixer", NULL, "DAC Right2"},

{"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"},
{"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"},
{"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"},
{"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"},

/* Internal playback routings */
/* Earpiece */
Expand Down Expand Up @@ -951,6 +1107,17 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},

/* Analog bypass routes */
{"Right1 Analog Loopback", "Switch", "Analog Right Capture Route"},
{"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"},
{"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"},
{"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"},

{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
{"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"},

};

static int twl4030_add_widgets(struct snd_soc_codec *codec)
Expand All @@ -967,16 +1134,25 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec)
static int twl4030_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct twl4030_priv *twl4030 = codec->private_data;

switch (level) {
case SND_SOC_BIAS_ON:
twl4030_power_up(codec);
twl4030_codec_mute(codec, 0);
break;
case SND_SOC_BIAS_PREPARE:
/* TODO: develop a twl4030_prepare function */
twl4030_power_up(codec);
if (twl4030->bypass_state)
twl4030_codec_mute(codec, 0);
else
twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_STANDBY:
/* TODO: develop a twl4030_standby function */
twl4030_power_down(codec);
twl4030_power_up(codec);
if (twl4030->bypass_state)
twl4030_codec_mute(codec, 0);
else
twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_OFF:
twl4030_power_down(codec);
Expand All @@ -996,7 +1172,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = socdev->card->codec;
u8 mode, old_mode, format, old_format;


/* bit rate */
old_mode = twl4030_read_reg_cache(codec,
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
Expand Down Expand Up @@ -1038,6 +1213,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,

if (mode != old_mode) {
/* change rate and set CODECPDZ */
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030_codec_enable(codec, 1);
}
Expand Down Expand Up @@ -1258,11 +1434,19 @@ static int twl4030_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;

codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;

twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
if (twl4030 == NULL) {
kfree(codec);
return -ENOMEM;
}

codec->private_data = twl4030;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
Expand All @@ -1280,8 +1464,10 @@ static int twl4030_remove(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->card->codec;

printk(KERN_INFO "TWL4030 Audio Codec remove\n");
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
kfree(codec->private_data);
kfree(codec);

return 0;
Expand Down
15 changes: 15 additions & 0 deletions trunk/sound/soc/codecs/twl4030.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@
#define TWL4030_CLK256FS_EN 0x02
#define TWL4030_AIF_EN 0x01

/* EAR_CTL (0x21) */
#define TWL4030_EAR_GAIN 0x30

/* HS_GAIN_SET (0x23) Fields */

#define TWL4030_HSR_GAIN 0x0C
Expand Down Expand Up @@ -198,6 +201,18 @@
#define TWL4030_RAMP_DELAY_2581MS 0x1C
#define TWL4030_RAMP_EN 0x02

/* PREDL_CTL (0x25) */
#define TWL4030_PREDL_GAIN 0x30

/* PREDR_CTL (0x26) */
#define TWL4030_PREDR_GAIN 0x30

/* PRECKL_CTL (0x27) */
#define TWL4030_PRECKL_GAIN 0x30

/* PRECKR_CTL (0x28) */
#define TWL4030_PRECKR_GAIN 0x30

/* HFL_CTL (0x29, 0x2A) Fields */
#define TWL4030_HF_CTL_HB_EN 0x04
#define TWL4030_HF_CTL_LOOP_EN 0x08
Expand Down

0 comments on commit fe8ff54

Please sign in to comment.