diff options
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/ak4642.c | 33 | ||||
-rw-r--r-- | sound/soc/codecs/arizona.c | 257 | ||||
-rw-r--r-- | sound/soc/codecs/arizona.h | 26 | ||||
-rw-r--r-- | sound/soc/codecs/cs42l52.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/lm49453.c | 106 | ||||
-rw-r--r-- | sound/soc/codecs/sgtl5000.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/sta529.c | 9 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/wm2000.c | 35 | ||||
-rw-r--r-- | sound/soc/codecs/wm2200.c | 15 | ||||
-rw-r--r-- | sound/soc/codecs/wm5100.c | 6 | ||||
-rw-r--r-- | sound/soc/codecs/wm5102.c | 223 | ||||
-rw-r--r-- | sound/soc/codecs/wm5110.c | 114 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.c | 526 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.h | 18 | ||||
-rw-r--r-- | sound/soc/codecs/wmfw.h | 15 |
16 files changed, 1127 insertions, 268 deletions
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 1f0cdab03294..2d0378709702 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/of_device.h> #include <linux/module.h> #include <sound/soc.h> #include <sound/initval.h> @@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = { }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device_node *np = i2c->dev.of_node; + const struct snd_soc_codec_driver *driver; + + driver = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4642_of_match, &i2c->dev); + if (of_id) + driver = of_id->data; + } else { + driver = (struct snd_soc_codec_driver *)id->driver_data; + } + + if (!driver) { + dev_err(&i2c->dev, "no driver\n"); + return -EINVAL; + } + return snd_soc_register_codec(&i2c->dev, - (struct snd_soc_codec_driver *)id->driver_data, - &ak4642_dai, 1); + driver, &ak4642_dai, 1); } static int ak4642_i2c_remove(struct i2c_client *client) @@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } +static struct of_device_id ak4642_of_match[] = { + { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4642_of_match); + static const struct i2c_device_id ak4642_i2c_id[] = { { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 }, { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 }, @@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = { .driver = { .name = "ak4642-codec", .owner = THIS_MODULE, + .of_match_table = ak4642_of_match, }, .probe = ak4642_i2c_probe, .remove = ak4642_i2c_remove, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index adf397b9d0e6..d824c984c8a4 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -56,14 +56,14 @@ #define arizona_fll_warn(_fll, fmt, ...) \ dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_dbg(_fll, fmt, ...) \ - dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_aif_err(_dai, fmt, ...) \ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_warn(_dai, fmt, ...) \ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_dbg(_dai, fmt, ...) \ - dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", @@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "ASRC1R", "ASRC2L", "ASRC2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3INT3", + "ISRC3INT4", + "ISRC3DEC1", + "ISRC3DEC2", + "ISRC3DEC3", + "ISRC3DEC4", }; EXPORT_SYMBOL_GPL(arizona_mixer_texts); @@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x91, 0x92, 0x93, + 0xa0, /* ISRC1INT1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1DEC1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, + 0xb0, /* ISRC3DEC1 */ + 0xb1, + 0xb2, + 0xb3, + 0xb4, /* ISRC3INT1 */ + 0xb5, + 0xb6, + 0xb7, }; EXPORT_SYMBOL_GPL(arizona_mixer_values); @@ -275,6 +323,15 @@ const struct soc_enum arizona_lhpf4_mode = arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); +static const char *arizona_ng_hold_text[] = { + "30ms", "120ms", "250ms", "500ms", +}; + +const struct soc_enum arizona_ng_hold = + SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT, + 4, arizona_ng_hold_text); +EXPORT_SYMBOL_GPL(arizona_ng_hold); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -417,6 +474,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, case 147456000: val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT; break; + case 0: + dev_dbg(arizona->dev, "%s cleared\n", name); + *clk = freq; + return 0; default: return -EINVAL; } @@ -446,15 +507,9 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_DSP_A: mode = 0; break; - case SND_SOC_DAIFMT_DSP_B: - mode = 1; - break; case SND_SOC_DAIFMT_I2S: mode = 2; break; - case SND_SOC_DAIFMT_LEFT_J: - mode = 3; - break; default: arizona_aif_err(dai, "Unsupported DAI format %d\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK); @@ -641,6 +696,9 @@ static int arizona_startup(struct snd_pcm_substream *substream, return 0; } + if (base_rate == 0) + return 0; + if (base_rate % 8000) constraint = &arizona_44k1_constraint; else @@ -651,25 +709,81 @@ static int arizona_startup(struct snd_pcm_substream *substream, constraint); } +static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; + int i, sr_val; + + /* + * We will need to be more flexible than this in future, + * currently we use a single sample rate for SYSCLK. + */ + for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) + if (arizona_sr_vals[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(arizona_sr_vals)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, + ARIZONA_SAMPLE_RATE_1_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 0); + break; + case ARIZONA_CLK_ASYNCCLK: + snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, + ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, + 8 << ARIZONA_AIF1_RATE_SHIFT); + break; + default: + arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + return 0; +} + static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct arizona *arizona = priv->arizona; int base = dai->driver->base; const int *rates; - int i; - int bclk, lrclk, wl, frame, sr_val; + int i, ret; + int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; + int bclk, lrclk, wl, frame, bclk_target; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; + bclk_target = snd_soc_params_to_bclk(params); + if (chan_limit && chan_limit < params_channels(params)) { + arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); + bclk_target /= params_channels(params); + bclk_target *= chan_limit; + } + for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { - if (rates[i] >= snd_soc_params_to_bclk(params) && + if (rates[i] >= bclk_target && rates[i] % params_rate(params) == 0) { bclk = i; break; @@ -681,17 +795,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) - if (arizona_sr_vals[i] == params_rate(params)) - break; - if (i == ARRAY_SIZE(arizona_sr_vals)) { - arizona_aif_err(dai, "Unsupported sample rate %dHz\n", - params_rate(params)); - return -EINVAL; - } - sr_val = i; - - lrclk = snd_soc_params_to_bclk(params) / params_rate(params); + lrclk = rates[bclk] / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", rates[bclk], rates[bclk] / lrclk); @@ -699,27 +803,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; - /* - * We will need to be more flexible than this in future, - * currently we use a single sample rate for SYSCLK. - */ - switch (dai_priv->clk) { - case ARIZONA_CLK_SYSCLK: - snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, - ARIZONA_SAMPLE_RATE_1_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, 0); - break; - case ARIZONA_CLK_ASYNCCLK: - snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, - ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, 8); - break; - default: - arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); - return -EINVAL; - } + ret = arizona_hw_params_rate(substream, params, dai); + if (ret != 0) + return ret; snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); @@ -794,11 +880,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, return snd_soc_dapm_sync(&codec->dapm); } +static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + int base = dai->driver->base; + unsigned int reg; + + if (tristate) + reg = ARIZONA_AIF1_TRI; + else + reg = 0; + + return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_TRI, reg); +} + const struct snd_soc_dai_ops arizona_dai_ops = { .startup = arizona_startup, .set_fmt = arizona_set_fmt, .hw_params = arizona_hw_params, .set_sysclk = arizona_dai_set_sysclk, + .set_tristate = arizona_set_tristate, }; EXPORT_SYMBOL_GPL(arizona_dai_ops); @@ -812,17 +914,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id) } EXPORT_SYMBOL_GPL(arizona_init_dai); -static irqreturn_t arizona_fll_lock(int irq, void *data) -{ - struct arizona_fll *fll = data; - - arizona_fll_dbg(fll, "Lock status changed\n"); - - complete(&fll->lock); - - return IRQ_HANDLED; -} - static irqreturn_t arizona_fll_clock_ok(int irq, void *data) { struct arizona_fll *fll = data; @@ -915,7 +1006,7 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->n = target / (ratio * Fref); - if (target % Fref) { + if (target % (ratio * Fref)) { gcd_fll = gcd(target, ratio * Fref); arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); @@ -927,6 +1018,15 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->lambda = 0; } + /* Round down to 16bit range with cost of accuracy lost. + * Denominator must be bigger than numerator so we only + * take care of it. + */ + while (cfg->lambda >= (1 << 16)) { + cfg->theta >>= 1; + cfg->lambda >>= 1; + } + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", cfg->n, cfg->theta, cfg->lambda); arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", @@ -1062,7 +1162,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, { int ret; - init_completion(&fll->lock); init_completion(&fll->ok); fll->id = id; @@ -1073,13 +1172,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), "FLL%d clock OK", id); - ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, - arizona_fll_lock, fll); - if (ret != 0) { - dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", - id, ret); - } - ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, arizona_fll_clock_ok, fll); if (ret != 0) { @@ -1087,10 +1179,47 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, id, ret); } + regmap_update_bits(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + return 0; } EXPORT_SYMBOL_GPL(arizona_init_fll); +/** + * arizona_set_output_mode - Set the mode of the specified output + * + * @codec: Device to configure + * @output: Output number + * @diff: True to set the output to differential mode + * + * Some systems use external analogue switches to connect more + * analogue devices to the CODEC than are supported by the device. In + * some systems this requires changing the switched output from single + * ended to differential mode dynamically at runtime, an operation + * supported using this function. + * + * Most systems have a single static configuration and should use + * platform data instead. + */ +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff) +{ + unsigned int reg, val; + + if (output < 1 || output > 6) + return -EINVAL; + + reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8; + + if (diff) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val); +} +EXPORT_SYMBOL_GPL(arizona_set_output_mode); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 41dae1ed3b71..116372c91f5d 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -34,15 +34,15 @@ #define ARIZONA_FLL_SRC_MCLK1 0 #define ARIZONA_FLL_SRC_MCLK2 1 -#define ARIZONA_FLL_SRC_SLIMCLK 2 -#define ARIZONA_FLL_SRC_FLL1 3 -#define ARIZONA_FLL_SRC_FLL2 4 -#define ARIZONA_FLL_SRC_AIF1BCLK 5 -#define ARIZONA_FLL_SRC_AIF2BCLK 6 -#define ARIZONA_FLL_SRC_AIF3BCLK 7 -#define ARIZONA_FLL_SRC_AIF1LRCLK 8 -#define ARIZONA_FLL_SRC_AIF2LRCLK 9 -#define ARIZONA_FLL_SRC_AIF3LRCLK 10 +#define ARIZONA_FLL_SRC_SLIMCLK 3 +#define ARIZONA_FLL_SRC_FLL1 4 +#define ARIZONA_FLL_SRC_FLL2 5 +#define ARIZONA_FLL_SRC_AIF1BCLK 8 +#define ARIZONA_FLL_SRC_AIF2BCLK 9 +#define ARIZONA_FLL_SRC_AIF3BCLK 10 +#define ARIZONA_FLL_SRC_AIF1LRCLK 12 +#define ARIZONA_FLL_SRC_AIF2LRCLK 13 +#define ARIZONA_FLL_SRC_AIF3LRCLK 14 #define ARIZONA_MIXER_VOL_MASK 0x00FE #define ARIZONA_MIXER_VOL_SHIFT 1 @@ -66,7 +66,7 @@ struct arizona_priv { struct arizona_dai_priv dai[ARIZONA_MAX_DAI]; }; -#define ARIZONA_NUM_MIXER_INPUTS 75 +#define ARIZONA_NUM_MIXER_INPUTS 99 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; @@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode; extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; +extern const struct soc_enum arizona_ng_hold; + extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -195,7 +197,6 @@ struct arizona_fll { int id; unsigned int base; unsigned int vco_mult; - struct completion lock; struct completion ok; unsigned int fref; unsigned int fout; @@ -211,4 +212,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source, extern int arizona_init_dai(struct arizona_priv *priv, int dai); +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, + bool diff); + #endif diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 99bb1c69499e..9811a5478c87 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -737,7 +737,7 @@ static const struct cs42l52_clk_para clk_map_table[] = { static int cs42l52_get_clk(int mclk, int rate) { - int i, ret = 0; + int i, ret = -EINVAL; u_int mclk1, mclk2 = 0; for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { @@ -749,8 +749,6 @@ static int cs42l52_get_clk(int mclk, int rate) } } } - if (ret > ARRAY_SIZE(clk_map_table)) - return -EINVAL; return ret; } diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index d75257d40a49..e19490cfb3a8 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -111,9 +111,9 @@ static struct reg_default lm49453_reg_defs[] = { { 101, 0x00 }, { 102, 0x00 }, { 103, 0x01 }, - { 105, 0x01 }, - { 106, 0x00 }, - { 107, 0x01 }, + { 104, 0x01 }, + { 105, 0x00 }, + { 106, 0x01 }, { 107, 0x00 }, { 108, 0x00 }, { 109, 0x00 }, @@ -163,56 +163,25 @@ static struct reg_default lm49453_reg_defs[] = { { 184, 0x00 }, { 185, 0x00 }, { 186, 0x00 }, - { 189, 0x00 }, + { 187, 0x00 }, { 188, 0x00 }, - { 194, 0x00 }, - { 195, 0x00 }, - { 196, 0x00 }, - { 197, 0x00 }, - { 200, 0x00 }, - { 201, 0x00 }, - { 202, 0x00 }, - { 203, 0x00 }, - { 204, 0x00 }, - { 205, 0x00 }, - { 208, 0x00 }, + { 189, 0x00 }, + { 208, 0x06 }, { 209, 0x00 }, - { 210, 0x00 }, - { 211, 0x00 }, - { 213, 0x00 }, - { 214, 0x00 }, - { 215, 0x00 }, - { 216, 0x00 }, - { 217, 0x00 }, - { 218, 0x00 }, - { 219, 0x00 }, + { 210, 0x08 }, + { 211, 0x54 }, + { 212, 0x14 }, + { 213, 0x0d }, + { 214, 0x0d }, + { 215, 0x14 }, + { 216, 0x60 }, { 221, 0x00 }, { 222, 0x00 }, + { 223, 0x00 }, { 224, 0x00 }, - { 225, 0x00 }, - { 226, 0x00 }, - { 227, 0x00 }, - { 228, 0x00 }, - { 229, 0x00 }, - { 230, 0x13 }, - { 231, 0x00 }, - { 232, 0x80 }, - { 233, 0x0C }, - { 234, 0xDD }, - { 235, 0x00 }, - { 236, 0x04 }, - { 237, 0x00 }, - { 238, 0x00 }, - { 239, 0x00 }, - { 240, 0x00 }, - { 241, 0x00 }, - { 242, 0x00 }, - { 243, 0x00 }, - { 244, 0x00 }, - { 245, 0x00 }, { 248, 0x00 }, { 249, 0x00 }, - { 254, 0x00 }, + { 250, 0x00 }, { 255, 0x00 }, }; @@ -525,36 +494,41 @@ SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0), }; /* TLV Declarations */ -static const DECLARE_TLV_DB_SCALE(digital_tlv, -7650, 150, 1); -static const DECLARE_TLV_DB_SCALE(port_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(adc_dac_tlv, -7650, 150, 1); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 200, 1); +static const DECLARE_TLV_DB_SCALE(port_tlv, -1800, 600, 0); +static const DECLARE_TLV_DB_SCALE(stn_tlv, -7200, 150, 0); static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = { /* Sidetone supports mono only */ SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG, - 0, 0x3F, 0, digital_tlv), + 0, 0x3F, 0, stn_tlv), SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG, - 0, 0x3F, 0, digital_tlv), + 0, 0x3F, 0, stn_tlv), SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG, - 0, 0x3F, 0, digital_tlv), + 0, 0x3F, 0, stn_tlv), SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG, - 0, 0x3F, 0, digital_tlv), + 0, 0x3F, 0, stn_tlv), SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG, - 0, 0x3F, 0, digital_tlv), + 0, 0x3F, 0, stn_tlv), SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG, - 0, 0x3F, 0, digital_tlv), + 0, 0x3F, 0, stn_tlv), }; static const struct snd_kcontrol_new lm49453_snd_controls[] = { /* mic1 and mic2 supports mono only */ - SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_ADC_LEVELL_REG, 0, 6, - 0, digital_tlv), - SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_ADC_LEVELR_REG, 0, 6, - 0, digital_tlv), + SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_MICL_REG, 0, 15, 0, mic_tlv), + SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_MICR_REG, 0, 15, 0, mic_tlv), + + SOC_SINGLE_TLV("ADCL Volume", LM49453_P0_ADC_LEVELL_REG, 0, 63, + 0, adc_dac_tlv), + SOC_SINGLE_TLV("ADCR Volume", LM49453_P0_ADC_LEVELR_REG, 0, 63, + 0, adc_dac_tlv), SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG, - LM49453_P0_DMIC1_LEVELR_REG, 0, 6, 0, digital_tlv), + LM49453_P0_DMIC1_LEVELR_REG, 0, 63, 0, adc_dac_tlv), SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG, - LM49453_P0_DMIC2_LEVELR_REG, 0, 6, 0, digital_tlv), + LM49453_P0_DMIC2_LEVELR_REG, 0, 63, 0, adc_dac_tlv), SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum), SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum), @@ -569,16 +543,16 @@ static const struct snd_kcontrol_new lm49453_snd_controls[] = { 2, 1, 0), SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG, - LM49453_P0_DAC_HP_LEVELR_REG, 0, 6, 0, digital_tlv), + LM49453_P0_DAC_HP_LEVELR_REG, 0, 63, 0, adc_dac_tlv), SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG, - LM49453_P0_DAC_LO_LEVELR_REG, 0, 6, 0, digital_tlv), + LM49453_P0_DAC_LO_LEVELR_REG, 0, 63, 0, adc_dac_tlv), SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG, - LM49453_P0_DAC_LS_LEVELR_REG, 0, 6, 0, digital_tlv), + LM49453_P0_DAC_LS_LEVELR_REG, 0, 63, 0, adc_dac_tlv), SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG, - LM49453_P0_DAC_HA_LEVELR_REG, 0, 6, 0, digital_tlv), + LM49453_P0_DAC_HA_LEVELR_REG, 0, 63, 0, adc_dac_tlv), SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG, - 0, 6, 0, digital_tlv), + 0, 63, 0, adc_dac_tlv), SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, 0, 3, 0, port_tlv), @@ -1218,7 +1192,7 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) } snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG, - LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(1)|BIT(5), + LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(0)|BIT(5), (aif_val | mode | clk_phase)); snd_soc_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index cb1675cd8e1c..92bbfec9b107 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -401,7 +401,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { 5, 1, 0), SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL, - 0, 4, 0, mic_gain_tlv), + 0, 3, 0, mic_gain_tlv), }; /* mute the codec used by alsa core */ @@ -1344,7 +1344,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) SGTL5000_HP_ZCD_EN | SGTL5000_ADC_ZCD_EN); - snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0); + snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 2); /* * disable DAP diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index ab355c4f0b2d..40c07be9b581 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -74,9 +74,10 @@ SNDRV_PCM_FMTBIT_S32_LE) #define S2PC_VALUE 0x98 #define CLOCK_OUT 0x60 -#define LEFT_J_DATA_FORMAT 0x10 -#define I2S_DATA_FORMAT 0x12 -#define RIGHT_J_DATA_FORMAT 0x14 +#define DATA_FORMAT_MSK 0x0E +#define LEFT_J_DATA_FORMAT 0x00 +#define I2S_DATA_FORMAT 0x02 +#define RIGHT_J_DATA_FORMAT 0x04 #define CODEC_MUTE_VAL 0x80 #define POWER_CNTLMSAK 0x40 @@ -289,7 +290,7 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt) return -EINVAL; } - snd_soc_update_bits(codec, STA529_S2PCFG0, 0x0D, mode); + snd_soc_update_bits(codec, STA529_S2PCFG0, DATA_FORMAT_MSK, mode); return 0; } diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5708a973a776..49891432af74 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1210,13 +1210,13 @@ static struct snd_soc_dai_driver aic3x_dai = { .name = "tlv320aic3x-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 1cbe88f01d63..eb96b8768098 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -76,6 +76,8 @@ struct wm2000_priv { int anc_download_size; char *anc_download; + + struct mutex lock; }; static int wm2000_write(struct i2c_client *i2c, unsigned int reg, @@ -209,9 +211,9 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue) ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY); if (wm2000->speech_clarity) - ret &= ~WM2000_SPEECH_CLARITY; - else ret |= WM2000_SPEECH_CLARITY; + else + ret &= ~WM2000_SPEECH_CLARITY; wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret); wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33); @@ -599,13 +601,20 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int anc_active = ucontrol->value.enumerated.item[0]; + int ret; if (anc_active > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->anc_active = anc_active; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, @@ -625,13 +634,20 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int val = ucontrol->value.enumerated.item[0]; + int ret; if (val > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->spk_ena = val; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_kcontrol_new wm2000_controls[] = { @@ -648,6 +664,9 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); + int ret; + + mutex_lock(&wm2000->lock); if (SND_SOC_DAPM_EVENT_ON(event)) wm2000->anc_eng_ena = 1; @@ -655,7 +674,11 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_OFF(event)) wm2000->anc_eng_ena = 0; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { @@ -782,6 +805,8 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, return -ENOMEM; } + mutex_init(&wm2000->lock); + dev_set_drvdata(&i2c->dev, wm2000); wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index afcf31df77e0..d5371e0d8909 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1019,8 +1019,6 @@ static const char *wm2200_mixer_texts[] = { "EQR", "LHPF1", "LHPF2", - "LHPF3", - "LHPF4", "DSP1.1", "DSP1.2", "DSP1.3", @@ -1053,7 +1051,6 @@ static int wm2200_mixer_values[] = { 0x25, 0x50, /* EQ */ 0x51, - 0x52, 0x60, /* LHPF1 */ 0x61, /* LHPF2 */ 0x68, /* DSP1 */ @@ -1129,9 +1126,9 @@ SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL, SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L, WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L, WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1), SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L, @@ -1566,15 +1563,9 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_DSP_A: fmt_val = 0; break; - case SND_SOC_DAIFMT_DSP_B: - fmt_val = 1; - break; case SND_SOC_DAIFMT_I2S: fmt_val = 2; break; - case SND_SOC_DAIFMT_LEFT_J: - fmt_val = 3; - break; default: dev_err(codec->dev, "Unsupported DAI format %d\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK); @@ -1626,7 +1617,7 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV, lrclk); snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_5, - WM2200_AIF1_FMT_MASK << 1, fmt_val << 1); + WM2200_AIF1_FMT_MASK, fmt_val); return 0; } diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 5a5f36936235..54397a508073 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1279,15 +1279,9 @@ static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_DSP_A: mask = 0; break; - case SND_SOC_DAIFMT_DSP_B: - mask = 1; - break; case SND_SOC_DAIFMT_I2S: mask = 2; break; - case SND_SOC_DAIFMT_LEFT_J: - mask = 3; - break; default: dev_err(codec->dev, "Unsupported DAI format %d\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 688ade080589..5e85b645f2eb 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -36,12 +36,16 @@ struct wm5102_priv { struct arizona_priv core; struct arizona_fll fll[2]; + + unsigned int spk_ena:2; + unsigned int spk_ena_pending:1; }; static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); static const struct wm_adsp_region wm5102_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, @@ -600,6 +604,17 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +#define WM5102_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + static const struct snd_kcontrol_new wm5102_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT, 1, 0), @@ -608,32 +623,44 @@ SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), @@ -771,6 +798,22 @@ SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), @@ -787,6 +830,47 @@ ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), }; +static int wm5102_spk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec); + + if (arizona->rev < 1) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!wm5102->spk_ena) { + snd_soc_write(codec, 0x4f5, 0x25a); + wm5102->spk_ena_pending = true; + } + break; + case SND_SOC_DAPM_POST_PMU: + if (wm5102->spk_ena_pending) { + msleep(75); + snd_soc_write(codec, 0x4f5, 0xda); + wm5102->spk_ena_pending = false; + wm5102->spk_ena++; + } + break; + case SND_SOC_DAPM_PRE_PMD: + wm5102->spk_ena--; + if (!wm5102->spk_ena) + snd_soc_write(codec, 0x4f5, 0x25a); + break; + case SND_SOC_DAPM_POST_PMD: + if (!wm5102->spk_ena) + snd_soc_write(codec, 0x4f5, 0x0da); + break; + } + + return 0; +} + + ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); @@ -836,6 +920,18 @@ ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE); @@ -852,8 +948,7 @@ static const unsigned int wm5102_aec_loopback_values[] = { static const struct soc_enum wm5102_aec_loopback = SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, - ARIZONA_AEC_LOOPBACK_SRC_MASK, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, ARRAY_SIZE(wm5102_aec_loopback_texts), wm5102_aec_loopback_texts, wm5102_aec_loopback_values); @@ -959,6 +1054,26 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, @@ -1034,10 +1149,10 @@ SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1, - ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1, - ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, @@ -1095,6 +1210,18 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + WM_ADSP2("DSP1", 0), SND_SOC_DAPM_OUTPUT("HPOUT1L"), @@ -1150,6 +1277,14 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"), { name, "ASRC1R", "ASRC1R" }, \ { name, "ASRC2L", "ASRC2L" }, \ { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ { name, "DSP1.1", "DSP1" }, \ { name, "DSP1.2", "DSP1" }, \ { name, "DSP1.3", "DSP1" }, \ @@ -1246,6 +1381,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "ASRC2L", NULL, "ASRC2L Input" }, { "ASRC2R", NULL, "ASRC2R Input" }, + { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" }, + { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" }, + + { "ISRC1INT1", NULL, "ISRC1INT1 Input" }, + { "ISRC1INT2", NULL, "ISRC1INT2 Input" }, + + { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" }, + { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" }, + + { "ISRC2INT1", NULL, "ISRC2INT1 Input" }, + { "ISRC2INT2", NULL, "ISRC2INT2 Input" }, + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), @@ -1293,6 +1440,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MUX_ROUTES("ASRC2L"), ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MUX_ROUTES("ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2"), + ARIZONA_DSP_ROUTES("DSP1"), { "AEC Loopback", "HPOUT1L", "OUT1L" }, @@ -1420,6 +1579,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) if (ret != 0) return ret; + ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1); + if (ret != 0) + return ret; + snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); priv->core.arizona->dapm = &codec->dapm; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index ae80c8c28536..23199372d518 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -41,6 +41,21 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM5110_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) static const struct snd_kcontrol_new wm5110_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, @@ -52,37 +67,52 @@ SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), @@ -263,6 +293,25 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L), +WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R), +WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), +WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L), +WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), @@ -344,8 +393,7 @@ static const unsigned int wm5110_aec_loopback_values[] = { static const struct soc_enum wm5110_aec_loopback = SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, - ARIZONA_AEC_LOOPBACK_SRC_MASK, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, ARRAY_SIZE(wm5110_aec_loopback_texts), wm5110_aec_loopback_texts, wm5110_aec_loopback_values); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ffc89fab96fb..f3f7e75f8628 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/firmware.h> +#include <linux/list.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -103,9 +104,19 @@ #define ADSP1_START_SHIFT 0 /* DSP1_START */ #define ADSP1_START_WIDTH 1 /* DSP1_START */ -#define ADSP2_CONTROL 0 -#define ADSP2_CLOCKING 1 -#define ADSP2_STATUS1 4 +/* + * ADSP1 Control 31 + */ +#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ + +#define ADSP2_CONTROL 0x0 +#define ADSP2_CLOCKING 0x1 +#define ADSP2_STATUS1 0x4 +#define ADSP2_WDMA_CONFIG_1 0x30 +#define ADSP2_WDMA_CONFIG_2 0x31 +#define ADSP2_RDMA_CONFIG_1 0x34 /* * ADSP2 Control @@ -143,6 +154,109 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +struct wm_adsp_buf { + struct list_head list; + void *buf; +}; + +static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, + struct list_head *list) +{ + struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); + + if (buf == NULL) + return NULL; + + buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); + if (!buf->buf) { + kfree(buf); + return NULL; + } + + if (list) + list_add_tail(&buf->list, list); + + return buf; +} + +static void wm_adsp_buf_free(struct list_head *list) +{ + while (!list_empty(list)) { + struct wm_adsp_buf *buf = list_first_entry(list, + struct wm_adsp_buf, + list); + list_del(&buf->list); + kfree(buf->buf); + kfree(buf); + } +} + +#define WM_ADSP_NUM_FW 4 + +static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { + "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC" +}; + +static struct { + const char *file; +} wm_adsp_fw[WM_ADSP_NUM_FW] = { + { .file = "mbc-vss" }, + { .file = "tx" }, + { .file = "tx-spk" }, + { .file = "rx-anc" }, +}; + +static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; + + return 0; +} + +static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) + return 0; + + if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + + if (adsp[e->shift_l].running) + return -EBUSY; + + adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct soc_enum wm_adsp_fw_enum[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), +}; + +const struct snd_kcontrol_new wm_adsp_fw_controls[] = { + SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], + wm_adsp_fw_get, wm_adsp_fw_put), +}; +EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -156,8 +270,29 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, return NULL; } +static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, + unsigned int offset) +{ + switch (region->type) { + case WMFW_ADSP1_PM: + return region->base + (offset * 3); + case WMFW_ADSP1_DM: + return region->base + (offset * 2); + case WMFW_ADSP2_XM: + return region->base + (offset * 2); + case WMFW_ADSP2_YM: + return region->base + (offset * 2); + case WMFW_ADSP1_ZM: + return region->base + (offset * 2); + default: + WARN_ON(NULL != "Unknown memory region type"); + return offset; + } +} + static int wm_adsp_load(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); const struct firmware *firmware; struct regmap *regmap = dsp->regmap; unsigned int pos = 0; @@ -169,6 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) const struct wm_adsp_region *mem; const char *region_name; char *file, *text; + struct wm_adsp_buf *buf; unsigned int reg; int regions = 0; int ret, offset, type, sizes; @@ -177,7 +313,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -282,27 +419,27 @@ static int wm_adsp_load(struct wm_adsp *dsp) case WMFW_ADSP1_PM: BUG_ON(!mem); region_name = "PM"; - reg = mem->base + (offset * 3); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_DM: BUG_ON(!mem); region_name = "DM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_XM: BUG_ON(!mem); region_name = "XM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_YM: BUG_ON(!mem); region_name = "YM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_ZM: BUG_ON(!mem); region_name = "ZM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; default: adsp_warn(dsp, @@ -322,8 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - ret = regmap_raw_write(regmap, reg, region->data, - le32_to_cpu(region->len)); + buf = wm_adsp_buf_alloc(region->data, + le32_to_cpu(region->len), + &buf_list); + if (!buf) { + adsp_err(dsp, "Out of memory\n"); + return -ENOMEM; + } + + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(region->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write %d bytes at %d in %s: %d\n", @@ -337,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp) pos += le32_to_cpu(region->len) + sizeof(*region); regions++; } - + + ret = regmap_async_complete(regmap); + if (ret != 0) { + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + goto out_fw; + } + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, regions, pos - firmware->size); out_fw: + regmap_async_complete(regmap); + wm_adsp_buf_free(&buf_list); release_firmware(firmware); out: kfree(file); @@ -350,21 +503,222 @@ out: return ret; } +static int wm_adsp_setup_algs(struct wm_adsp *dsp) +{ + struct regmap *regmap = dsp->regmap; + struct wmfw_adsp1_id_hdr adsp1_id; + struct wmfw_adsp2_id_hdr adsp2_id; + struct wmfw_adsp1_alg_hdr *adsp1_alg; + struct wmfw_adsp2_alg_hdr *adsp2_alg; + void *alg, *buf; + struct wm_adsp_alg_region *region; + const struct wm_adsp_region *mem; + unsigned int pos, term; + size_t algs, buf_size; + __be32 val; + int i, ret; + + switch (dsp->type) { + case WMFW_ADSP1: + mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); + break; + case WMFW_ADSP2: + mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); + break; + default: + mem = NULL; + break; + } + + if (mem == NULL) { + BUG_ON(mem != NULL); + return -EINVAL; + } + + switch (dsp->type) { + case WMFW_ADSP1: + ret = regmap_raw_read(regmap, mem->base, &adsp1_id, + sizeof(adsp1_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + buf = &adsp1_id; + buf_size = sizeof(adsp1_id); + + algs = be32_to_cpu(adsp1_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp1_id.fw.id), + (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp1_id) / 2; + term = pos + ((sizeof(*adsp1_alg) * algs) / 2); + break; + + case WMFW_ADSP2: + ret = regmap_raw_read(regmap, mem->base, &adsp2_id, + sizeof(adsp2_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + buf = &adsp2_id; + buf_size = sizeof(adsp2_id); + + algs = be32_to_cpu(adsp2_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp2_id.fw.id), + (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp2_id) / 2; + term = pos + ((sizeof(*adsp2_alg) * algs) / 2); + break; + + default: + BUG_ON(NULL == "Unknown DSP type"); + return -EINVAL; + } + + if (algs == 0) { + adsp_err(dsp, "No algorithms\n"); + return -EINVAL; + } + + if (algs > 1024) { + adsp_err(dsp, "Algorithm count %zx excessive\n", algs); + print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, + buf, buf_size); + return -EINVAL; + } + + /* Read the terminator first to validate the length */ + ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list end: %d\n", + ret); + return ret; + } + + if (be32_to_cpu(val) != 0xbedead) + adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", + term, be32_to_cpu(val)); + + alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); + if (!alg) + return -ENOMEM; + + ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list: %d\n", + ret); + goto out; + } + + adsp1_alg = alg; + adsp2_alg = alg; + + for (i = 0; i < algs; i++) { + switch (dsp->type) { + case WMFW_ADSP1: + adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", + i, be32_to_cpu(adsp1_alg[i].alg.id), + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp1_alg[i].dm), + be32_to_cpu(adsp1_alg[i].zm)); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_DM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].dm); + list_add_tail(®ion->list, &dsp->alg_regions); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_ZM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); + break; + + case WMFW_ADSP2: + adsp_info(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", + i, be32_to_cpu(adsp2_alg[i].alg.id), + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp2_alg[i].xm), + be32_to_cpu(adsp2_alg[i].ym), + be32_to_cpu(adsp2_alg[i].zm)); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_XM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].xm); + list_add_tail(®ion->list, &dsp->alg_regions); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_YM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].ym); + list_add_tail(®ion->list, &dsp->alg_regions); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_ZM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); + break; + } + } + +out: + kfree(alg); + return ret; +} + static int wm_adsp_load_coeff(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); struct regmap *regmap = dsp->regmap; struct wmfw_coeff_hdr *hdr; struct wmfw_coeff_item *blk; const struct firmware *firmware; + const struct wm_adsp_region *mem; + struct wm_adsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg; char *file; + struct wm_adsp_buf *buf; + int tmp; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -384,7 +738,17 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) hdr = (void*)&firmware->data[0]; if (memcmp(hdr->magic, "WMDR", 4) != 0) { adsp_err(dsp, "%s: invalid magic\n", file); - return -EINVAL; + goto out_fw; + } + + switch (be32_to_cpu(hdr->rev) & 0xff) { + case 1: + break; + default: + adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", + file, be32_to_cpu(hdr->rev) & 0xff); + ret = -EINVAL; + goto out_fw; } adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, @@ -399,8 +763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) pos - firmware->size > sizeof(*blk)) { blk = (void*)(&firmware->data[pos]); - type = be32_to_cpu(blk->type) & 0xff; - offset = le32_to_cpu(blk->offset) & 0xffffff; + type = le16_to_cpu(blk->type); + offset = le16_to_cpu(blk->offset); adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", file, blocks, le32_to_cpu(blk->id), @@ -413,21 +777,65 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) reg = 0; region_name = "Unknown"; switch (type) { - case WMFW_NAME_TEXT: - case WMFW_INFO_TEXT: + case (WMFW_NAME_TEXT << 8): + case (WMFW_INFO_TEXT << 8): break; - case WMFW_ABSOLUTE: + case (WMFW_ABSOLUTE << 8): region_name = "register"; reg = offset; break; + + case WMFW_ADSP1_DM: + case WMFW_ADSP1_ZM: + case WMFW_ADSP2_XM: + case WMFW_ADSP2_YM: + adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", + file, blocks, le32_to_cpu(blk->len), + type, le32_to_cpu(blk->id)); + + mem = wm_adsp_find_region(dsp, type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", type); + break; + } + + reg = 0; + list_for_each_entry(alg_region, + &dsp->alg_regions, list) { + if (le32_to_cpu(blk->id) == alg_region->alg && + type == alg_region->type) { + reg = alg_region->base; + reg = wm_adsp_region_to_reg(mem, + reg); + reg += offset; + } + } + + if (reg == 0) + adsp_err(dsp, "No %x for algorithm %x\n", + type, le32_to_cpu(blk->id)); + break; + default: - adsp_err(dsp, "Unknown region type %x\n", type); + adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", + file, blocks, type, pos); break; } if (reg) { - ret = regmap_raw_write(regmap, reg, blk->data, - le32_to_cpu(blk->len)); + buf = wm_adsp_buf_alloc(blk->data, + le32_to_cpu(blk->len), + &buf_list); + if (!buf) { + adsp_err(dsp, "Out of memory\n"); + return -ENOMEM; + } + + adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", + file, blocks, le32_to_cpu(blk->len), + reg); + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(blk->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write to %x in %s\n", @@ -435,21 +843,39 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } } - pos += le32_to_cpu(blk->len) + sizeof(*blk); + tmp = le32_to_cpu(blk->len) % 4; + if (tmp) + pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); + else + pos += le32_to_cpu(blk->len) + sizeof(*blk); + blocks++; } + ret = regmap_async_complete(regmap); + if (ret != 0) + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, blocks, pos - firmware->size); out_fw: release_firmware(firmware); + wm_adsp_buf_free(&buf_list); out: kfree(file); return 0; } +int wm_adsp1_init(struct wm_adsp *adsp) +{ + INIT_LIST_HEAD(&adsp->alg_regions); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp1_init); + int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -458,16 +884,46 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; int ret; + int val; switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, ADSP1_SYS_ENA); + /* + * For simplicity set the DSP clock rate to be the + * SYSCLK rate rather than making it configurable. + */ + if(dsp->sysclk_reg) { + ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); + if (ret != 0) { + adsp_err(dsp, "Failed to read SYSCLK state: %d\n", + ret); + return ret; + } + + val = (val & dsp->sysclk_mask) + >> dsp->sysclk_shift; + + ret = regmap_update_bits(dsp->regmap, + dsp->base + ADSP1_CONTROL_31, + ADSP1_CLK_SEL_MASK, val); + if (ret != 0) { + adsp_err(dsp, "Failed to set clock rate: %d\n", + ret); + return ret; + } + } + ret = wm_adsp_load(dsp); if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -539,6 +995,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_alg_region *alg_region; unsigned int val; int ret; @@ -604,6 +1061,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -614,13 +1075,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; + + dsp->running = true; break; case SND_SOC_DAPM_PRE_PMD: + dsp->running = false; + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); + /* Make sure DMAs are quiesced */ + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); + regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); + if (dsp->dvfs) { ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000); @@ -635,6 +1105,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, "Failed to enable supply: %d\n", ret); } + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } break; default: @@ -664,6 +1142,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) return ret; } + INIT_LIST_HEAD(&adsp->alg_regions); + if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index ffd29a4609e2..cb8871a3ec00 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -25,6 +25,13 @@ struct wm_adsp_region { unsigned int base; }; +struct wm_adsp_alg_region { + struct list_head list; + unsigned int alg; + int type; + unsigned int base; +}; + struct wm_adsp { const char *part; int num; @@ -33,10 +40,18 @@ struct wm_adsp { struct regmap *regmap; int base; + int sysclk_reg; + int sysclk_mask; + int sysclk_shift; + + struct list_head alg_regions; const struct wm_adsp_region *mem; int num_mems; + int fw; + bool running; + struct regulator *dvfs; }; @@ -50,6 +65,9 @@ struct wm_adsp { .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } +extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; + +int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 5632ded67fdd..ef163360a745 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr { struct wmfw_coeff_hdr { u8 magic[4]; __le32 len; - __le32 ver; + union { + __be32 rev; + __le32 ver; + }; + union { + __be32 core; + __le32 core_ver; + }; u8 data[]; } __packed; struct wmfw_coeff_item { - union { - __be32 type; - __le32 offset; - }; + __le16 offset; + __le16 type; __le32 id; __le32 ver; __le32 sr; |