aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/codecs/wm_hubs.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/wm_hubs.c')
-rw-r--r--sound/soc/codecs/wm_hubs.c157
1 files changed, 107 insertions, 50 deletions
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index dfe957a47f2..916817fe663 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -1,7 +1,7 @@
/*
* wm_hubs.c -- WM8993/4 common code
*
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
@@ -50,16 +50,16 @@ static const char *speaker_ref_text[] = {
"VMID",
};
-static const struct soc_enum speaker_ref =
- SOC_ENUM_SINGLE(WM8993_SPEAKER_MIXER, 8, 2, speaker_ref_text);
+static SOC_ENUM_SINGLE_DECL(speaker_ref,
+ WM8993_SPEAKER_MIXER, 8, speaker_ref_text);
static const char *speaker_mode_text[] = {
"Class D",
"Class AB",
};
-static const struct soc_enum speaker_mode =
- SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
+static SOC_ENUM_SINGLE_DECL(speaker_mode,
+ WM8993_SPKMIXR_ATTENUATION, 8, speaker_mode_text);
static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
{
@@ -199,15 +199,58 @@ static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
list_add_tail(&cache->list, &hubs->dcs_cache);
}
+static int wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
+ u16 *reg_l, u16 *reg_r)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ u16 dcs_reg, reg;
+ int ret = 0;
+
+ switch (hubs->dcs_readback_mode) {
+ case 2:
+ dcs_reg = WM8994_DC_SERVO_4E;
+ break;
+ case 1:
+ dcs_reg = WM8994_DC_SERVO_READBACK;
+ break;
+ default:
+ dcs_reg = WM8993_DC_SERVO_3;
+ break;
+ }
+
+ /* Different chips in the family support different readback
+ * methods.
+ */
+ switch (hubs->dcs_readback_mode) {
+ case 0:
+ *reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
+ & WM8993_DCS_INTEG_CHAN_0_MASK;
+ *reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
+ & WM8993_DCS_INTEG_CHAN_1_MASK;
+ break;
+ case 2:
+ case 1:
+ reg = snd_soc_read(codec, dcs_reg);
+ *reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
+ >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+ *reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
+ break;
+ default:
+ WARN(1, "Unknown DCS readback method\n");
+ ret = -1;
+ }
+ return ret;
+}
+
/*
* Startup calibration of the DC servo
*/
-static void calibrate_dc_servo(struct snd_soc_codec *codec)
+static void enable_dc_servo(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct wm_hubs_dcs_cache *cache;
s8 offset;
- u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
+ u16 reg_l, reg_r, dcs_cfg, dcs_reg;
switch (hubs->dcs_readback_mode) {
case 2:
@@ -245,27 +288,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
WM8993_DCS_TRIG_STARTUP_1);
}
- /* Different chips in the family support different readback
- * methods.
- */
- switch (hubs->dcs_readback_mode) {
- case 0:
- reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
- & WM8993_DCS_INTEG_CHAN_0_MASK;
- reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
- & WM8993_DCS_INTEG_CHAN_1_MASK;
- break;
- case 2:
- case 1:
- reg = snd_soc_read(codec, dcs_reg);
- reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
- >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
- reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
- break;
- default:
- WARN(1, "Unknown DCS readback method\n");
+ if (wm_hubs_read_dc_servo(codec, &reg_l, &reg_r) < 0)
return;
- }
dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
@@ -276,12 +300,16 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
hubs->dcs_codes_l, hubs->dcs_codes_r);
/* HPOUT1R */
- offset = reg_r;
+ offset = (s8)reg_r;
+ dev_dbg(codec->dev, "DCS right %d->%d\n", offset,
+ offset + hubs->dcs_codes_r);
offset += hubs->dcs_codes_r;
dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
/* HPOUT1L */
- offset = reg_l;
+ offset = (s8)reg_l;
+ dev_dbg(codec->dev, "DCS left %d->%d\n", offset,
+ offset + hubs->dcs_codes_l);
offset += hubs->dcs_codes_l;
dcs_cfg |= (u8)offset;
@@ -309,7 +337,7 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
int ret;
@@ -502,6 +530,7 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
hubs->hp_startup_mode);
break;
}
+ break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
@@ -535,7 +564,7 @@ static int hp_event(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
- calibrate_dc_servo(codec);
+ enable_dc_servo(codec);
reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
@@ -582,7 +611,7 @@ static int earpiece_event(struct snd_soc_dapm_widget *w,
break;
default:
- BUG();
+ WARN(1, "Invalid event %d\n", event);
break;
}
@@ -619,6 +648,28 @@ static int lineout_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int micbias_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 = snd_soc_codec_get_drvdata(codec);
+
+ switch (w->shift) {
+ case WM8993_MICB1_ENA_SHIFT:
+ if (hubs->micb1_delay)
+ msleep(hubs->micb1_delay);
+ break;
+ case WM8993_MICB2_ENA_SHIFT:
+ if (hubs->micb2_delay)
+ msleep(hubs->micb2_delay);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
void wm_hubs_update_class_w(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
@@ -634,21 +685,22 @@ void wm_hubs_update_class_w(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8993_CLASS_W_0,
WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
+
+ snd_soc_write(codec, WM8993_LEFT_OUTPUT_VOLUME,
+ snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME));
+ snd_soc_write(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+ snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME));
}
EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, class_w_put_volsw)
static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
@@ -668,9 +720,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
static int class_w_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
@@ -685,15 +735,15 @@ static const char *hp_mux_text[] = {
"DAC",
};
-static const struct soc_enum hpl_enum =
- SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+static SOC_ENUM_SINGLE_DECL(hpl_enum,
+ WM8993_OUTPUT_MIXER1, 8, hp_mux_text);
const struct snd_kcontrol_new wm_hubs_hpl_mux =
WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
-static const struct soc_enum hpr_enum =
- SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+static SOC_ENUM_SINGLE_DECL(hpr_enum,
+ WM8993_OUTPUT_MIXER2, 8, hp_mux_text);
const struct snd_kcontrol_new wm_hubs_hpr_mux =
WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
@@ -809,8 +859,10 @@ SND_SOC_DAPM_INPUT("IN1RP"),
SND_SOC_DAPM_INPUT("IN2RN"),
SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
-SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
-SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0,
+ micbias_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0,
+ micbias_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
in1l_pga, ARRAY_SIZE(in1l_pga)),
@@ -1112,6 +1164,8 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ hubs->codec = codec;
+
INIT_LIST_HEAD(&hubs->dcs_cache);
init_completion(&hubs->dcs_done);
@@ -1143,13 +1197,16 @@ EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
int lineout1_diff, int lineout2_diff,
int lineout1fb, int lineout2fb,
- int jd_scthr, int jd_thr, int micbias1_lvl,
- int micbias2_lvl)
+ int jd_scthr, int jd_thr,
+ int micbias1_delay, int micbias2_delay,
+ int micbias1_lvl, int micbias2_lvl)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
hubs->lineout1_se = !lineout1_diff;
hubs->lineout2_se = !lineout2_diff;
+ hubs->micb1_delay = micbias1_delay;
+ hubs->micb2_delay = micbias2_delay;
if (!lineout1_diff)
snd_soc_update_bits(codec, WM8993_LINE_MIXER1,