diff options
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/Kconfig | 11 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ak4613.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/cs4271.c | 2 | ||||
-rw-r--r-- | sound/soc/codecs/cs53l30.c | 1 | ||||
-rw-r--r-- | sound/soc/codecs/da7213.c | 13 | ||||
-rw-r--r-- | sound/soc/codecs/dio2125.c | 120 | ||||
-rw-r--r-- | sound/soc/codecs/hdac_hdmi.c | 20 | ||||
-rw-r--r-- | sound/soc/codecs/rt5665.c | 232 | ||||
-rw-r--r-- | sound/soc/codecs/rt5665.h | 4 | ||||
-rw-r--r-- | sound/soc/codecs/rt5670.c | 21 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.c | 333 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.h | 24 |
13 files changed, 647 insertions, 146 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 00885c2cb685..e3e6f39d14e8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -70,6 +70,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C + select SND_SOC_DIO2125 select SND_SOC_DMIC select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C @@ -304,12 +305,14 @@ config SND_SOC_ADAU1761 select SND_SOC_ADAU17X1 config SND_SOC_ADAU1761_I2C - tristate + tristate "Analog Devices AU1761 CODEC - I2C" + depends on I2C select SND_SOC_ADAU1761 select REGMAP_I2C config SND_SOC_ADAU1761_SPI - tristate + tristate "Analog Devices AU1761 CODEC - SPI" + depends on SPI select SND_SOC_ADAU1761 select REGMAP_SPI @@ -521,6 +524,10 @@ config SND_SOC_DA732X config SND_SOC_DA9055 tristate +config SND_SOC_DIO2125 + tristate "Dioo DIO2125 Amplifier" + select GPIOLIB + config SND_SOC_DMIC tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 97b36e4ce665..2c596d84fa3e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -222,6 +222,7 @@ snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o # Amp +snd-soc-dio2125-objs := dio2125.o snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o snd-soc-tpa6130a2-objs := tpa6130a2.o @@ -450,6 +451,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o # Amp +obj-$(CONFIG_SND_SOC_DIO2125) += snd-soc-dio2125.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index e819dd8c82fd..b2dfddead227 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -75,6 +75,12 @@ #define DFS_DOUBLE_SPEED (1 << 2) #define DFS_QUAD_SPEED (2 << 2) +/* ICTRL */ +#define ICTRL_MASK (0x3) + +/* OCTRL */ +#define OCTRL_MASK (0x3F) + struct ak4613_formats { unsigned int width; unsigned int fmt; @@ -365,8 +371,8 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2); - snd_soc_write(codec, ICTRL, priv->ic); - snd_soc_write(codec, OCTRL, priv->oc); + snd_soc_update_bits(codec, ICTRL, ICTRL_MASK, priv->ic); + snd_soc_update_bits(codec, OCTRL, OCTRL_MASK, priv->oc); hw_params_end: if (ret < 0) diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 8c0f3b89b5bc..e78b5f055f25 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -498,7 +498,7 @@ static int cs4271_reset(struct snd_soc_codec *codec) struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); if (gpio_is_valid(cs4271->gpio_nreset)) { - gpio_set_value(cs4271->gpio_nreset, 0); + gpio_direction_output(cs4271->gpio_nreset, 0); mdelay(1); gpio_set_value(cs4271->gpio_nreset, 1); mdelay(1); diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index cb47fb595ff4..1e0d5973b758 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -1130,6 +1130,7 @@ MODULE_DEVICE_TABLE(i2c, cs53l30_id); static struct i2c_driver cs53l30_i2c_driver = { .driver = { .name = "cs53l30", + .of_match_table = cs53l30_of_match, .pm = &cs53l30_runtime_pm, }, .id_table = cs53l30_id, diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 12da55882c06..6dd7578f0bb8 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -12,6 +12,7 @@ * option) any later version. */ +#include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -1528,12 +1529,23 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, return 0; } +#if defined(CONFIG_OF) /* DT */ static const struct of_device_id da7213_of_match[] = { { .compatible = "dlg,da7213", }, { } }; MODULE_DEVICE_TABLE(of, da7213_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id da7213_acpi_match[] = { + { "DLGS7212", 0}, + { "DLGS7213", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, da7213_acpi_match); +#endif static enum da7213_micbias_voltage da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) @@ -1844,6 +1856,7 @@ static struct i2c_driver da7213_i2c_driver = { .driver = { .name = "da7213", .of_match_table = of_match_ptr(da7213_of_match), + .acpi_match_table = ACPI_PTR(da7213_acpi_match), }, .probe = da7213_i2c_probe, .remove = da7213_remove, diff --git a/sound/soc/codecs/dio2125.c b/sound/soc/codecs/dio2125.c new file mode 100644 index 000000000000..09451cd44f9b --- /dev/null +++ b/sound/soc/codecs/dio2125.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + */ + +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <sound/soc.h> + +#define DRV_NAME "dio2125" + +struct dio2125 { + struct gpio_desc *gpiod_enable; +}; + +static int drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct dio2125 *priv = snd_soc_component_get_drvdata(c); + int val; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = 1; + break; + case SND_SOC_DAPM_PRE_PMD: + val = 0; + break; + default: + WARN(1, "Unexpected event"); + return -EINVAL; + } + + gpiod_set_value_cansleep(priv->gpiod_enable, val); + + return 0; +} + +static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("INL"), + SND_SOC_DAPM_INPUT("INR"), + SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event, + (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route dio2125_dapm_routes[] = { + { "DRV", NULL, "INL" }, + { "DRV", NULL, "INR" }, + { "OUTL", NULL, "DRV" }, + { "OUTR", NULL, "DRV" }, +}; + +static const struct snd_soc_component_driver dio2125_component_driver = { + .dapm_widgets = dio2125_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dio2125_dapm_widgets), + .dapm_routes = dio2125_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(dio2125_dapm_routes), +}; + +static int dio2125_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dio2125 *priv; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->gpiod_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(priv->gpiod_enable)) { + err = PTR_ERR(priv->gpiod_enable); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'enable' gpio: %d", err); + return err; + } + + return devm_snd_soc_register_component(dev, &dio2125_component_driver, + NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id dio2125_ids[] = { + { .compatible = "dioo,dio2125", }, + { } +}; +MODULE_DEVICE_TABLE(of, dio2125_ids); +#endif + +static struct platform_driver dio2125_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(dio2125_ids), + }, + .probe = dio2125_probe, +}; + +module_platform_driver(dio2125_driver); + +MODULE_DESCRIPTION("ASoC DIO2125 output driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 78fca8acd3ec..bc2e74ff3b2d 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -469,7 +469,7 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, format = snd_hdac_calc_stream_format(params_rate(hparams), params_channels(hparams), params_format(hparams), - 24, 0); + dai->driver->playback.sig_bits, 0); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); if (!pcm) @@ -1419,8 +1419,8 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac, hdmi_dais[i].playback.rate_min = rate_min; hdmi_dais[i].playback.channels_min = 2; hdmi_dais[i].playback.channels_max = 2; + hdmi_dais[i].playback.sig_bits = bps; hdmi_dais[i].ops = &hdmi_dai_ops; - i++; } @@ -1534,21 +1534,20 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) pin->mst_capable = false; /* if not MST, default is port[0] */ hport = &pin->ports[0]; - goto out; } else { for (i = 0; i < pin->num_ports; i++) { pin->mst_capable = true; if (pin->ports[i].id == pipe) { hport = &pin->ports[i]; - goto out; + break; } } } + + if (hport) + hdac_hdmi_present_sense(pin, hport); } -out: - if (pin && hport) - hdac_hdmi_present_sense(pin, hport); } static struct i915_audio_component_audio_ops aops = { @@ -1998,7 +1997,7 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) struct hdac_hdmi_pin *pin, *pin_next; struct hdac_hdmi_cvt *cvt, *cvt_next; struct hdac_hdmi_pcm *pcm, *pcm_next; - struct hdac_hdmi_port *port; + struct hdac_hdmi_port *port, *port_next; int i; snd_soc_unregister_codec(&edev->hdac.dev); @@ -2008,8 +2007,9 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) if (list_empty(&pcm->port_list)) continue; - list_for_each_entry(port, &pcm->port_list, head) - port = NULL; + list_for_each_entry_safe(port, port_next, + &pcm->port_list, head) + list_del(&port->head); list_del(&pcm->head); kfree(pcm); diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 324461e985b3..8cd22307f5b6 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -1139,7 +1139,8 @@ static void rt5665_enable_push_button_irq(struct snd_soc_codec *codec, bool enable) { if (enable) { - snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x000b); + snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x0003); + snd_soc_update_bits(codec, RT5665_SAR_IL_CMD_9, 0x1, 0x1); snd_soc_write(codec, RT5665_IL_CMD_1, 0x0048); snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2, RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK, @@ -1192,10 +1193,13 @@ static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert) } regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, - 0x180, 0x180); + 0x1a0, 0x120); regmap_write(rt5665->regmap, RT5665_EJD_CTRL_3, 0x3424); + regmap_write(rt5665->regmap, RT5665_IL_CMD_1, 0x0048); regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0xa291); + usleep_range(10000, 15000); + rt5665->sar_adc_value = snd_soc_read(rt5665->codec, RT5665_SAR_IL_CMD_4) & 0x7ff; @@ -1241,7 +1245,7 @@ static irqreturn_t rt5665_irq(int irq, void *data) static void rt5665_jd_check_handler(struct work_struct *work) { struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv, - calibrate_work.work); + jd_check_work.work); if (snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010) { /* jack out */ @@ -1256,8 +1260,8 @@ static void rt5665_jd_check_handler(struct work_struct *work) } } -int rt5665_set_jack_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *hs_jack) +static int rt5665_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hs_jack, void *data) { struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); @@ -1284,7 +1288,6 @@ int rt5665_set_jack_detect(struct snd_soc_codec *codec, return 0; } -EXPORT_SYMBOL_GPL(rt5665_set_jack_detect); static void rt5665_jack_detect_handler(struct work_struct *work) { @@ -2252,7 +2255,7 @@ static const char * const rt5665_if2_1_adc_in_src[] = { static const SOC_ENUM_SINGLE_DECL( rt5665_if2_1_adc_in_enum, RT5665_DIG_INF2_DATA, - RT5665_IF3_ADC_IN_SFT, rt5665_if2_1_adc_in_src); + RT5665_IF2_1_ADC_IN_SFT, rt5665_if2_1_adc_in_src); static const struct snd_kcontrol_new rt5665_if2_1_adc_in_mux = SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_1_adc_in_enum); @@ -2600,6 +2603,55 @@ static int rt5655_set_verf(struct snd_soc_dapm_widget *w, return 0; } +static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int val1, val2, mask1, mask2 = 0; + + switch (w->shift) { + case RT5665_PWR_I2S2_1_BIT: + mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK | + RT5665_GP4_PIN_MASK | RT5665_GP5_PIN_MASK; + val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 | + RT5665_GP4_PIN_DACDAT2_1 | RT5665_GP5_PIN_ADCDAT2_1; + break; + case RT5665_PWR_I2S2_2_BIT: + mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK | + RT5665_GP8_PIN_MASK; + val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 | + RT5665_GP8_PIN_DACDAT2_2; + mask2 = RT5665_GP9_PIN_MASK; + val2 = RT5665_GP9_PIN_ADCDAT2_2; + break; + case RT5665_PWR_I2S3_BIT: + mask1 = RT5665_GP6_PIN_MASK | RT5665_GP7_PIN_MASK | + RT5665_GP8_PIN_MASK; + val1 = RT5665_GP6_PIN_BCLK3 | RT5665_GP7_PIN_LRCK3 | + RT5665_GP8_PIN_DACDAT3; + mask2 = RT5665_GP9_PIN_MASK; + val2 = RT5665_GP9_PIN_ADCDAT3; + break; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, val1); + if (mask2) + snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2, + mask2, val2); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, 0); + if (mask2) + snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2, + mask2, 0); + break; + default: + return 0; + } + + return 0; +} static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("LDO2", RT5665_PWR_ANLG_3, RT5665_PWR_LDO2_BIT, 0, @@ -2852,11 +2904,14 @@ static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("I2S1_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_2_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("I2S2_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_1_BIT, - 0, NULL, 0), + 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("I2S2_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_2_BIT, - 0, NULL, 0), + 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("I2S3", RT5665_PWR_DIG_1, RT5665_PWR_I2S3_BIT, - 0, NULL, 0), + 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -3178,6 +3233,9 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = { {"DAC Mono Right Filter", NULL, "DAC Mono R ASRC", is_using_asrc}, {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, {"DAC Stereo2 Filter", NULL, "DAC STO2 ASRC", is_using_asrc}, + {"I2S1 ASRC", NULL, "CLKDET"}, + {"I2S2 ASRC", NULL, "CLKDET"}, + {"I2S3 ASRC", NULL, "CLKDET"}, /*Vref*/ {"Mic Det Power", NULL, "Vref2"}, @@ -3912,6 +3970,7 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = { {"Mono MIX", "MONOVOL Switch", "MONOVOL"}, {"Mono Amp", NULL, "Mono MIX"}, {"Mono Amp", NULL, "Vref2"}, + {"Mono Amp", NULL, "Vref3"}, {"Mono Amp", NULL, "CLKDET SYS"}, {"Mono Amp", NULL, "CLKDET MONO"}, {"Mono Playback", "Switch", "Mono Amp"}, @@ -3959,12 +4018,68 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = { {"PDMR", NULL, "PDM R Playback"}, }; +static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= RT5665_I2S1_MODE_TDM; + + switch (slots) { + case 4: + val |= RT5665_TDM_IN_CH_4; + val |= RT5665_TDM_OUT_CH_4; + break; + case 6: + val |= RT5665_TDM_IN_CH_6; + val |= RT5665_TDM_OUT_CH_6; + break; + case 8: + val |= RT5665_TDM_IN_CH_8; + val |= RT5665_TDM_OUT_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= RT5665_TDM_IN_LEN_20; + val |= RT5665_TDM_OUT_LEN_20; + break; + case 24: + val |= RT5665_TDM_IN_LEN_24; + val |= RT5665_TDM_OUT_LEN_24; + break; + case 32: + val |= RT5665_TDM_IN_LEN_32; + val |= RT5665_TDM_OUT_LEN_32; + break; + case 16: + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, RT5665_TDM_CTRL_1, + RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK | + RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK | + RT5665_TDM_OUT_LEN_MASK, val); + + return 0; +} + + static int rt5665_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 rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); - unsigned int val_len = 0, val_clk, mask_clk, val_bits = 0x0100; + unsigned int val_len = 0, val_clk, reg_clk, mask_clk, val_bits = 0x0100; int pre_div, frame_size; rt5665->lrck[dai->id] = params_rate(params); @@ -4005,6 +4120,10 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream, switch (dai->id) { case RT5665_AIF1_1: case RT5665_AIF1_2: + if (params_channels(params) > 2) + rt5665_set_tdm_slot(dai, 0xf, 0xf, + params_channels(params), params_width(params)); + reg_clk = RT5665_ADDA_CLK_1; mask_clk = RT5665_I2S_PD1_MASK; val_clk = pre_div << RT5665_I2S_PD1_SFT; snd_soc_update_bits(codec, RT5665_I2S1_SDP, @@ -4012,12 +4131,14 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream, break; case RT5665_AIF2_1: case RT5665_AIF2_2: + reg_clk = RT5665_ADDA_CLK_2; mask_clk = RT5665_I2S_PD2_MASK; val_clk = pre_div << RT5665_I2S_PD2_SFT; snd_soc_update_bits(codec, RT5665_I2S2_SDP, RT5665_I2S_DL_MASK, val_len); break; case RT5665_AIF3: + reg_clk = RT5665_ADDA_CLK_2; mask_clk = RT5665_I2S_PD3_MASK; val_clk = pre_div << RT5665_I2S_PD3_SFT; snd_soc_update_bits(codec, RT5665_I2S3_SDP, @@ -4028,7 +4149,7 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, mask_clk, val_clk); + snd_soc_update_bits(codec, reg_clk, mask_clk, val_clk); snd_soc_update_bits(codec, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits); switch (rt5665->lrck[dai->id]) { @@ -4121,10 +4242,9 @@ static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) +static int rt5665_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) { - struct snd_soc_codec *codec = dai->codec; struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); unsigned int reg_val = 0; @@ -4150,20 +4270,20 @@ static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai, rt5665->sysclk = freq; rt5665->sysclk_src = clk_id; - dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); return 0; } -static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, - unsigned int freq_in, unsigned int freq_out) +static int rt5665_set_codec_pll(struct snd_soc_codec *codec, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) { - struct snd_soc_codec *codec = dai->codec; struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); struct rl6231_pll_code pll_code; int ret; - if (Source == rt5665->pll_src && freq_in == rt5665->pll_in && + if (source == rt5665->pll_src && freq_in == rt5665->pll_in && freq_out == rt5665->pll_out) return 0; @@ -4177,7 +4297,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, return 0; } - switch (Source) { + switch (source) { case RT5665_PLL1_S_MCLK: snd_soc_update_bits(codec, RT5665_GLB_CLK, RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK); @@ -4195,7 +4315,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3); break; default: - dev_err(codec->dev, "Unknown PLL Source %d\n", Source); + dev_err(codec->dev, "Unknown PLL Source %d\n", source); return -EINVAL; } @@ -4217,62 +4337,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, rt5665->pll_in = freq_in; rt5665->pll_out = freq_out; - rt5665->pll_src = Source; - - return 0; -} - -static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, - unsigned int rx_mask, int slots, int slot_width) -{ - struct snd_soc_codec *codec = dai->codec; - unsigned int val = 0; - - if (rx_mask || tx_mask) - val |= RT5665_I2S1_MODE_TDM; - - switch (slots) { - case 4: - val |= RT5665_TDM_IN_CH_4; - val |= RT5665_TDM_OUT_CH_4; - break; - case 6: - val |= RT5665_TDM_IN_CH_6; - val |= RT5665_TDM_OUT_CH_6; - break; - case 8: - val |= RT5665_TDM_IN_CH_8; - val |= RT5665_TDM_OUT_CH_8; - break; - case 2: - break; - default: - return -EINVAL; - } - - switch (slot_width) { - case 20: - val |= RT5665_TDM_IN_LEN_20; - val |= RT5665_TDM_OUT_LEN_20; - break; - case 24: - val |= RT5665_TDM_IN_LEN_24; - val |= RT5665_TDM_OUT_LEN_24; - break; - case 32: - val |= RT5665_TDM_IN_LEN_32; - val |= RT5665_TDM_OUT_LEN_32; - break; - case 16: - break; - default: - return -EINVAL; - } - - snd_soc_update_bits(codec, RT5665_TDM_CTRL_1, - RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK | - RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK | - RT5665_TDM_OUT_LEN_MASK, val); + rt5665->pll_src = source; return 0; } @@ -4389,9 +4454,7 @@ static int rt5665_resume(struct snd_soc_codec *codec) static const struct snd_soc_dai_ops rt5665_aif_dai_ops = { .hw_params = rt5665_hw_params, .set_fmt = rt5665_set_dai_fmt, - .set_sysclk = rt5665_set_dai_sysclk, .set_tdm_slot = rt5665_set_tdm_slot, - .set_pll = rt5665_set_dai_pll, .set_bclk_ratio = rt5665_set_bclk_ratio, }; @@ -4500,7 +4563,10 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5665 = { .num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets), .dapm_routes = rt5665_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes), - } + }, + .set_sysclk = rt5665_set_codec_sysclk, + .set_pll = rt5665_set_codec_pll, + .set_jack = rt5665_set_jack_detect, }; @@ -4779,7 +4845,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c, regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0002); regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, - 0xf000 | RT5665_VREF_POW_MASK, 0xd000 | RT5665_VREF_POW_REG); + 0xf000 | RT5665_VREF_POW_MASK, 0xe000 | RT5665_VREF_POW_REG); /* Work around for pow_pump */ regmap_update_bits(rt5665->regmap, RT5665_STO1_DAC_SIL_DET, RT5665_DEB_STO_DAC_MASK, RT5665_DEB_80_MS); @@ -4798,7 +4864,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c, /* Enhance performance*/ regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1, RT5665_HP_DRIVER_MASK | RT5665_LDO1_DVO_MASK, - RT5665_HP_DRIVER_5X | RT5665_LDO1_DVO_09); + RT5665_HP_DRIVER_5X | RT5665_LDO1_DVO_12); INIT_DELAYED_WORK(&rt5665->jack_detect_work, rt5665_jack_detect_handler); diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h index 12f7080a0d3c..1db5c6a62a8e 100644 --- a/sound/soc/codecs/rt5665.h +++ b/sound/soc/codecs/rt5665.h @@ -1106,7 +1106,7 @@ #define RT5665_HP_DRIVER_MASK (0x3 << 2) #define RT5665_HP_DRIVER_1X (0x0 << 2) #define RT5665_HP_DRIVER_3X (0x1 << 2) -#define RT5665_HP_DRIVER_5X (0x2 << 2) +#define RT5665_HP_DRIVER_5X (0x3 << 2) #define RT5665_LDO1_DVO_MASK (0x3) #define RT5665_LDO1_DVO_09 (0x0) #define RT5665_LDO1_DVO_10 (0x1) @@ -1984,7 +1984,5 @@ enum { int rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec, unsigned int filter_mask, unsigned int clk_src); -int rt5665_set_jack_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *hs_jack); #endif /* __RT5665_H__ */ diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 17d20b99f041..e27c5a4a0a15 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2835,6 +2835,27 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"), }, }, + { + .ident = "Lenovo Thinkpad Tablet 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), + }, + }, + { + .ident = "Lenovo Thinkpad Tablet 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), + }, + }, + { + .ident = "Lenovo Thinkpad Tablet 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), + }, + }, {} }; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d151224ffcca..20695b691aff 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -112,17 +112,22 @@ #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 - -#define ADSP2_SCRATCH0 0x40 -#define ADSP2_SCRATCH1 0x41 -#define ADSP2_SCRATCH2 0x42 -#define ADSP2_SCRATCH3 0x43 +#define ADSP2_CONTROL 0x0 +#define ADSP2_CLOCKING 0x1 +#define ADSP2V2_CLOCKING 0x2 +#define ADSP2_STATUS1 0x4 +#define ADSP2_WDMA_CONFIG_1 0x30 +#define ADSP2_WDMA_CONFIG_2 0x31 +#define ADSP2V2_WDMA_CONFIG_2 0x32 +#define ADSP2_RDMA_CONFIG_1 0x34 + +#define ADSP2_SCRATCH0 0x40 +#define ADSP2_SCRATCH1 0x41 +#define ADSP2_SCRATCH2 0x42 +#define ADSP2_SCRATCH3 0x43 + +#define ADSP2V2_SCRATCH0_1 0x40 +#define ADSP2V2_SCRATCH2_3 0x42 /* * ADSP2 Control @@ -153,6 +158,17 @@ #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ /* + * ADSP2V2 clocking + */ +#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ +#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ +#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ + +#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ +#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ +#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ + +/* * ADSP2 Status 1 */ #define ADSP2_RAM_RDY 0x0001 @@ -160,6 +176,37 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +/* + * ADSP2 Lock support + */ +#define ADSP2_LOCK_CODE_0 0x5555 +#define ADSP2_LOCK_CODE_1 0xAAAA + +#define ADSP2_WATCHDOG 0x0A +#define ADSP2_BUS_ERR_ADDR 0x52 +#define ADSP2_REGION_LOCK_STATUS 0x64 +#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 +#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 +#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A +#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C +#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E +#define ADSP2_LOCK_REGION_CTRL 0x7A +#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C + +#define ADSP2_REGION_LOCK_ERR_MASK 0x8000 +#define ADSP2_SLAVE_ERR_MASK 0x4000 +#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 +#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 +#define ADSP2_CTRL_ERR_EINT 0x0001 + +#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF +#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF +#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 +#define ADSP2_PMEM_ERR_ADDR_SHIFT 16 +#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD + +#define ADSP2_LOCK_REGION_SHIFT 16 + #define ADSP_MAX_STD_CTRL_SIZE 512 #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 @@ -683,6 +730,9 @@ static const struct soc_enum wm_adsp_fw_enum[] = { 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), + SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), }; const struct snd_kcontrol_new wm_adsp_fw_controls[] = { @@ -694,6 +744,12 @@ const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 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), + SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6], + wm_adsp_fw_get, wm_adsp_fw_put), }; EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); @@ -750,6 +806,29 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) be16_to_cpu(scratch[3])); } +static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) +{ + u32 scratch[2]; + int ret; + + ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1, + scratch, sizeof(scratch)); + + if (ret) { + adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); + return; + } + + scratch[0] = be32_to_cpu(scratch[0]); + scratch[1] = be32_to_cpu(scratch[1]); + + adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", + scratch[0] & 0xFFFF, + scratch[0] >> 16, + scratch[1] & 0xFFFF, + scratch[1] >> 16); +} + static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) { return container_of(ext, struct wm_coeff_ctl, bytes_ext); @@ -899,7 +978,10 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, mutex_lock(&ctl->dsp->pwr_lock); - memcpy(ctl->cache, p, ctl->len); + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + ret = -EPERM; + else + memcpy(ctl->cache, p, ctl->len); ctl->set = 1; if (ctl->enabled && ctl->dsp->running) @@ -926,6 +1008,8 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, ctl->set = 1; if (ctl->enabled && ctl->dsp->running) ret = wm_coeff_write_control(ctl, ctl->cache, size); + else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + ret = -EPERM; } mutex_unlock(&ctl->dsp->pwr_lock); @@ -947,7 +1031,7 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, mutex_lock(&ctl->dsp->pwr_lock); - if (ctl->enabled) + if (ctl->enabled && ctl->dsp->running) ret = wm_coeff_write_acked_control(ctl, val); else ret = -EPERM; @@ -2430,10 +2514,17 @@ static int wm_adsp2_ena(struct wm_adsp *dsp) unsigned int val; int ret, count; - ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_SYS_ENA, ADSP2_SYS_ENA); - if (ret != 0) - return ret; + switch (dsp->rev) { + case 0: + ret = regmap_update_bits_async(dsp->regmap, + dsp->base + ADSP2_CONTROL, + ADSP2_SYS_ENA, ADSP2_SYS_ENA); + if (ret != 0) + return ret; + break; + default: + break; + } /* Wait for the RAM to start, should be near instantaneous */ for (count = 0; count < 10; ++count) { @@ -2492,11 +2583,17 @@ static void wm_adsp2_boot_work(struct work_struct *work) if (ret != 0) goto err_ena; - /* Turn DSP back off until we are ready to run */ - ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_SYS_ENA, 0); - if (ret != 0) - goto err_ena; + switch (dsp->rev) { + case 0: + /* Turn DSP back off until we are ready to run */ + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_SYS_ENA, 0); + if (ret != 0) + goto err_ena; + break; + default: + break; + } dsp->booted = true; @@ -2518,12 +2615,21 @@ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) { int ret; - ret = regmap_update_bits_async(dsp->regmap, - dsp->base + ADSP2_CLOCKING, - ADSP2_CLK_SEL_MASK, - freq << ADSP2_CLK_SEL_SHIFT); - if (ret != 0) - adsp_err(dsp, "Failed to set clock rate: %d\n", ret); + switch (dsp->rev) { + case 0: + ret = regmap_update_bits_async(dsp->regmap, + dsp->base + ADSP2_CLOCKING, + ADSP2_CLK_SEL_MASK, + freq << ADSP2_CLK_SEL_SHIFT); + if (ret) { + adsp_err(dsp, "Failed to set clock rate: %d\n", ret); + return; + } + break; + default: + /* clock is handled by parent codec driver */ + break; + } } int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, @@ -2563,6 +2669,18 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); +static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) +{ + switch (dsp->rev) { + case 0: + case 1: + return; + default: + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, + ADSP2_WDT_ENA_MASK, 0); + } +} + int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event, unsigned int freq) @@ -2635,6 +2753,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + wm_adsp2_lock(dsp, dsp->lock_regions); + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_CORE_ENA | ADSP2_START, @@ -2658,23 +2778,49 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, /* Tell the firmware to cleanup */ wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); + wm_adsp_stop_watchdog(dsp); + /* Log firmware state, it can be useful for analysis */ - wm_adsp2_show_fw_status(dsp); + switch (dsp->rev) { + case 0: + wm_adsp2_show_fw_status(dsp); + break; + default: + wm_adsp2v2_show_fw_status(dsp); + break; + } mutex_lock(&dsp->pwr_lock); dsp->running = false; - regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + regmap_update_bits(dsp->regmap, + dsp->base + ADSP2_CONTROL, ADSP2_CORE_ENA | ADSP2_START, 0); /* Make sure DMAs are quiesced */ - regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); - regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); - regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); - - regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_SYS_ENA, 0); + switch (dsp->rev) { + case 0: + regmap_write(dsp->regmap, + dsp->base + ADSP2_RDMA_CONFIG_1, 0); + regmap_write(dsp->regmap, + dsp->base + ADSP2_WDMA_CONFIG_1, 0); + regmap_write(dsp->regmap, + dsp->base + ADSP2_WDMA_CONFIG_2, 0); + + regmap_update_bits(dsp->regmap, + dsp->base + ADSP2_CONTROL, + ADSP2_SYS_ENA, 0); + break; + default: + regmap_write(dsp->regmap, + dsp->base + ADSP2_RDMA_CONFIG_1, 0); + regmap_write(dsp->regmap, + dsp->base + ADSP2_WDMA_CONFIG_1, 0); + regmap_write(dsp->regmap, + dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); + break; + } if (wm_adsp_fw[dsp->fw].num_caps != 0) wm_adsp_buffer_free(dsp); @@ -2727,15 +2873,22 @@ int wm_adsp2_init(struct wm_adsp *dsp) { int ret; - /* - * Disable the DSP memory by default when in reset for a small - * power saving. - */ - ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_MEM_ENA, 0); - if (ret != 0) { - adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); - return ret; + switch (dsp->rev) { + case 0: + /* + * Disable the DSP memory by default when in reset for a small + * power saving. + */ + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_MEM_ENA, 0); + if (ret) { + adsp_err(dsp, + "Failed to clear memory retention: %d\n", ret); + return ret; + } + break; + default: + break; } INIT_LIST_HEAD(&dsp->alg_regions); @@ -3518,4 +3671,94 @@ int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, } EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); +int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) +{ + struct regmap *regmap = dsp->regmap; + unsigned int code0, code1, lock_reg; + + if (!(lock_regions & WM_ADSP2_REGION_ALL)) + return 0; + + lock_regions &= WM_ADSP2_REGION_ALL; + lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; + + while (lock_regions) { + code0 = code1 = 0; + if (lock_regions & BIT(0)) { + code0 = ADSP2_LOCK_CODE_0; + code1 = ADSP2_LOCK_CODE_1; + } + if (lock_regions & BIT(1)) { + code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; + code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; + } + regmap_write(regmap, lock_reg, code0); + regmap_write(regmap, lock_reg, code1); + lock_regions >>= 2; + lock_reg += 2; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_lock); + +irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp) +{ + unsigned int val; + struct regmap *regmap = dsp->regmap; + int ret = 0; + + ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); + if (ret) { + adsp_err(dsp, + "Failed to read Region Lock Ctrl register: %d\n", ret); + return IRQ_HANDLED; + } + + if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { + adsp_err(dsp, "watchdog timeout error\n"); + wm_adsp_stop_watchdog(dsp); + } + + if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { + if (val & ADSP2_SLAVE_ERR_MASK) + adsp_err(dsp, "bus error: slave error\n"); + else + adsp_err(dsp, "bus error: region lock error\n"); + + ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); + if (ret) { + adsp_err(dsp, + "Failed to read Bus Err Addr register: %d\n", + ret); + return IRQ_HANDLED; + } + + adsp_err(dsp, "bus error address = 0x%x\n", + val & ADSP2_BUS_ERR_ADDR_MASK); + + ret = regmap_read(regmap, + dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, + &val); + if (ret) { + adsp_err(dsp, + "Failed to read Pmem Xmem Err Addr register: %d\n", + ret); + return IRQ_HANDLED; + } + + adsp_err(dsp, "xmem error address = 0x%x\n", + val & ADSP2_XMEM_ERR_ADDR_MASK); + adsp_err(dsp, "pmem error address = 0x%x\n", + (val & ADSP2_PMEM_ERR_ADDR_MASK) >> + ADSP2_PMEM_ERR_ADDR_SHIFT); + } + + regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, + ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); + MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 3706b11053a3..41cc11c19b83 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -23,6 +23,23 @@ #define WM_ADSP_COMPR_OK 0 #define WM_ADSP_COMPR_VOICE_TRIGGER 1 +#define WM_ADSP2_REGION_0 BIT(0) +#define WM_ADSP2_REGION_1 BIT(1) +#define WM_ADSP2_REGION_2 BIT(2) +#define WM_ADSP2_REGION_3 BIT(3) +#define WM_ADSP2_REGION_4 BIT(4) +#define WM_ADSP2_REGION_5 BIT(5) +#define WM_ADSP2_REGION_6 BIT(6) +#define WM_ADSP2_REGION_7 BIT(7) +#define WM_ADSP2_REGION_8 BIT(8) +#define WM_ADSP2_REGION_9 BIT(9) +#define WM_ADSP2_REGION_1_9 (WM_ADSP2_REGION_1 | \ + WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3 | \ + WM_ADSP2_REGION_4 | WM_ADSP2_REGION_5 | \ + WM_ADSP2_REGION_6 | WM_ADSP2_REGION_7 | \ + WM_ADSP2_REGION_8 | WM_ADSP2_REGION_9) +#define WM_ADSP2_REGION_ALL (WM_ADSP2_REGION_0 | WM_ADSP2_REGION_1_9) + struct wm_adsp_region { int type; unsigned int base; @@ -40,6 +57,7 @@ struct wm_adsp_compr_buf; struct wm_adsp { const char *part; + int rev; int num; int type; struct device *dev; @@ -75,6 +93,8 @@ struct wm_adsp { struct mutex pwr_lock; + unsigned int lock_regions; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root; char *wmfw_file_name; @@ -113,6 +133,10 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event, unsigned int freq); + +int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions); +irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp); + int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); |