Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 182866
b: refs/heads/master
c: 3ed7074
h: refs/heads/master
v: v3
  • Loading branch information
Mark Brown committed Feb 1, 2010
1 parent 85a2d2c commit 89e1c0d
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 27 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: 2f1ff6614cb5938e5c5760358752d92deb67fb63
refs/heads/master: 3ed7074c4cc0de5ba77e180e5d96c23ef96859f0
8 changes: 8 additions & 0 deletions trunk/sound/soc/codecs/wm8993.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ static struct {
};

struct wm8993_priv {
struct wm_hubs_data hubs_data;
u16 reg_cache[WM8993_REGISTER_COUNT];
struct wm8993_platform_data pdata;
struct snd_soc_codec codec;
Expand Down Expand Up @@ -997,6 +998,11 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,

case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Tune DC servo configuration */
snd_soc_write(codec, 0x44, 3);
snd_soc_write(codec, 0x56, 3);
snd_soc_write(codec, 0x44, 0);

/* Bring up VMID with fast soft start */
snd_soc_update_bits(codec, WM8993_ANTIPOP2,
WM8993_STARTUP_BIAS_ENA |
Expand Down Expand Up @@ -1591,6 +1597,8 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
codec->num_dai = 1;
codec->private_data = wm8993;

wm8993->hubs_data.hp_startup_mode = 1;

memcpy(wm8993->reg_cache, wm8993_reg_defaults,
sizeof(wm8993->reg_cache));

Expand Down
142 changes: 116 additions & 26 deletions trunk/sound/soc/codecs/wm_hubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,24 +68,77 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec)
int count = 0;

dev_dbg(codec->dev, "Waiting for DC servo...\n");

do {
count++;
msleep(1);
reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
dev_dbg(codec->dev, "DC servo status: %x\n", reg);
} while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
!= WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
dev_dbg(codec->dev, "DC servo: %x\n", reg);
} while (reg & WM8993_DCS_DATAPATH_BUSY);

if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
!= WM8993_DCS_CAL_COMPLETE_MASK)
if (reg & WM8993_DCS_DATAPATH_BUSY)
dev_err(codec->dev, "Timed out waiting for DC Servo\n");
}

/*
* Startup calibration of the DC servo
*/
static void calibrate_dc_servo(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = codec->private_data;
u16 reg, dcs_cfg;

/* Set for 32 series updates */
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_SERIES_NO_01_MASK,
32 << WM8993_DCS_SERIES_NO_01_SHIFT);

/* Enable the DC servo. Write all bits to avoid triggering startup
* or write calibration.
*/
snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
0xFFFF,
WM8993_DCS_ENA_CHAN_0 |
WM8993_DCS_ENA_CHAN_1 |
WM8993_DCS_TRIG_SERIES_1 |
WM8993_DCS_TRIG_SERIES_0);

wait_for_dc_servo(codec);

/* Apply correction to DC servo result */
if (hubs->dcs_codes) {
dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
hubs->dcs_codes);

/* HPOUT1L */
reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) &
WM8993_DCS_INTEG_CHAN_0_MASK;;
reg += hubs->dcs_codes;
dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT;

/* HPOUT1R */
reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) &
WM8993_DCS_INTEG_CHAN_1_MASK;
reg += hubs->dcs_codes;
dcs_cfg |= reg;

/* Do it */
snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1);

wait_for_dc_servo(codec);
}
}

/*
* Update the DC servo calibration on gain changes
*/
static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int ret;
Expand Down Expand Up @@ -251,6 +304,47 @@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
line_tlv),
};

static int hp_supply_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct wm_hubs_data *hubs = codec->private_data;

switch (event) {
case SND_SOC_DAPM_PRE_PMU:
switch (hubs->hp_startup_mode) {
case 0:
break;
case 1:
/* Enable the headphone amp */
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA |
WM8993_HPOUT1R_ENA,
WM8993_HPOUT1L_ENA |
WM8993_HPOUT1R_ENA);

/* Enable the second stage */
snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY);
break;
default:
dev_err(codec->dev, "Unknown HP startup mode %d\n",
hubs->hp_startup_mode);
break;
}

case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
WM8993_CP_ENA, 0);
break;
}

return 0;
}

static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
Expand All @@ -271,38 +365,31 @@ static int hp_event(struct snd_soc_dapm_widget *w,
reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);

/* Start the DC servo */
snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
0xFFFF,
WM8993_DCS_ENA_CHAN_0 |
WM8993_DCS_ENA_CHAN_1 |
WM8993_DCS_TRIG_STARTUP_1 |
WM8993_DCS_TRIG_STARTUP_0);
wait_for_dc_servo(codec);
/* Smallest supported update interval */
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_TIMER_PERIOD_01_MASK, 1);

calibrate_dc_servo(codec);

reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
break;

case SND_SOC_DAPM_PRE_PMD:
reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1L_OUTP |
WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1R_DLY |
WM8993_HPOUT1R_OUTP);
snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY |
WM8993_HPOUT1L_RMV_SHORT |
WM8993_HPOUT1R_RMV_SHORT, 0);

snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
0xffff, 0);
snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_OUTP |
WM8993_HPOUT1R_OUTP, 0);

snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
0);

snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
WM8993_CP_ENA, 0);
break;
}

Expand Down Expand Up @@ -473,6 +560,8 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),

SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
NULL, 0,
hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
Expand Down Expand Up @@ -626,6 +715,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "Headphone PGA", NULL, "Left Headphone Mux" },
{ "Headphone PGA", NULL, "Right Headphone Mux" },
{ "Headphone PGA", NULL, "CLK_SYS" },
{ "Headphone PGA", NULL, "Headphone Supply" },

{ "HPOUT1L", NULL, "Headphone PGA" },
{ "HPOUT1R", NULL, "Headphone PGA" },
Expand Down
6 changes: 6 additions & 0 deletions trunk/sound/soc/codecs/wm_hubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ struct snd_soc_codec;

extern const unsigned int wm_hubs_spkmix_tlv[];

/* This *must* be the first element of the codec->private_data struct */
struct wm_hubs_data {
int dcs_codes;
int hp_startup_mode;
};

extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
Expand Down

0 comments on commit 89e1c0d

Please sign in to comment.