0x00, 0x00, 0x00, 0x00, /* 88 */
0x00, 0x00, 0x00, 0x00, /* 92 */
0x00, 0x00, 0x00, 0x00, /* 96 */
- 0x00, 0x00, 0x02, /* 100 */
+ 0x00, 0x00, 0x02, 0x00, /* 100 */
+ 0x00, 0x00, 0x00, 0x00, /* 104 */
+ 0x00, 0x00, /* 108 */
};
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
};
+static const char *aic3x_agc_level[] =
+ { "-5.5dB", "-8dB", "-10dB", "-12dB", "-14dB", "-17dB", "-20dB", "-24dB" };
+static const struct soc_enum aic3x_agc_level_enum[] = {
+ SOC_ENUM_SINGLE(LAGC_CTRL_A, 4, 8, aic3x_agc_level),
+ SOC_ENUM_SINGLE(RAGC_CTRL_A, 4, 8, aic3x_agc_level),
+};
+
+static const char *aic3x_agc_attack[] = { "8ms", "11ms", "16ms", "20ms" };
+static const struct soc_enum aic3x_agc_attack_enum[] = {
+ SOC_ENUM_SINGLE(LAGC_CTRL_A, 2, 4, aic3x_agc_attack),
+ SOC_ENUM_SINGLE(RAGC_CTRL_A, 2, 4, aic3x_agc_attack),
+};
+
+static const char *aic3x_agc_decay[] = { "100ms", "200ms", "400ms", "500ms" };
+static const struct soc_enum aic3x_agc_decay_enum[] = {
+ SOC_ENUM_SINGLE(LAGC_CTRL_A, 0, 4, aic3x_agc_decay),
+ SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay),
+};
+
/*
* DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
*/
* adjust PGA to max value when ADC is on and will never go back.
*/
SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
+ SOC_ENUM("Left AGC Target level", aic3x_agc_level_enum[0]),
+ SOC_ENUM("Right AGC Target level", aic3x_agc_level_enum[1]),
+ SOC_ENUM("Left AGC Attack time", aic3x_agc_attack_enum[0]),
+ SOC_ENUM("Right AGC Attack time", aic3x_agc_attack_enum[1]),
+ SOC_ENUM("Left AGC Decay time", aic3x_agc_decay_enum[0]),
+ SOC_ENUM("Right AGC Decay time", aic3x_agc_decay_enum[1]),
+
+ /* De-emphasis */
+ SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0),
/* Input */
SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL,
static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0);
static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl =
- SOC_DOUBLE_TLV("Class-D Amplifier Gain", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv);
+ SOC_DOUBLE_TLV("Class-D Playback Volume", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv);
/* Left DAC Mux */
static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
}
found:
- data = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
- snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
- data | (pll_p << PLLP_SHIFT));
+ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, PLLP_MASK, pll_p);
snd_soc_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG,
pll_r << PLLR_SHIFT);
snd_soc_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
struct snd_soc_codec *codec = codec_dai->codec;
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ /* set clock on MCLK or GPIO2 or BCLK */
+ snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK,
+ clk_id << PLLCLK_IN_SHIFT);
+ snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK,
+ clk_id << CLKDIV_IN_SHIFT);
+
aic3x->sysclk = freq;
return 0;
}
#define _AIC3X_H
/* AIC3X register space */
-#define AIC3X_CACHEREGNUM 103
+#define AIC3X_CACHEREGNUM 110
/* Page select register */
#define AIC3X_PAGE_SELECT 0
#define HPLCOM_CFG 37
/* Right High Power Output control registers */
#define HPRCOM_CFG 38
+/* High Power Output Stage Control Register */
+#define HPOUT_SC 40
/* DAC Output Switching control registers */
#define DAC_LINE_MUX 41
/* High Power Output Driver Pop Reduction registers */
#define AIC3X_GPIOB_REG 101
/* Clock generation control register */
#define AIC3X_CLKGEN_CTRL_REG 102
+/* New AGC registers */
+#define LAGCN_ATTACK 103
+#define LAGCN_DECAY 104
+#define RAGCN_ATTACK 105
+#define RAGCN_DECAY 106
+/* New Programmable ADC Digital Path and I2C Bus Condition Register */
+#define NEW_ADC_DIGITALPATH 107
+/* Passive Analog Signal Bypass Selection During Powerdown Register */
+#define PASSIVE_BYPASS 108
+/* DAC Quiescent Current Adjustment Register */
+#define DAC_ICC_ADJ 109
/* Page select register bits */
#define PAGE0_SELECT 0
#define DUAL_RATE_MODE ((1 << 5) | (1 << 6))
#define LDAC2LCH (0x1 << 3)
#define RDAC2RCH (0x1 << 1)
+#define LDAC2RCH (0x2 << 3)
+#define RDAC2LCH (0x2 << 1)
+#define LDAC2MONOMIX (0x3 << 3)
+#define RDAC2MONOMIX (0x3 << 1)
/* PLL registers bitfields */
#define PLLP_SHIFT 0
+ #define PLLP_MASK 7
#define PLLQ_SHIFT 3
#define PLLR_SHIFT 0
#define PLLJ_SHIFT 2
#define PLL_CLKIN_SHIFT 4
#define MCLK_SOURCE 0x0
#define PLL_CLKDIV_SHIFT 0
+#define PLLCLK_IN_MASK 0x30
+#define PLLCLK_IN_SHIFT 4
+#define CLKDIV_IN_MASK 0xc0
+#define CLKDIV_IN_SHIFT 6
+/* clock in source */
+#define CLKIN_MCLK 0
+#define CLKIN_GPIO2 1
+#define CLKIN_BCLK 2
/* Software reset register bits */
#define SOFT_RESET 0x80
/*
* wm8994.c -- WM8994 ALSA SoC Audio driver
*
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
if (!wm8994->jackdet || !wm8994->jack_cb)
return;
- if (!wm8994->jackdet || !wm8994->jack_cb)
- return;
-
if (wm8994->active_refcount)
mode = WM1811_JACKDET_MODE_AUDIO;
static int wm8994_codec_suspend(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994 *control = wm8994->wm8994;
int i, ret;
- switch (control->type) {
- case WM8994:
- snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
- break;
- case WM1811:
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
- WM1811_JACKDET_MODE_MASK, 0);
- /* Fall through */
- case WM8958:
- snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
- WM8958_MICD_ENA, 0);
- break;
- }
-
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
sizeof(struct wm8994_fll_config));
i + 1, ret);
}
- switch (control->type) {
- case WM8994:
- if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
- snd_soc_update_bits(codec, WM8994_MICBIAS,
- WM8994_MICD_ENA, WM8994_MICD_ENA);
- break;
- case WM1811:
- if (wm8994->jackdet && wm8994->jack_cb) {
- /* Restart from idle */
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
- WM1811_JACKDET_MODE_MASK,
- WM1811_JACKDET_MODE_JACK);
- break;
- }
- break;
- case WM8958:
- if (wm8994->jack_cb)
- snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
- WM8958_MICD_ENA, WM8958_MICD_ENA);
- break;
- }
-
return 0;
}
#else
if (wm8994->pdata && wm8994->pdata->micdet_irq)
wm8994->micdet_irq = wm8994->pdata->micdet_irq;
- else if (wm8994->pdata && wm8994->pdata->irq_base)
- wm8994->micdet_irq = wm8994->pdata->irq_base +
- WM8994_IRQ_MIC1_DET;
pm_runtime_enable(codec->dev);
pm_runtime_idle(codec->dev);
dev_warn(codec->dev,
"Failed to request Mic detect IRQ: %d\n",
ret);
+ } else {
+ wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET,
+ wm8958_mic_irq, "Mic detect",
+ wm8994);
}
}
#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
[snd_soc_dapm_pre] = 0,
[snd_soc_dapm_supply] = 1,
[snd_soc_dapm_regulator_supply] = 1,
+ [snd_soc_dapm_clock_supply] = 1,
[snd_soc_dapm_micbias] = 2,
[snd_soc_dapm_dai_link] = 2,
[snd_soc_dapm_dai] = 3,
[snd_soc_dapm_aif_out] = 10,
[snd_soc_dapm_dai] = 10,
[snd_soc_dapm_dai_link] = 11,
+ [snd_soc_dapm_clock_supply] = 12,
[snd_soc_dapm_regulator_supply] = 12,
[snd_soc_dapm_supply] = 12,
[snd_soc_dapm_post] = 13,
if (dapm->codec->driver->set_bias_level)
ret = dapm->codec->driver->set_bias_level(dapm->codec,
level);
- else
- dapm->bias_level = level;
- }
+ } else
+ dapm->bias_level = level;
+
if (ret != 0)
goto out;
val = soc_widget_read(w, reg);
val = (val >> shift) & mask;
+ if (invert)
+ val = max - val;
- if ((invert && !val) || (!invert && val))
- p->connect = 1;
- else
- p->connect = 0;
+ p->connect = !!val;
}
break;
case snd_soc_dapm_mux: {
case snd_soc_dapm_vmid:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai:
switch (widget->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
return 0;
default:
break;
switch (widget->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
return 0;
default:
break;
}
EXPORT_SYMBOL_GPL(dapm_regulator_event);
+/*
+ * Handler for clock supply widget.
+ */
+int dapm_clock_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (!w->clk)
+ return -EIO;
+
+#ifdef CONFIG_HAVE_CLK
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ return clk_enable(w->clk);
+ } else {
+ clk_disable(w->clk);
+ return 0;
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dapm_clock_event);
+
static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
if (w->power_checked)
switch (w->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
/* Supplies can't affect their outputs, only their inputs */
break;
default:
}
list_for_each_entry(w, &card->widgets, list) {
- list_del_init(&w->dirty);
+ switch (w->id) {
+ case snd_soc_dapm_pre:
+ case snd_soc_dapm_post:
+ /* These widgets always need to be powered */
+ break;
+ default:
+ list_del_init(&w->dirty);
+ break;
+ }
if (w->power) {
d = w->dapm;
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
case snd_soc_dapm_micbias:
if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
d->target_bias_level = SND_SOC_BIAS_STANDBY;
case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
if (w->name)
count += sprintf(buf + count, "%s: %s\n",
w->name, w->power ? "On":"Off");
case snd_soc_dapm_post:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai:
path->connect = 0;
return 0;
}
+
+ dapm_mark_dirty(wsource, "Route added");
+ dapm_mark_dirty(wsink, "Route added");
+
return 0;
err:
return ret;
}
+static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_route *route)
+{
+ struct snd_soc_dapm_path *path, *p;
+ const char *sink;
+ const char *source;
+ char prefixed_sink[80];
+ char prefixed_source[80];
+
+ if (route->control) {
+ dev_err(dapm->dev,
+ "Removal of routes with controls not supported\n");
+ return -EINVAL;
+ }
+
+ if (dapm->codec && dapm->codec->name_prefix) {
+ snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
+ dapm->codec->name_prefix, route->sink);
+ sink = prefixed_sink;
+ snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
+ dapm->codec->name_prefix, route->source);
+ source = prefixed_source;
+ } else {
+ sink = route->sink;
+ source = route->source;
+ }
+
+ path = NULL;
+ list_for_each_entry(p, &dapm->card->paths, list) {
+ if (strcmp(p->source->name, source) != 0)
+ continue;
+ if (strcmp(p->sink->name, sink) != 0)
+ continue;
+ path = p;
+ break;
+ }
+
+ if (path) {
+ dapm_mark_dirty(path->source, "Route removed");
+ dapm_mark_dirty(path->sink, "Route removed");
+
+ list_del(&path->list);
+ list_del(&path->list_sink);
+ list_del(&path->list_source);
+ kfree(path);
+ } else {
+ dev_warn(dapm->dev, "Route %s->%s does not exist\n",
+ source, sink);
+ }
+
+ return 0;
+}
+
/**
* snd_soc_dapm_add_routes - Add routes between DAPM widgets
* @dapm: DAPM context
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num)
{
- int i, ret = 0;
+ int i, r, ret = 0;
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
- ret = snd_soc_dapm_add_route(dapm, route);
- if (ret < 0) {
+ r = snd_soc_dapm_add_route(dapm, route);
+ if (r < 0) {
dev_err(dapm->dev, "Failed to add route %s->%s\n",
route->source, route->sink);
- break;
+ ret = r;
}
route++;
}
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
+/**
+ * snd_soc_dapm_del_routes - Remove routes between DAPM widgets
+ * @dapm: DAPM context
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Removes routes from the DAPM context.
+ */
+int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_route *route, int num)
+{
+ int i, ret = 0;
+
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+ for (i = 0; i < num; i++) {
+ snd_soc_dapm_del_route(dapm, route);
+ route++;
+ }
+ mutex_unlock(&dapm->card->dapm_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);
+
static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
int max = mc->max;
- unsigned int invert = mc->invert;
unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+
+ if (snd_soc_volsw_is_stereo(mc))
+ dev_warn(widget->dapm->dev,
+ "Control '%s' is stereo, which is not supported\n",
+ kcontrol->id.name);
ucontrol->value.integer.value[0] =
(snd_soc_read(widget->codec, reg) >> shift) & mask;
- if (shift != rshift)
- ucontrol->value.integer.value[1] =
- (snd_soc_read(widget->codec, reg) >> rshift) & mask;
- if (invert) {
+ if (invert)
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
- if (shift != rshift)
- ucontrol->value.integer.value[1] =
- max - ucontrol->value.integer.value[1];
- }
return 0;
}
struct snd_soc_dapm_update update;
int wi;
+ if (snd_soc_volsw_is_stereo(mc))
+ dev_warn(widget->dapm->dev,
+ "Control '%s' is stereo, which is not supported\n",
+ kcontrol->id.name);
+
val = (ucontrol->value.integer.value[0] & mask);
+ connect = !!val;
if (invert)
val = max - val;
mask = mask << shift;
val = val << shift;
- if (val)
- /* new connection */
- connect = invert ? 0 : 1;
- else
- /* old connection must be powered down */
- connect = invert ? 1 : 0;
-
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
change = snd_soc_test_bits(widget->codec, reg, mask, val);
return NULL;
}
break;
+ case snd_soc_dapm_clock_supply:
+#ifdef CONFIG_CLKDEV_LOOKUP
+ w->clk = devm_clk_get(dapm->dev, w->name);
+ if (IS_ERR(w->clk)) {
+ ret = PTR_ERR(w->clk);
+ dev_err(dapm->dev, "Failed to request %s: %d\n",
+ w->name, ret);
+ return NULL;
+ }
+#else
+ return NULL;
+#endif
+ break;
default:
break;
}
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
w->power_check = dapm_supply_check_power;
break;
case snd_soc_dapm_dai:
static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
{
+ struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
LIST_HEAD(down_list);
int powerdown = 0;
+ mutex_lock(&card->dapm_mutex);
+
list_for_each_entry(w, &dapm->card->widgets, list) {
if (w->dapm != dapm)
continue;
snd_soc_dapm_set_bias_level(dapm,
SND_SOC_BIAS_STANDBY);
}
+
+ mutex_unlock(&card->dapm_mutex);
}
/*