diff options
Diffstat (limited to 'sound/soc')
57 files changed, 1554 insertions, 309 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7ea66ee3653f..182d92efc7c8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -6,7 +6,7 @@ menuconfig SND_SOC tristate "ALSA for SoC audio support" select SND_PCM select AC97_BUS if SND_SOC_AC97_BUS - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER ---help--- diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 649e92a252ae..26ae0b5d3e16 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -94,6 +94,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM3008 select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_SPI if SPI_MASTER + select SND_SOC_PCM5102A select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C @@ -575,6 +576,9 @@ config SND_SOC_PCM3168A_SPI select SND_SOC_PCM3168A select REGMAP_SPI +config SND_SOC_PCM5102A + tristate + config SND_SOC_PCM512x tristate @@ -629,6 +633,7 @@ config SND_SOC_RT5514 config SND_SOC_RT5616 tristate "Realtek RT5616 CODEC" + depends on I2C config SND_SOC_RT5631 tristate "Realtek ALC5631/RT5631 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 185a712a7fe7..4532a743b5f8 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -89,6 +89,7 @@ snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o +snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o @@ -298,6 +299,7 @@ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o +obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 92d22a018d68..83959312f7a0 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -249,6 +249,18 @@ int arizona_init_spk(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(arizona_init_spk); +int arizona_free_spk(struct snd_soc_codec *codec) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + + arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona); + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_free_spk); + static const struct snd_soc_dapm_route arizona_mono_routes[] = { { "OUT1R", NULL, "OUT1L" }, { "OUT2R", NULL, "OUT2L" }, diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 1ea8e4ecf8d4..ce0531b8c632 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -307,6 +307,8 @@ extern int arizona_init_spk(struct snd_soc_codec *codec); extern int arizona_init_gpio(struct snd_soc_codec *codec); extern int arizona_init_mono(struct snd_soc_codec *codec); +extern int arizona_free_spk(struct snd_soc_codec *codec); + extern int arizona_init_dai(struct arizona_priv *priv, int dai); int arizona_set_output_mode(struct snd_soc_codec *codec, int output, diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 44c30fe3e315..287d13740be4 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -274,7 +274,9 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client, if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0) pdata->sdout_share = val; - of_property_read_u32(np, "cirrus,boost-manager", &val); + if (of_property_read_u32(np, "cirrus,boost-manager", &val)) + val = -1u; + switch (val) { case CS35L32_BOOST_MGR_AUTO: case CS35L32_BOOST_MGR_AUTO_AUDIO: @@ -282,13 +284,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client, case CS35L32_BOOST_MGR_FIXED: pdata->boost_mng = val; break; + case -1u: default: dev_err(&i2c_client->dev, "Wrong cirrus,boost-manager DT value %d\n", val); pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS; } - of_property_read_u32(np, "cirrus,sdout-datacfg", &val); + if (of_property_read_u32(np, "cirrus,sdout-datacfg", &val)) + val = -1u; switch (val) { case CS35L32_DATA_CFG_LR_VP: case CS35L32_DATA_CFG_LR_STAT: @@ -296,13 +300,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client, case CS35L32_DATA_CFG_LR_VPSTAT: pdata->sdout_datacfg = val; break; + case -1u: default: dev_err(&i2c_client->dev, "Wrong cirrus,sdout-datacfg DT value %d\n", val); pdata->sdout_datacfg = CS35L32_DATA_CFG_LR; } - of_property_read_u32(np, "cirrus,battery-threshold", &val); + if (of_property_read_u32(np, "cirrus,battery-threshold", &val)) + val = -1u; switch (val) { case CS35L32_BATT_THRESH_3_1V: case CS35L32_BATT_THRESH_3_2V: @@ -310,13 +316,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client, case CS35L32_BATT_THRESH_3_4V: pdata->batt_thresh = val; break; + case -1u: default: dev_err(&i2c_client->dev, "Wrong cirrus,battery-threshold DT value %d\n", val); pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V; } - of_property_read_u32(np, "cirrus,battery-recovery", &val); + if (of_property_read_u32(np, "cirrus,battery-recovery", &val)) + val = -1u; switch (val) { case CS35L32_BATT_RECOV_3_1V: case CS35L32_BATT_RECOV_3_2V: @@ -326,6 +334,7 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client, case CS35L32_BATT_RECOV_3_6V: pdata->batt_recov = val; break; + case -1u: default: dev_err(&i2c_client->dev, "Wrong cirrus,battery-recovery DT value %d\n", val); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index e770ee6f36da..0c0010b25421 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -26,6 +26,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> #include <sound/pcm.h> #include <sound/soc.h> #include <sound/tlv.h> @@ -157,6 +158,10 @@ static bool cs4271_volatile_reg(struct device *dev, unsigned int reg) return reg == CS4271_CHIPID; } +static const char * const supply_names[] = { + "vd", "vl", "va" +}; + struct cs4271_private { unsigned int mclk; bool master; @@ -170,6 +175,7 @@ struct cs4271_private { int gpio_disable; /* enable soft reset workaround */ bool enable_soft_reset; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = { @@ -487,6 +493,20 @@ static struct snd_soc_dai_driver cs4271_dai = { .symmetric_rates = 1, }; +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); + mdelay(1); + gpio_set_value(cs4271->gpio_nreset, 1); + mdelay(1); + } + + return 0; +} + #ifdef CONFIG_PM static int cs4271_soc_suspend(struct snd_soc_codec *codec) { @@ -499,6 +519,9 @@ static int cs4271_soc_suspend(struct snd_soc_codec *codec) if (ret < 0) return ret; + regcache_mark_dirty(cs4271->regmap); + regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies); + return 0; } @@ -507,6 +530,16 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec) int ret; struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies), + cs4271->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + /* Do a proper reset after power up */ + cs4271_reset(codec); + /* Restore codec state */ ret = regcache_sync(cs4271->regmap); if (ret < 0) @@ -553,19 +586,24 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec) } #endif + ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies), + cs4271->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + if (cs4271plat) { amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; } - if (gpio_is_valid(cs4271->gpio_nreset)) { - /* Reset codec */ - gpio_direction_output(cs4271->gpio_nreset, 0); - mdelay(1); - gpio_set_value(cs4271->gpio_nreset, 1); - /* Give the codec time to wake up */ - mdelay(1); - } + /* Reset codec */ + cs4271_reset(codec); + + ret = regcache_sync(cs4271->regmap); + if (ret < 0) + return ret; ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, CS4271_MODE2_PDN | CS4271_MODE2_CPEN, @@ -595,6 +633,9 @@ static int cs4271_codec_remove(struct snd_soc_codec *codec) /* Set codec to the reset state */ gpio_set_value(cs4271->gpio_nreset, 0); + regcache_mark_dirty(cs4271->regmap); + regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies); + return 0; }; @@ -617,6 +658,7 @@ static int cs4271_common_probe(struct device *dev, { struct cs4271_platform_data *cs4271plat = dev->platform_data; struct cs4271_private *cs4271; + int i, ret; cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL); if (!cs4271) @@ -638,6 +680,17 @@ static int cs4271_common_probe(struct device *dev, return ret; } + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + cs4271->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs4271->supplies), + cs4271->supplies); + + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + *c = cs4271; return 0; } diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 576087bda330..00e9b6fc1b5c 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1108,6 +1108,9 @@ static int cs47l24_codec_remove(struct snd_soc_codec *codec) priv->core.arizona->dapm = NULL; arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); + + arizona_free_spk(codec); + return 0; } diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 26f9459cb3bc..13002f33384e 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1378,10 +1378,18 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(&codec->component); struct hdac_hdmi_pin *pin; + struct hdac_ext_link *hlink = NULL; int ret; edev->scodec = codec; + /* + * hold the ref while we probe, also no need to drop the ref on + * exit, we call pm_runtime_suspend() so that will do for us + */ + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + snd_hdac_ext_bus_link_get(edev->ebus, hlink); + ret = create_fill_widget_route_map(dapm); if (ret < 0) return ret; @@ -1420,32 +1428,39 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec) } #ifdef CONFIG_PM -static int hdmi_codec_resume(struct snd_soc_codec *codec) +static int hdmi_codec_prepare(struct device *dev) { - struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + struct hdac_ext_device *edev = to_hda_ext_device(dev); + struct hdac_device *hdac = &edev->hdac; + + pm_runtime_get_sync(&edev->hdac.dev); + + /* + * Power down afg. + * codec_read is preferred over codec_write to set the power state. + * This way verb is send to set the power state and response + * is received. So setting power state is ensured without using loop + * to read the state. + */ + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + + return 0; +} + +static void hdmi_codec_complete(struct device *dev) +{ + struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_pin *pin; struct hdac_device *hdac = &edev->hdac; - struct hdac_bus *bus = hdac->bus; - int err; - unsigned long timeout; - - hdac_hdmi_skl_enable_all_pins(&edev->hdac); - hdac_hdmi_skl_enable_dp12(&edev->hdac); /* Power up afg */ - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) { - - snd_hdac_codec_write(hdac, hdac->afg, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); - /* Wait till power state is set to D0 */ - timeout = jiffies + msecs_to_jiffies(1000); - while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0) - && time_before(jiffies, timeout)) { - msleep(50); - } - } + hdac_hdmi_skl_enable_all_pins(&edev->hdac); + hdac_hdmi_skl_enable_dp12(&edev->hdac); /* * As the ELD notify callback request is not entertained while the @@ -1455,28 +1470,16 @@ static int hdmi_codec_resume(struct snd_soc_codec *codec) list_for_each_entry(pin, &hdmi->pin_list, head) hdac_hdmi_present_sense(pin, 1); - /* - * Codec power is turned ON during controller resume. - * Turn it OFF here - */ - err = snd_hdac_display_power(bus, false); - if (err < 0) { - dev_err(bus->dev, - "Cannot turn OFF display power on i915, err: %d\n", - err); - return err; - } - - return 0; + pm_runtime_put_sync(&edev->hdac.dev); } #else -#define hdmi_codec_resume NULL +#define hdmi_codec_prepare NULL +#define hdmi_codec_complete NULL #endif static struct snd_soc_codec_driver hdmi_hda_codec = { .probe = hdmi_codec_probe, .remove = hdmi_codec_remove, - .resume = hdmi_codec_resume, .idle_bias_off = true, }; @@ -1485,9 +1488,14 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) struct hdac_device *codec = &edev->hdac; struct hdac_hdmi_priv *hdmi_priv; struct snd_soc_dai_driver *hdmi_dais = NULL; + struct hdac_ext_link *hlink = NULL; int num_dais = 0; int ret = 0; + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + snd_hdac_ext_bus_link_get(edev->ebus, hlink); + hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) return -ENOMEM; @@ -1521,8 +1529,12 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) } /* ASoC specific initialization */ - return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, - hdmi_dais, num_dais); + ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, + hdmi_dais, num_dais); + + snd_hdac_ext_bus_link_put(edev->ebus, hlink); + + return ret; } static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) @@ -1562,6 +1574,8 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; unsigned long timeout; + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + struct hdac_ext_link *hlink = NULL; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -1570,26 +1584,24 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) if (!bus) return 0; - /* Power down afg */ - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) { - snd_hdac_codec_write(hdac, hdac->afg, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - /* Wait till power state is set to D3 */ - timeout = jiffies + msecs_to_jiffies(1000); - while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3) - && time_before(jiffies, timeout)) { - - msleep(50); - } - } - + /* + * Power down afg. + * codec_read is preferred over codec_write to set the power state. + * This way verb is send to set the power state and response + * is received. So setting power state is ensured without using loop + * to read the state. + */ + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); err = snd_hdac_display_power(bus, false); if (err < 0) { dev_err(bus->dev, "Cannot turn on display power on i915\n"); return err; } + hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + snd_hdac_ext_bus_link_put(ebus, hlink); + return 0; } @@ -1598,6 +1610,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev) struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + struct hdac_ext_link *hlink = NULL; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -1606,6 +1620,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev) if (!bus) return 0; + hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + snd_hdac_ext_bus_link_get(ebus, hlink); + err = snd_hdac_display_power(bus, true); if (err < 0) { dev_err(bus->dev, "Cannot turn on display power on i915\n"); @@ -1616,9 +1633,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev) hdac_hdmi_skl_enable_dp12(&edev->hdac); /* Power up afg */ - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) - snd_hdac_codec_write(hdac, hdac->afg, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); return 0; } @@ -1629,6 +1645,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev) static const struct dev_pm_ops hdac_hdmi_pm = { SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) + .prepare = hdmi_codec_prepare, + .complete = hdmi_codec_complete, }; static const struct hda_device_id hdmi_list[] = { diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 1c8729984c2b..683769f0f246 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -343,9 +343,12 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL, 0), - /* ADC for button press detection */ - SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, - NAU8825_SAR_ADC_EN_SFT, 0), + /* ADC for button press detection. A dapm supply widget is used to + * prevent dapm_power_widgets keeping the codec at SND_SOC_BIAS_ON + * during suspend. + */ + SND_SOC_DAPM_SUPPLY("SAR", NAU8825_REG_SAR_CTRL, + NAU8825_SAR_ADC_EN_SFT, 0, NULL, 0), SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0), SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0), @@ -607,6 +610,16 @@ static bool nau8825_is_jack_inserted(struct regmap *regmap) static void nau8825_restart_jack_detection(struct regmap *regmap) { + /* Chip needs one FSCLK cycle in order to generate interrupts, + * as we cannot guarantee one will be provided by the system. Turning + * master mode on then off enables us to generate that FSCLK cycle + * with a minimum of contention on the clock bus. + */ + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); + /* this will restart the entire jack detection process including MIC/GND * switching and create interrupts. We have to go from 0 to 1 and back * to 0 to restart. @@ -728,7 +741,10 @@ static irqreturn_t nau8825_interrupt(int irq, void *data) struct regmap *regmap = nau8825->regmap; int active_irq, clear_irq = 0, event = 0, event_mask = 0; - regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq); + if (regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq)) { + dev_err(nau8825->dev, "failed to read irq status\n"); + return IRQ_NONE; + } if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == NAU8825_JACK_EJECTION_DETECTED) { @@ -1141,33 +1157,74 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec, return ret; } } - - ret = regcache_sync(nau8825->regmap); - if (ret) { - dev_err(codec->dev, - "Failed to sync cache: %d\n", ret); - return ret; - } } - break; case SND_SOC_BIAS_OFF: if (nau8825->mclk_freq) clk_disable_unprepare(nau8825->mclk); - - regcache_mark_dirty(nau8825->regmap); break; } return 0; } +#ifdef CONFIG_PM +static int nau8825_suspend(struct snd_soc_codec *codec) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + + disable_irq(nau8825->irq); + regcache_cache_only(nau8825->regmap, true); + regcache_mark_dirty(nau8825->regmap); + + return 0; +} + +static int nau8825_resume(struct snd_soc_codec *codec) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + + /* The chip may lose power and reset in S3. regcache_sync restores + * register values including configurations for sysclk, irq, and + * jack/button detection. + */ + regcache_cache_only(nau8825->regmap, false); + regcache_sync(nau8825->regmap); + + /* Check the jack plug status directly. If the headset is unplugged + * during S3 when the chip has no power, there will be no jack + * detection irq even after the nau8825_restart_jack_detection below, + * because the chip just thinks no headset has ever been plugged in. + */ + if (!nau8825_is_jack_inserted(nau8825->regmap)) { + nau8825_eject_jack(nau8825); + snd_soc_jack_report(nau8825->jack, 0, SND_JACK_HEADSET); + } + + enable_irq(nau8825->irq); + + /* Run jack detection to check the type (OMTP or CTIA) of the headset + * if there is one. This handles the case where a different type of + * headset is plugged in during S3. This triggers an IRQ iff a headset + * is already plugged in. + */ + nau8825_restart_jack_detection(nau8825->regmap); + + return 0; +} +#else +#define nau8825_suspend NULL +#define nau8825_resume NULL +#endif + static struct snd_soc_codec_driver nau8825_codec_driver = { .probe = nau8825_codec_probe, .set_sysclk = nau8825_set_sysclk, .set_pll = nau8825_set_pll, .set_bias_level = nau8825_set_bias_level, .suspend_bias_off = true, + .suspend = nau8825_suspend, + .resume = nau8825_resume, .controls = nau8825_controls, .num_controls = ARRAY_SIZE(nau8825_controls), @@ -1277,16 +1334,6 @@ static int nau8825_setup_irq(struct nau8825 *nau8825) regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR); - /* Chip needs one FSCLK cycle in order to generate interrupts, - * as we cannot guarantee one will be provided by the system. Turning - * master mode on then off enables us to generate that FSCLK cycle - * with a minimum of contention on the clock bus. - */ - regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, - NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); - regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, - NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); - ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL, nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "nau8825", nau8825); @@ -1354,36 +1401,6 @@ static int nau8825_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int nau8825_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct nau8825 *nau8825 = dev_get_drvdata(dev); - - disable_irq(client->irq); - regcache_cache_only(nau8825->regmap, true); - regcache_mark_dirty(nau8825->regmap); - - return 0; -} - -static int nau8825_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct nau8825 *nau8825 = dev_get_drvdata(dev); - - regcache_cache_only(nau8825->regmap, false); - regcache_sync(nau8825->regmap); - enable_irq(client->irq); - - return 0; -} -#endif - -static const struct dev_pm_ops nau8825_pm = { - SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume) -}; - static const struct i2c_device_id nau8825_i2c_ids[] = { { "nau8825", 0 }, { } @@ -1410,7 +1427,6 @@ static struct i2c_driver nau8825_driver = { .name = "nau8825", .of_match_table = of_match_ptr(nau8825_of_ids), .acpi_match_table = ACPI_PTR(nau8825_acpi_match), - .pm = &nau8825_pm, }, .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c new file mode 100644 index 000000000000..ed515677409b --- /dev/null +++ b/sound/soc/codecs/pcm5102a.c @@ -0,0 +1,69 @@ +/* + * Driver for the PCM5102A codec + * + * Author: Florian Meier <florian.meier@koalo.de> + * Copyright 2013 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/soc.h> + +static struct snd_soc_dai_driver pcm5102a_dai = { + .name = "pcm5102a-hifi", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; + +static int pcm5102a_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, + &pcm5102a_dai, 1); +} + +static int pcm5102a_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id pcm5102a_of_match[] = { + { .compatible = "ti,pcm5102a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm5102a_of_match); + +static struct platform_driver pcm5102a_codec_driver = { + .probe = pcm5102a_probe, + .remove = pcm5102a_remove, + .driver = { + .name = "pcm5102a-codec", + .owner = THIS_MODULE, + .of_match_table = pcm5102a_of_match, + }, +}; + +module_platform_driver(pcm5102a_codec_driver); + +MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); +MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index f0e6c06e89ac..a1aaffc20862 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/dmi.h> #include <linux/acpi.h> #include <sound/core.h> #include <sound/pcm.h> @@ -1132,6 +1133,17 @@ static const struct acpi_device_id rt298_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, rt298_acpi_match); +static const struct dmi_system_id force_combo_jack_table[] = { + { + .ident = "Intel Broxton P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P") + } + }, + { } +}; + static int rt298_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1184,11 +1196,16 @@ static int rt298_i2c_probe(struct i2c_client *i2c, /* enable jack combo mode on supported devices */ acpiid = acpi_match_device(dev->driver->acpi_match_table, dev); - if (acpiid) { + if (acpiid && acpiid->driver_data) { rt298->pdata = *(struct rt298_platform_data *) acpiid->driver_data; } + if (dmi_check_system(force_combo_jack_table)) { + rt298->pdata.cbj_en = true; + rt298->pdata.gpio2_en = false; + } + /* VREF Charging */ regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80); regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860); diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e8b5ba04417a..09e8988bbb2d 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -359,7 +359,7 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv, /* Interface data select */ static const char * const rt5640_data_select[] = { - "Normal", "left copy to right", "right copy to left", "Swap"}; + "Normal", "Swap", "left copy to right", "right copy to left"}; static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA, RT5640_IF1_DAC_SEL_SFT, rt5640_data_select); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 1761c3a98b76..58b664b06c16 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -443,39 +443,39 @@ #define RT5640_IF1_DAC_SEL_MASK (0x3 << 14) #define RT5640_IF1_DAC_SEL_SFT 14 #define RT5640_IF1_DAC_SEL_NOR (0x0 << 14) -#define RT5640_IF1_DAC_SEL_L2R (0x1 << 14) -#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14) -#define RT5640_IF1_DAC_SEL_SWAP (0x3 << 14) +#define RT5640_IF1_DAC_SEL_SWAP (0x1 << 14) +#define RT5640_IF1_DAC_SEL_L2R (0x2 << 14) +#define RT5640_IF1_DAC_SEL_R2L (0x3 << 14) #define RT5640_IF1_ADC_SEL_MASK (0x3 << 12) #define RT5640_IF1_ADC_SEL_SFT 12 #define RT5640_IF1_ADC_SEL_NOR (0x0 << 12) -#define RT5640_IF1_ADC_SEL_L2R (0x1 << 12) -#define RT5640_IF1_ADC_SEL_R2L (0x2 << 12) -#define RT5640_IF1_ADC_SEL_SWAP (0x3 << 12) +#define RT5640_IF1_ADC_SEL_SWAP (0x1 << 12) +#define RT5640_IF1_ADC_SEL_L2R (0x2 << 12) +#define RT5640_IF1_ADC_SEL_R2L (0x3 << 12) #define RT5640_IF2_DAC_SEL_MASK (0x3 << 10) #define RT5640_IF2_DAC_SEL_SFT 10 #define RT5640_IF2_DAC_SEL_NOR (0x0 << 10) -#define RT5640_IF2_DAC_SEL_L2R (0x1 << 10) -#define RT5640_IF2_DAC_SEL_R2L (0x2 << 10) -#define RT5640_IF2_DAC_SEL_SWAP (0x3 << 10) +#define RT5640_IF2_DAC_SEL_SWAP (0x1 << 10) +#define RT5640_IF2_DAC_SEL_L2R (0x2 << 10) +#define RT5640_IF2_DAC_SEL_R2L (0x3 << 10) #define RT5640_IF2_ADC_SEL_MASK (0x3 << 8) #define RT5640_IF2_ADC_SEL_SFT 8 #define RT5640_IF2_ADC_SEL_NOR (0x0 << 8) -#define RT5640_IF2_ADC_SEL_L2R (0x1 << 8) -#define RT5640_IF2_ADC_SEL_R2L (0x2 << 8) -#define RT5640_IF2_ADC_SEL_SWAP (0x3 << 8) +#define RT5640_IF2_ADC_SEL_SWAP (0x1 << 8) +#define RT5640_IF2_ADC_SEL_L2R (0x2 << 8) +#define RT5640_IF2_ADC_SEL_R2L (0x3 << 8) #define RT5640_IF3_DAC_SEL_MASK (0x3 << 6) #define RT5640_IF3_DAC_SEL_SFT 6 #define RT5640_IF3_DAC_SEL_NOR (0x0 << 6) -#define RT5640_IF3_DAC_SEL_L2R (0x1 << 6) -#define RT5640_IF3_DAC_SEL_R2L (0x2 << 6) -#define RT5640_IF3_DAC_SEL_SWAP (0x3 << 6) +#define RT5640_IF3_DAC_SEL_SWAP (0x1 << 6) +#define RT5640_IF3_DAC_SEL_L2R (0x2 << 6) +#define RT5640_IF3_DAC_SEL_R2L (0x3 << 6) #define RT5640_IF3_ADC_SEL_MASK (0x3 << 4) #define RT5640_IF3_ADC_SEL_SFT 4 #define RT5640_IF3_ADC_SEL_NOR (0x0 << 4) -#define RT5640_IF3_ADC_SEL_L2R (0x1 << 4) -#define RT5640_IF3_ADC_SEL_R2L (0x2 << 4) -#define RT5640_IF3_ADC_SEL_SWAP (0x3 << 4) +#define RT5640_IF3_ADC_SEL_SWAP (0x1 << 4) +#define RT5640_IF3_ADC_SEL_L2R (0x2 << 4) +#define RT5640_IF3_ADC_SEL_R2L (0x3 << 4) /* REC Left Mixer Control 1 (0x3b) */ #define RT5640_G_HP_L_RM_L_MASK (0x7 << 13) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index a8b3e3f701f9..1bae17ee8817 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1955,11 +1955,16 @@ err_adsp2_codec_probe: static int wm5102_codec_remove(struct snd_soc_codec *codec) { struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; wm_adsp2_codec_remove(&priv->core.adsp[0], codec); priv->core.arizona->dapm = NULL; + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); + + arizona_free_spk(codec); + return 0; } diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 83ba70fe16e6..2728ac545ffe 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2298,6 +2298,8 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec) arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); + arizona_free_spk(codec); + return 0; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 88223608a33f..720a14e0687d 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2471,7 +2471,7 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) break; default: dev_warn(codec->dev, "Unknown DSPCLK divisor read back\n"); - dspclk = wm8962->sysclk; + dspclk = wm8962->sysclk_rate; } dev_dbg(codec->dev, "DSPCLK is %dHz, BCLK %d\n", dspclk, wm8962->bclk); diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 52d766efe14f..6b0785b5a5c5 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1072,6 +1072,8 @@ static int wm8997_codec_remove(struct snd_soc_codec *codec) priv->core.arizona->dapm = NULL; + arizona_free_spk(codec); + return 0; } diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 012396074a8a..449f66636205 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -1324,6 +1324,8 @@ static int wm8998_codec_remove(struct snd_soc_codec *codec) priv->core.arizona->dapm = NULL; + arizona_free_spk(codec); + return 0; } diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index e1324989bd6b..a1197ad023f6 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -489,7 +489,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, - ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR); + ACLKX | AFSX | ACLKR | AHCLKR | AFSR); mcasp->bclk_master = 0; break; default: diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index ed8de1035cda..08dcbbf60adb 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -137,6 +137,7 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) case CCSR_SSI_SACDAT: case CCSR_SSI_SATAG: case CCSR_SSI_SACCST: + case CCSR_SSI_SOR: return true; default: return false; diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 49d7513f429e..dac6688540dc 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -217,10 +217,10 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; int ret; - ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + ret = dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, + runtime->dma_addr, runtime->dma_bytes); - pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret, + pr_debug("%s: ret: %d %p %pad 0x%08zx\n", __func__, ret, runtime->dma_area, &runtime->dma_addr, runtime->dma_bytes); @@ -247,8 +247,7 @@ static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); + buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); if (!buf->area) return -ENOMEM; buf->bytes = size; @@ -330,8 +329,7 @@ static void imx_pcm_free(struct snd_pcm *pcm) if (!buf->area) continue; - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); + dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); buf->area = NULL; } } diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index b3e6c2300457..91c15abb625e 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -58,6 +58,21 @@ config SND_SOC_INTEL_HASWELL_MACH Say Y if you have such a device If unsure select "N". +config SND_SOC_INTEL_BXT_RT298_MACH + tristate "ASoC Audio driver for Broxton with RT298 I2S mode" + depends on X86 && ACPI && I2C + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT298 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Broxton platforms + with RT286 I2S audio codec. + Say Y if you have such a device + If unsure select "N". + config SND_SOC_INTEL_BYT_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" depends on X86_INTEL_LPSS && I2C @@ -162,8 +177,8 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE + select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY - select SND_HDA_I915 select SND_SOC_INTEL_SST config SND_SOC_INTEL_SKL_RT286_MACH diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index b97e6adcf1b2..98720a93de8a 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -195,7 +195,7 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol if (e->w && e->w->power) ret = sst_send_slot_map(drv); - else + else if (!e->w) dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n", kcontrol->id.name); return ret; diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 3310c0f9c356..a8506774f510 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -2,6 +2,7 @@ snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-broadwell-objs := broadwell.o +snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -14,6 +15,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 3f8a1e10bed0..7486a0022fde 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -201,7 +201,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { { /* SSP0 - Codec */ .name = "Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "snd-soc-dummy-dai", .platform_name = "snd-soc-dummy", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c new file mode 100644 index 000000000000..f4787515c0ed --- /dev/null +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -0,0 +1,353 @@ +/* + * Intel Broxton-P I2S Machine Driver + * + * Copyright (C) 2014-2016, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Skylake I2S Machine driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include "../../codecs/hdac_hdmi.h" +#include "../../codecs/rt298.h" + +static struct snd_soc_jack broxton_headset; +/* Headset jack detection DAPM pins */ + +enum { + BXT_DPCM_AUDIO_PB = 0, + BXT_DPCM_AUDIO_CP, + BXT_DPCM_AUDIO_REF_CP, + BXT_DPCM_AUDIO_HDMI1_PB, + BXT_DPCM_AUDIO_HDMI2_PB, + BXT_DPCM_AUDIO_HDMI3_PB, +}; + +static struct snd_soc_jack_pin broxton_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new broxton_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack detect */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMic", NULL, "SoC DMIC"}, + + {"HDMI1", NULL, "hif5 Output"}, + {"HDMI2", NULL, "hif6 Output"}, + {"HDMI3", NULL, "hif7 Output"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "codec0_out"}, + + { "codec0_in", NULL, "ssp5 Rx" }, + { "ssp5 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "Capture" }, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + +}; + +static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret = 0; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &broxton_headset, + broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins)); + + if (ret) + return ret; + + rt298_mic_detect(codec, &broxton_headset); + return 0; +} + +static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id); +} + +static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP5 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int broxton_rt298_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, + 19200000, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + + return ret; +} + +static struct snd_soc_ops broxton_rt298_ops = { + .hw_params = broxton_rt298_hw_params, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Front End DAI links */ + [BXT_DPCM_AUDIO_PB] + { + .name = "Bxt Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + [BXT_DPCM_AUDIO_CP] + { + .name = "Bxt Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + [BXT_DPCM_AUDIO_REF_CP] + { + .name = "Bxt Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI1_PB] + { + .name = "Bxt HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI2_PB] + { + .name = "Bxt HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI3_PB] + { + .name = "Bxt HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + /* Back End DAI links */ + { + /* SSP5 - Codec */ + .name = "SSP5-Codec", + .id = 0, + .cpu_dai_name = "SSP5 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt298-aif1", + .init = broxton_rt298_codec_init, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp5_fixup, + .ops = &broxton_rt298_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-rt298", + .owner = THIS_MODULE, + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = broxton_controls, + .num_controls = ARRAY_SIZE(broxton_controls), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + broxton_rt298.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298); +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .driver = { + .name = "bxt_alc298s_i2s", + }, +}; +module_platform_driver(broxton_audio) + +/* Module information */ +MODULE_AUTHOR("Ramesh Babu <Ramesh.Babu@intel.com>"); +MODULE_AUTHOR("Senthilnathan Veppur <senthilnathanx.veppur@intel.com>"); +MODULE_DESCRIPTION("Intel SST Audio for Broxton"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_alc298s_i2s"); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 032a2e753f0b..88efb62439ba 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -304,7 +304,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 1c95ccc886c4..35f591eab3c9 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -267,7 +267,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index e609f089593a..6260df6bd49c 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -255,7 +255,7 @@ static struct snd_soc_dai_link cht_dailink[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 2a6f80843bc9..0618a7f1025b 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -295,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 2e5347f8f96c..df9d254baa18 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -273,7 +273,7 @@ static struct snd_soc_dai_link cht_dailink[] = { { /* SSP2 - Codec */ .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index 22558572cb9c..863f1d5e2a2c 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -156,7 +156,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { { /* SSP0 - Codec */ .name = "Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "snd-soc-dummy-dai", .platform_name = "snd-soc-dummy", .no_pcm = 1, diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 72176b79a18d..7cc725153422 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -391,7 +391,6 @@ static struct snd_soc_dai_link skylake_dais[] = { .platform_name = "0000:00:1f.3", .init = NULL, .dpcm_capture = 1, - .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, .ops = &skylaye_refcap_ops, @@ -456,7 +455,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP0 - Codec */ .name = "SSP0-Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -472,7 +471,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -489,7 +488,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 2, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -501,7 +500,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp1", - .be_id = 3, + .id = 3, .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", @@ -512,7 +511,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp2", - .be_id = 4, + .id = 4, .cpu_dai_name = "iDisp2 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi2", @@ -523,7 +522,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp3", - .be_id = 5, + .id = 5, .cpu_dai_name = "iDisp3 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi3", diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 5f1ca99ae9b0..73cbddb640c6 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -440,7 +440,6 @@ static struct snd_soc_dai_link skylake_dais[] = { .platform_name = "0000:00:1f.3", .init = NULL, .dpcm_capture = 1, - .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, .ops = &skylaye_refcap_ops, @@ -505,7 +504,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP0 - Codec */ .name = "SSP0-Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -523,7 +522,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -540,7 +539,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 2, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -552,7 +551,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp1", - .be_id = 3, + .id = 3, .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", @@ -563,7 +562,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp2", - .be_id = 4, + .id = 4, .cpu_dai_name = "iDisp2 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi2", @@ -574,7 +573,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp3", - .be_id = 5, + .id = 5, .cpu_dai_name = "iDisp3 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi3", diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 2016397a8e75..d4cdb949f733 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -317,7 +317,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .platform_name = "0000:00:1f.3", .init = NULL, .dpcm_capture = 1, - .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, }, @@ -375,7 +374,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { { /* SSP0 - Codec */ .name = "SSP0-Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -393,7 +392,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "dmic01", - .be_id = 1, + .id = 1, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -405,7 +404,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "iDisp1", - .be_id = 2, + .id = 2, .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", @@ -416,7 +415,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "iDisp2", - .be_id = 3, + .id = 3, .cpu_dai_name = "iDisp2 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi2", @@ -427,7 +426,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "iDisp3", - .be_id = 4, + .id = 4, .cpu_dai_name = "iDisp3 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi3", diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 4dcfb7e5ed70..8398cb227ba9 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -12,10 +12,19 @@ * */ +#include <linux/kconfig.h> +#include <linux/stddef.h> #include <linux/acpi.h> /* translation fron HID to I2C name, needed for DAI codec_name */ +#if IS_ENABLED(CONFIG_ACPI) const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); +#else +inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +{ + return NULL; +} +#endif /* acpi match */ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index ac60f1301e21..91565229d074 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -1345,7 +1345,7 @@ int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream) return 0; /* wait for pause to complete before we reset the stream */ - while (stream->running && tries--) + while (stream->running && --tries) msleep(1); if (!tries) { dev_err(hsw->dev, "error: reset stream %d still running\n", diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 1aa819c7e09b..994256b39b9c 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -445,7 +445,7 @@ static int create_adsp_page_table(struct snd_pcm_substream *substream, pages = snd_sgbuf_aligned_pages(size); - dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n", + dev_dbg(rtd->dev, "generating page table for %p size 0x%zx pages %d\n", dma_area, size, pages); for (i = 0; i < pages; i++) { diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 914b6dab9bea..c28f5d0e1d99 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o # Skylake IPC Support snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \ - skl-sst.o + skl-sst.o bxt-sst.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c new file mode 100644 index 000000000000..965ce40ce752 --- /dev/null +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -0,0 +1,328 @@ +/* + * bxt-sst.c - DSP library functions for BXT platform + * + * Copyright (C) 2015-16 Intel Corp + * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> + * Jeeja KP <jeeja.kp@intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/device.h> + +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-ipc.h" + +#define BXT_BASEFW_TIMEOUT 3000 +#define BXT_INIT_TIMEOUT 500 +#define BXT_IPC_PURGE_FW 0x01004000 + +#define BXT_ROM_INIT 0x5 +#define BXT_ADSP_SRAM0_BASE 0x80000 + +/* Firmware status window */ +#define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE +#define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4) + +#define BXT_ADSP_SRAM1_BASE 0xA0000 + +static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) +{ + return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); +} + +static int sst_bxt_prepare_fw(struct sst_dsp *ctx, + const void *fwdata, u32 fwsize) +{ + int stream_tag, ret, i; + u32 reg; + + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); + if (stream_tag < 0) { + dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n", + stream_tag); + return stream_tag; + } + + ctx->dsp_ops.stream_tag = stream_tag; + memcpy(ctx->dmab.area, fwdata, fwsize); + + /* Purge FW request */ + sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY | + BXT_IPC_PURGE_FW | (stream_tag - 1)); + + ret = skl_dsp_enable_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret); + ret = -EIO; + goto base_fw_load_failed; + } + + for (i = BXT_INIT_TIMEOUT; i > 0; --i) { + reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE); + + if (reg & SKL_ADSP_REG_HIPCIE_DONE) { + sst_dsp_shim_update_bits_forced(ctx, + SKL_ADSP_REG_HIPCIE, + SKL_ADSP_REG_HIPCIE_DONE, + SKL_ADSP_REG_HIPCIE_DONE); + break; + } + mdelay(1); + } + if (!i) { + dev_info(ctx->dev, "Waiting for HIPCIE done, reg: 0x%x\n", reg); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCIE, + SKL_ADSP_REG_HIPCIE_DONE, + SKL_ADSP_REG_HIPCIE_DONE); + } + + /* enable Interrupt */ + skl_ipc_int_enable(ctx); + skl_ipc_op_int_enable(ctx); + + for (i = BXT_INIT_TIMEOUT; i > 0; --i) { + if (SKL_FW_INIT == + (sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) & + SKL_FW_STS_MASK)) { + + dev_info(ctx->dev, "ROM loaded, continue FW loading\n"); + break; + } + mdelay(1); + } + if (!i) { + dev_err(ctx->dev, "Timeout for ROM init, HIPCIE: 0x%x\n", reg); + ret = -EIO; + goto base_fw_load_failed; + } + + return ret; + +base_fw_load_failed: + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); + skl_dsp_disable_core(ctx); + return ret; +} + +static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) +{ + int ret; + + ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); + ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, + BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot"); + + ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); + + return ret; +} + +static int bxt_load_base_firmware(struct sst_dsp *ctx) +{ + const struct firmware *fw = NULL; + struct skl_sst *skl = ctx->thread_context; + int ret; + + ret = request_firmware(&fw, ctx->fw_name, ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + goto sst_load_base_firmware_failed; + } + + ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size); + /* Retry Enabling core and ROM load. Retry seemed to help */ + if (ret < 0) { + ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size); + if (ret < 0) { + dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret); + goto sst_load_base_firmware_failed; + } + } + + ret = sst_transfer_fw_host_dma(ctx); + if (ret < 0) { + dev_err(ctx->dev, "Transfer firmware failed %d\n", ret); + dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + + skl_dsp_disable_core(ctx); + } else { + dev_dbg(ctx->dev, "Firmware download successful\n"); + ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, + msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); + if (ret == 0) { + dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n"); + skl_dsp_disable_core(ctx); + ret = -EIO; + } else { + skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + ret = 0; + } + } + +sst_load_base_firmware_failed: + release_firmware(fw); + return ret; +} + +static int bxt_set_dsp_D0(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + int ret; + + skl->boot_complete = false; + + ret = skl_dsp_enable_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret); + return ret; + } + + /* enable interrupt */ + skl_ipc_int_enable(ctx); + skl_ipc_op_int_enable(ctx); + + ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, + msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); + if (ret == 0) { + dev_err(ctx->dev, "ipc: error DSP boot timeout\n"); + dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + return -EIO; + } + + skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + return 0; +} + +static int bxt_set_dsp_D3(struct sst_dsp *ctx) +{ + struct skl_ipc_dxstate_info dx; + struct skl_sst *skl = ctx->thread_context; + int ret = 0; + + if (!is_skl_dsp_running(ctx)) + return ret; + + dx.core_mask = SKL_DSP_CORE0_MASK; + dx.dx_mask = SKL_IPC_D3_MASK; + + ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, + SKL_BASE_FW_MODULE_ID, &dx); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret); + return ret; + } + + ret = skl_dsp_disable_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret); + ret = -EIO; + } + + skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + return 0; +} + +static struct skl_dsp_fw_ops bxt_fw_ops = { + .set_state_D0 = bxt_set_dsp_D0, + .set_state_D3 = bxt_set_dsp_D3, + .load_fw = bxt_load_base_firmware, + .get_fw_errcode = bxt_get_errorcode, +}; + +static struct sst_ops skl_ops = { + .irq_handler = skl_dsp_sst_interrupt, + .write = sst_shim32_write, + .read = sst_shim32_read, + .ram_read = sst_memcpy_fromio_32, + .ram_write = sst_memcpy_toio_32, + .free = skl_dsp_free, +}; + +static struct sst_dsp_device skl_dev = { + .thread = skl_dsp_irq_thread_handler, + .ops = &skl_ops, +}; + +int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp) +{ + struct skl_sst *skl; + struct sst_dsp *sst; + int ret; + + skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL); + if (skl == NULL) + return -ENOMEM; + + skl->dev = dev; + skl_dev.thread_context = skl; + + skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq); + if (!skl->dsp) { + dev_err(skl->dev, "skl_dsp_ctx_init failed\n"); + return -ENODEV; + } + + sst = skl->dsp; + sst->fw_name = fw_name; + sst->dsp_ops = dsp_ops; + sst->fw_ops = bxt_fw_ops; + sst->addr.lpe = mmio_base; + sst->addr.shim = mmio_base; + + sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), + SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); + + ret = skl_ipc_init(dev, skl); + if (ret) + return ret; + + skl->boot_complete = false; + init_waitqueue_head(&skl->boot_wait); + + ret = sst->fw_ops.load_fw(sst); + if (ret < 0) { + dev_err(dev, "Load base fw failed: %x", ret); + return ret; + } + + if (dsp) + *dsp = skl; + + return 0; +} +EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); + + +void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) +{ + skl_ipc_free(&ctx->ipc); + ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); + + if (ctx->dsp->addr.lpe) + iounmap(ctx->dsp->addr.lpe); + + ctx->dsp->ops->free(ctx->dsp); +} +EXPORT_SYMBOL_GPL(bxt_sst_dsp_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Broxton IPC driver"); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 79c5089b85d6..226db84ba20f 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -72,6 +72,105 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); } +static int skl_dsp_setup_spib(struct device *dev, unsigned int size, + int stream_tag, int enable) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *stream = snd_hdac_get_stream(bus, + SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + struct hdac_ext_stream *estream; + + if (!stream) + return -EINVAL; + + estream = stream_to_hdac_ext_stream(stream); + /* enable/disable SPIB for this hdac stream */ + snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index); + + /* set the spib value */ + snd_hdac_ext_stream_set_spib(ebus, estream, size); + + return 0; +} + +static int skl_dsp_prepare(struct device *dev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *estream; + struct hdac_stream *stream; + struct snd_pcm_substream substream; + int ret; + + if (!bus) + return -ENODEV; + + memset(&substream, 0, sizeof(substream)); + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + + estream = snd_hdac_ext_stream_assign(ebus, &substream, + HDAC_EXT_STREAM_TYPE_HOST); + if (!estream) + return -ENODEV; + + stream = hdac_stream(estream); + + /* assign decouple host dma channel */ + ret = snd_hdac_dsp_prepare(stream, format, size, dmab); + if (ret < 0) + return ret; + + skl_dsp_setup_spib(dev, size, stream->stream_tag, true); + + return stream->stream_tag; +} + +static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_stream *stream; + struct hdac_bus *bus = ebus_to_hbus(ebus); + + if (!bus) + return -ENODEV; + + stream = snd_hdac_get_stream(bus, + SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + if (!stream) + return -EINVAL; + + snd_hdac_dsp_trigger(stream, start); + + return 0; +} + +static int skl_dsp_cleanup(struct device *dev, + struct snd_dma_buffer *dmab, int stream_tag) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_stream *stream; + struct hdac_ext_stream *estream; + struct hdac_bus *bus = ebus_to_hbus(ebus); + + if (!bus) + return -ENODEV; + + stream = snd_hdac_get_stream(bus, + SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + if (!stream) + return -EINVAL; + + estream = stream_to_hdac_ext_stream(stream); + skl_dsp_setup_spib(dev, 0, stream_tag, false); + snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST); + + snd_hdac_dsp_cleanup(stream, dmab); + + return 0; +} + static struct skl_dsp_loader_ops skl_get_loader_ops(void) { struct skl_dsp_loader_ops loader_ops; @@ -84,6 +183,21 @@ static struct skl_dsp_loader_ops skl_get_loader_ops(void) return loader_ops; }; +static struct skl_dsp_loader_ops bxt_get_loader_ops(void) +{ + struct skl_dsp_loader_ops loader_ops; + + memset(&loader_ops, 0, sizeof(loader_ops)); + + loader_ops.alloc_dma_buf = skl_alloc_dma_buf; + loader_ops.free_dma_buf = skl_free_dma_buf; + loader_ops.prepare = skl_dsp_prepare; + loader_ops.trigger = skl_dsp_trigger; + loader_ops.cleanup = skl_dsp_cleanup; + + return loader_ops; +}; + static const struct skl_dsp_ops dsp_ops[] = { { .id = 0x9d70, @@ -91,6 +205,12 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = skl_sst_dsp_init, .cleanup = skl_sst_dsp_cleanup }, + { + .id = 0x5a98, + .loader_ops = bxt_get_loader_ops, + .init = bxt_sst_dsp_init, + .cleanup = bxt_sst_dsp_cleanup + }, }; static int skl_get_dsp_ops(int pci_id) @@ -744,7 +864,7 @@ int skl_init_module(struct skl_sst *ctx, return ret; } mconfig->m_state = SKL_MODULE_INIT_DONE; - + kfree(param_data); return ret; } diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 14d1916ea9f8..7d73648e5f9a 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -25,11 +25,12 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" -void *skl_nhlt_init(struct device *dev) +struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) { acpi_handle handle; union acpi_object *obj; struct nhlt_resource_desc *nhlt_ptr = NULL; + struct nhlt_acpi_table *nhlt_table = NULL; if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) { dev_err(dev, "Requested NHLT device not found\n"); @@ -39,18 +40,20 @@ void *skl_nhlt_init(struct device *dev) obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL); if (obj && obj->type == ACPI_TYPE_BUFFER) { nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - - return memremap(nhlt_ptr->min_addr, nhlt_ptr->length, + nhlt_table = (struct nhlt_acpi_table *) + memremap(nhlt_ptr->min_addr, nhlt_ptr->length, MEMREMAP_WB); + ACPI_FREE(obj); + return nhlt_table; } dev_err(dev, "device specific method to extract NHLT blob failed\n"); return NULL; } -void skl_nhlt_free(void *addr) +void skl_nhlt_free(struct nhlt_acpi_table *nhlt) { - memunmap(addr); + memunmap((void *) nhlt); } static struct nhlt_specific_cfg *skl_get_specific_cfg( @@ -120,7 +123,7 @@ struct nhlt_specific_cfg struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct device *dev = bus->dev; struct nhlt_specific_cfg *sp_config; - struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; + struct nhlt_acpi_table *nhlt = skl->nhlt; u16 bps = (s_fmt == 16) ? 16 : 32; u8 j; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index dab0900eef26..15480234b20b 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -213,7 +213,7 @@ static int skl_be_prepare(struct snd_pcm_substream *substream, struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; - if ((dai->playback_active > 1) || (dai->capture_active > 1)) + if (dai->playback_widget->power || dai->capture_widget->power) return 0; mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); @@ -402,23 +402,33 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl_module_cfg *mconfig; struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct snd_soc_dapm_widget *w; int ret; mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); if (!mconfig) return -EIO; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + w = dai->playback_widget; + else + w = dai->capture_widget; + switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - skl_pcm_prepare(substream, dai); - /* - * enable DMA Resume enable bit for the stream, set the dpib - * & lpib position to resune before starting the DMA - */ - snd_hdac_ext_stream_drsm_enable(ebus, true, - hdac_stream(stream)->index); - snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib); - snd_hdac_ext_stream_set_lpib(stream, stream->lpib); + if (!w->ignore_suspend) { + skl_pcm_prepare(substream, dai); + /* + * enable DMA Resume enable bit for the stream, set the + * dpib & lpib position to resume before starting the + * DMA + */ + snd_hdac_ext_stream_drsm_enable(ebus, true, + hdac_stream(stream)->index); + snd_hdac_ext_stream_set_dpibr(ebus, stream, + stream->dpib); + snd_hdac_ext_stream_set_lpib(stream, stream->lpib); + } case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: @@ -448,7 +458,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, return ret; ret = skl_decoupled_trigger(substream, cmd); - if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) { + if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) { /* save the dpib and lpib positions */ stream->dpib = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE + @@ -523,7 +533,6 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, if (!link) return -EINVAL; - snd_hdac_ext_bus_link_power_up(link); snd_hdac_ext_link_stream_reset(link_dev); snd_hdac_ext_link_stream_setup(link_dev, format_val); @@ -760,6 +769,78 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { + .name = "SSP2 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp2 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp2 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "SSP3 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp3 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp3 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "SSP4 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp4 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp4 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "SSP5 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp5 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp5 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ .name = "iDisp1 Pin", .ops = &skl_link_dai_ops, .playback = { diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index a5267e8a96e0..13c19855ee1a 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -336,6 +336,9 @@ void skl_dsp_free(struct sst_dsp *dsp) skl_ipc_int_disable(dsp); free_irq(dsp->irq, dsp); + skl_ipc_op_int_disable(dsp); + skl_ipc_int_disable(dsp); + skl_dsp_disable_core(dsp); } EXPORT_SYMBOL_GPL(skl_dsp_free); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index b6e310d49dd6..deabe7308d3b 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -118,16 +118,25 @@ struct skl_dsp_fw_ops { int (*set_state_D0)(struct sst_dsp *ctx); int (*set_state_D3)(struct sst_dsp *ctx); unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); - int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name); + int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); }; struct skl_dsp_loader_ops { + int stream_tag; + int (*alloc_dma_buf)(struct device *dev, struct snd_dma_buffer *dmab, size_t size); int (*free_dma_buf)(struct device *dev, struct snd_dma_buffer *dmab); + int (*prepare)(struct device *dev, unsigned int format, + unsigned int byte_size, + struct snd_dma_buffer *bufp); + int (*trigger)(struct device *dev, bool start, int stream_tag); + + int (*cleanup)(struct device *dev, struct snd_dma_buffer *dmab, + int stream_tag); }; struct skl_load_module_info { @@ -160,6 +169,10 @@ int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); +int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); +void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 348a734f8e24..13ec8d53b526 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -20,6 +20,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/uuid.h> #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" #include "../common/sst-ipc.h" @@ -304,14 +305,16 @@ static int skl_transfer_module(struct sst_dsp *ctx, return ret; } -static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid) +static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid) { struct skl_module_table *module_entry = NULL; int ret = 0; char mod_name[64]; /* guid str = 32 chars + 4 hyphens */ + uuid_le *uuid_mod; - snprintf(mod_name, sizeof(mod_name), "%s%s%s", - "intel/dsp_fw_", guid, ".bin"); + uuid_mod = (uuid_le *)guid; + snprintf(mod_name, sizeof(mod_name), "%s%pUL%s", + "intel/dsp_fw_", uuid_mod, ".bin"); module_entry = skl_module_get_from_id(ctx, mod_id); if (module_entry == NULL) { @@ -451,6 +454,10 @@ void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) skl_clear_module_table(ctx->dsp); skl_ipc_free(&ctx->ipc); ctx->dsp->ops->free(ctx->dsp); + if (ctx->boot_complete) { + ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); + skl_cldma_int_disable(ctx->dsp); + } } EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 545b4e77b8aa..a1f4478fabcb 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -239,6 +239,7 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx, { int multiplier = 1; struct skl_module_fmt *in_fmt, *out_fmt; + int in_rate, out_rate; /* Since fixups is applied to pin 0 only, ibs, obs needs @@ -249,15 +250,24 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx, if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT) multiplier = 5; - mcfg->ibs = (in_fmt->s_freq / 1000) * - (mcfg->in_fmt->channels) * - (mcfg->in_fmt->bit_depth >> 3) * - multiplier; - - mcfg->obs = (mcfg->out_fmt->s_freq / 1000) * - (mcfg->out_fmt->channels) * - (mcfg->out_fmt->bit_depth >> 3) * - multiplier; + + if (in_fmt->s_freq % 1000) + in_rate = (in_fmt->s_freq / 1000) + 1; + else + in_rate = (in_fmt->s_freq / 1000); + + mcfg->ibs = in_rate * (mcfg->in_fmt->channels) * + (mcfg->in_fmt->bit_depth >> 3) * + multiplier; + + if (mcfg->out_fmt->s_freq % 1000) + out_rate = (mcfg->out_fmt->s_freq / 1000) + 1; + else + out_rate = (mcfg->out_fmt->s_freq / 1000); + + mcfg->obs = out_rate * (mcfg->out_fmt->channels) * + (mcfg->out_fmt->bit_depth >> 3) * + multiplier; } static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, @@ -485,11 +495,15 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -ENOMEM; + skl_tplg_alloc_pipe_mcps(skl, mconfig); + if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) { ret = ctx->dsp->fw_ops.load_mod(ctx->dsp, mconfig->id.module_id, mconfig->guid); if (ret < 0) return ret; + + mconfig->m_state = SKL_MODULE_LOADED; } /* update blob if blob is null for be with default value */ @@ -509,7 +523,6 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) ret = skl_tplg_set_module_params(w, ctx); if (ret < 0) return ret; - skl_tplg_alloc_pipe_mcps(skl, mconfig); } return 0; @@ -524,7 +537,8 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, list_for_each_entry(w_module, &pipe->w_list, node) { mconfig = w_module->w->priv; - if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod) + if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod && + mconfig->m_state > SKL_MODULE_UNINIT) return ctx->dsp->fw_ops.unload_mod(ctx->dsp, mconfig->id.module_id); } @@ -558,6 +572,9 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, if (!skl_is_pipe_mem_avail(skl, mconfig)) return -ENOMEM; + skl_tplg_alloc_pipe_mem(skl, mconfig); + skl_tplg_alloc_pipe_mcps(skl, mconfig); + /* * Create a list of modules for pipe. * This list contains modules from source to sink @@ -601,9 +618,6 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, src_module = dst_module; } - skl_tplg_alloc_pipe_mem(skl, mconfig); - skl_tplg_alloc_pipe_mcps(skl, mconfig); - return 0; } @@ -1550,6 +1564,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, return -ENOMEM; w->priv = mconfig; + memcpy(&mconfig->guid, &dfw_config->uuid, 16); + mconfig->id.module_id = dfw_config->module_id; mconfig->id.instance_id = dfw_config->instance_id; mconfig->mcps = dfw_config->max_mcps; @@ -1579,10 +1595,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - if (dfw_config->is_loadable) - memcpy(mconfig->guid, dfw_config->uuid, - ARRAY_SIZE(dfw_config->uuid)); - mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * sizeof(*mconfig->m_in_pin), GFP_KERNEL); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index de3c401284d9..e4b399cd7868 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -274,14 +274,14 @@ struct skl_pipe { enum skl_module_state { SKL_MODULE_UNINIT = 0, - SKL_MODULE_INIT_DONE = 1, - SKL_MODULE_LOADED = 2, - SKL_MODULE_UNLOADED = 3, - SKL_MODULE_BIND_DONE = 4 + SKL_MODULE_LOADED = 1, + SKL_MODULE_INIT_DONE = 2, + SKL_MODULE_BIND_DONE = 3, + SKL_MODULE_UNLOADED = 4, }; struct skl_module_cfg { - char guid[SKL_UUID_STR_SZ]; + u8 guid[16]; struct skl_module_inst_id id; u8 domain; bool homogenous_inputs; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 1db88a63ac17..a32e5e9cc530 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -181,7 +181,7 @@ struct skl_dfw_pipe { } __packed; struct skl_dfw_module { - char uuid[SKL_UUID_STR_SZ]; + u8 uuid[16]; u16 module_id; u16 instance_id; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ab5e25aaeee3..06d8c263c68f 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -222,20 +222,36 @@ static int skl_suspend(struct device *dev) struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct skl *skl = ebus_to_skl(ebus); struct hdac_bus *bus = ebus_to_hbus(ebus); + int ret = 0; /* * Do not suspend if streams which are marked ignore suspend are * running, we need to save the state for these and continue */ if (skl->supend_active) { + /* turn off the links and stop the CORB/RIRB DMA if it is On */ snd_hdac_ext_bus_link_power_down_all(ebus); + + if (ebus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(&ebus->bus); + enable_irq_wake(bus->irq); pci_save_state(pci); pci_disable_device(pci); - return 0; } else { - return _skl_suspend(ebus); + ret = _skl_suspend(ebus); + if (ret < 0) + return ret; + } + + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, false); + if (ret < 0) + dev_err(bus->dev, + "Cannot turn OFF display power on i915\n"); } + + return ret; } static int skl_resume(struct device *dev) @@ -244,6 +260,7 @@ static int skl_resume(struct device *dev) struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct skl *skl = ebus_to_skl(ebus); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_link *hlink = NULL; int ret; /* Turned OFF in HDMI codec driver after codec reconfiguration */ @@ -265,8 +282,29 @@ static int skl_resume(struct device *dev) ret = pci_enable_device(pci); snd_hdac_ext_bus_link_power_up_all(ebus); disable_irq_wake(bus->irq); + /* + * turn On the links which are On before active suspend + * and start the CORB/RIRB DMA if On before + * active suspend. + */ + list_for_each_entry(hlink, &ebus->hlink_list, list) { + if (hlink->ref_count) + snd_hdac_ext_bus_link_power_up(hlink); + } + + if (ebus->cmd_dma_state) + snd_hdac_bus_init_cmd_io(&ebus->bus); } else { ret = _skl_resume(ebus); + + /* turn off the links which are off before suspend */ + list_for_each_entry(hlink, &ebus->hlink_list, list) { + if (!hlink->ref_count) + snd_hdac_ext_bus_link_power_down(hlink); + } + + if (!ebus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(&ebus->bus); } return ret; @@ -316,17 +354,20 @@ static int skl_free(struct hdac_ext_bus *ebus) if (bus->irq >= 0) free_irq(bus->irq, (void *)bus); - if (bus->remap_addr) - iounmap(bus->remap_addr); - snd_hdac_bus_free_stream_pages(bus); snd_hdac_stream_free_all(ebus); snd_hdac_link_free_all(ebus); + + if (bus->remap_addr) + iounmap(bus->remap_addr); + pci_release_regions(skl->pci); pci_disable_device(skl->pci); snd_hdac_ext_bus_exit(ebus); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_i915_exit(&ebus->bus); return 0; } @@ -599,6 +640,7 @@ static int skl_probe(struct pci_dev *pci, struct skl *skl; struct hdac_ext_bus *ebus = NULL; struct hdac_bus *bus = NULL; + struct hdac_ext_link *hlink = NULL; int err; /* we use ext core ops, so provide NULL for ops here */ @@ -629,7 +671,7 @@ static int skl_probe(struct pci_dev *pci, err = skl_machine_device_register(skl, (void *)pci_id->driver_data); if (err < 0) - goto out_free; + goto out_nhlt_free; err = skl_init_dsp(skl); if (err < 0) { @@ -665,6 +707,12 @@ static int skl_probe(struct pci_dev *pci, } } + /* + * we are done probling so decrement link counts + */ + list_for_each_entry(hlink, &ebus->hlink_list, list) + snd_hdac_ext_bus_link_put(ebus, hlink); + /*configure PM */ pm_runtime_put_noidle(bus->dev); pm_runtime_allow(bus->dev); @@ -679,6 +727,8 @@ out_dsp_free: skl_free_dsp(skl); out_mach_free: skl_machine_device_unregister(skl); +out_nhlt_free: + skl_nhlt_free(skl->nhlt); out_free: skl->init_failed = 1; skl_free(ebus); @@ -719,16 +769,17 @@ static void skl_remove(struct pci_dev *pci) if (skl->tplg) release_firmware(skl->tplg); - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) - snd_hdac_i915_exit(&ebus->bus); - if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev); - pci_dev_put(pci); + + /* codec removal, invoke bus_device_remove */ + snd_hdac_ext_bus_device_remove(ebus); + skl_platform_unregister(&pci->dev); skl_free_dsp(skl); skl_machine_device_unregister(skl); skl_dmic_device_unregister(skl); + skl_nhlt_free(skl->nhlt); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 39e16fa7a92b..4b4b3876aea9 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -66,7 +66,7 @@ struct skl { struct platform_device *dmic_dev; struct platform_device *i2s_dev; - void *nhlt; /* nhlt ptr */ + struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ struct skl_dsp_resource resource; @@ -103,8 +103,8 @@ struct skl_dsp_ops { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -void *skl_nhlt_init(struct device *dev); -void skl_nhlt_free(void *addr); +struct nhlt_acpi_table *skl_nhlt_init(struct device *dev); +void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index e09326158bc2..2cca055fd806 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -267,10 +267,8 @@ static int nuc900_dma_mmap(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); + return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, + runtime->dma_addr, runtime->dma_bytes); } static struct snd_pcm_ops nuc900_dma_ops = { diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 6bb623a2a4df..99381a27295b 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -156,10 +156,8 @@ static int omap_pcm_mmap(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); + return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, + runtime->dma_addr, runtime->dma_bytes); } static struct snd_pcm_ops omap_pcm_ops = { @@ -183,8 +181,7 @@ static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); + buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); if (!buf->area) return -ENOMEM; @@ -207,8 +204,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) if (!buf->area) continue; - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); + dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); buf->area = NULL; } } diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 2f8e20416bd3..574c6af28c06 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -34,6 +34,13 @@ struct rk_i2s_dev { struct regmap *regmap; +/* + * Used to indicate the tx/rx status. + * I2S controller hopes to start the tx and rx together, + * also to stop them when they are both try to stop. +*/ + bool tx_start; + bool rx_start; bool is_master_mode; }; @@ -75,29 +82,37 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START, - I2S_XFER_TXS_START); + I2S_XFER_TXS_START | I2S_XFER_RXS_START, + I2S_XFER_TXS_START | I2S_XFER_RXS_START); + + i2s->tx_start = true; } else { + i2s->tx_start = false; + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); - regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START, - I2S_XFER_TXS_STOP); + if (!i2s->rx_start) { + regmap_update_bits(i2s->regmap, I2S_XFER, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START, + I2S_XFER_TXS_STOP | + I2S_XFER_RXS_STOP); - regmap_update_bits(i2s->regmap, I2S_CLR, - I2S_CLR_TXC, - I2S_CLR_TXC); + regmap_update_bits(i2s->regmap, I2S_CLR, + I2S_CLR_TXC | I2S_CLR_RXC, + I2S_CLR_TXC | I2S_CLR_RXC); - regmap_read(i2s->regmap, I2S_CLR, &val); - - /* Should wait for clear operation to finish */ - while (val & I2S_CLR_TXC) { regmap_read(i2s->regmap, I2S_CLR, &val); - retry--; - if (!retry) { - dev_warn(i2s->dev, "fail to clear\n"); - break; + + /* Should wait for clear operation to finish */ + while (val) { + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { + dev_warn(i2s->dev, "fail to clear\n"); + break; + } } } } @@ -113,29 +128,37 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_RXS_START, - I2S_XFER_RXS_START); + I2S_XFER_TXS_START | I2S_XFER_RXS_START, + I2S_XFER_TXS_START | I2S_XFER_RXS_START); + + i2s->rx_start = true; } else { + i2s->rx_start = false; + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE); - regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_RXS_START, - I2S_XFER_RXS_STOP); + if (!i2s->tx_start) { + regmap_update_bits(i2s->regmap, I2S_XFER, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START, + I2S_XFER_TXS_STOP | + I2S_XFER_RXS_STOP); - regmap_update_bits(i2s->regmap, I2S_CLR, - I2S_CLR_RXC, - I2S_CLR_RXC); + regmap_update_bits(i2s->regmap, I2S_CLR, + I2S_CLR_TXC | I2S_CLR_RXC, + I2S_CLR_TXC | I2S_CLR_RXC); - regmap_read(i2s->regmap, I2S_CLR, &val); - - /* Should wait for clear operation to finish */ - while (val & I2S_CLR_RXC) { regmap_read(i2s->regmap, I2S_CLR, &val); - retry--; - if (!retry) { - dev_warn(i2s->dev, "fail to clear\n"); - break; + + /* Should wait for clear operation to finish */ + while (val) { + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { + dev_warn(i2s->dev, "fail to clear\n"); + break; + } } } } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 801ae1a81dfd..c4464858bf01 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2188,6 +2188,13 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, int count = 0; char *state = "not set"; + /* card won't be set for the dummy component, as a spot fix + * we're checking for that case specifically here but in future + * we will ensure that the dummy component looks like others. + */ + if (!cmpnt->card) + return 0; + list_for_each_entry(w, &cmpnt->card->widgets, list) { if (w->dapm != dapm) continue; diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 6fd1906af387..6cef3977507a 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -163,31 +163,42 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea } /* - * Prepare formats mask for valid/allowed sample types. If the dma does - * not have support for the given physical word size, it needs to be - * masked out so user space can not use the format which produces - * corrupted audio. - * In case the dma driver does not implement the slave_caps the default - * assumption is that it supports 1, 2 and 4 bytes widths. + * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep + * hw.formats set to 0, meaning no restrictions are in place. + * In this case it's the responsibility of the DAI driver to + * provide the supported format information. */ - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - int bits = snd_pcm_format_physical_width(i); - - /* Enable only samples with DMA supported physical widths */ - switch (bits) { - case 8: - case 16: - case 24: - case 32: - case 64: - if (addr_widths & (1 << (bits / 8))) - hw.formats |= (1LL << i); - break; - default: - /* Unsupported types */ - break; + if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)) + /* + * Prepare formats mask for valid/allowed sample types. If the + * dma does not have support for the given physical word size, + * it needs to be masked out so user space can not use the + * format which produces corrupted audio. + * In case the dma driver does not implement the slave_caps the + * default assumption is that it supports 1, 2 and 4 bytes + * widths. + */ + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { + int bits = snd_pcm_format_physical_width(i); + + /* + * Enable only samples with DMA supported physical + * widths + */ + switch (bits) { + case 8: + case 16: + case 24: + case 32: + case 64: + if (addr_widths & (1 << (bits / 8))) + hw.formats |= (1LL << i); + break; + default: + /* Unsupported types */ + break; + } } - } return snd_soc_set_runtime_hwparams(substream, &hw); } |