Commit aab0bb17 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/intel' into asoc-next

parents 77b62fa5 a5e5e12b
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
struct rt5670_platform_data { struct rt5670_platform_data {
int jd_mode; int jd_mode;
bool in2_diff; bool in2_diff;
bool dev_gpio;
bool dmic_en; bool dmic_en;
unsigned int dmic1_data_pin; unsigned int dmic1_data_pin;
......
...@@ -403,6 +403,189 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg) ...@@ -403,6 +403,189 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
} }
} }
/**
* rt5670_headset_detect - Detect headset.
* @codec: SoC audio codec device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
*
* Returns detect status.
*/
static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
{
int val;
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
if (jack_insert) {
snd_soc_dapm_force_enable_pin(&codec->dapm,
"Mic Det Power");
snd_soc_dapm_sync(&codec->dapm);
snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
RT5670_CBJ_MN_JD);
snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004);
snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
snd_soc_update_bits(codec, RT5670_CJ_CTRL1,
RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN);
snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0);
snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD);
snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
RT5670_CBJ_MN_JD, 0);
msleep(300);
val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7;
if (val == 0x1 || val == 0x2) {
rt5670->jack_type = SND_JACK_HEADSET;
/* for push button */
snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8);
snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40);
snd_soc_read(codec, RT5670_IL_CMD);
} else {
snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = SND_JACK_HEADPHONE;
snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
snd_soc_dapm_sync(&codec->dapm);
}
} else {
snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = 0;
snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
snd_soc_dapm_sync(&codec->dapm);
}
return rt5670->jack_type;
}
void rt5670_jack_suspend(struct snd_soc_codec *codec)
{
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
rt5670->jack_type_saved = rt5670->jack_type;
rt5670_headset_detect(codec, 0);
}
EXPORT_SYMBOL_GPL(rt5670_jack_suspend);
void rt5670_jack_resume(struct snd_soc_codec *codec)
{
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
if (rt5670->jack_type_saved)
rt5670_headset_detect(codec, 1);
}
EXPORT_SYMBOL_GPL(rt5670_jack_resume);
static int rt5670_button_detect(struct snd_soc_codec *codec)
{
int btn_type, val;
val = snd_soc_read(codec, RT5670_IL_CMD);
btn_type = val & 0xff80;
snd_soc_write(codec, RT5670_IL_CMD, val);
if (btn_type != 0) {
msleep(20);
val = snd_soc_read(codec, RT5670_IL_CMD);
snd_soc_write(codec, RT5670_IL_CMD, val);
}
return btn_type;
}
static int rt5670_irq_detection(void *data)
{
struct rt5670_priv *rt5670 = (struct rt5670_priv *)data;
struct snd_soc_jack_gpio *gpio = &rt5670->hp_gpio;
struct snd_soc_jack *jack = rt5670->jack;
int val, btn_type, report = jack->status;
if (rt5670->pdata.jd_mode == 1) /* 2 port */
val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070;
else
val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020;
switch (val) {
/* jack in */
case 0x30: /* 2 port */
case 0x0: /* 1 port or 2 port */
if (rt5670->jack_type == 0) {
report = rt5670_headset_detect(rt5670->codec, 1);
/* for push button and jack out */
gpio->debounce_time = 25;
break;
}
btn_type = 0;
if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) {
/* button pressed */
report = SND_JACK_HEADSET;
btn_type = rt5670_button_detect(rt5670->codec);
switch (btn_type) {
case 0x2000: /* up */
report |= SND_JACK_BTN_1;
break;
case 0x0400: /* center */
report |= SND_JACK_BTN_0;
break;
case 0x0080: /* down */
report |= SND_JACK_BTN_2;
break;
default:
dev_err(rt5670->codec->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
}
}
if (btn_type == 0)/* button release */
report = rt5670->jack_type;
break;
/* jack out */
case 0x70: /* 2 port */
case 0x10: /* 2 port */
case 0x20: /* 1 port */
report = 0;
snd_soc_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0);
rt5670_headset_detect(rt5670->codec, 0);
gpio->debounce_time = 150; /* for jack in */
break;
default:
break;
}
return report;
}
int rt5670_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack)
{
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
int ret;
rt5670->jack = jack;
rt5670->hp_gpio.gpiod_dev = codec->dev;
rt5670->hp_gpio.name = "headphone detect";
rt5670->hp_gpio.report = SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2;
rt5670->hp_gpio.debounce_time = 150;
rt5670->hp_gpio.wake = true;
rt5670->hp_gpio.data = (struct rt5670_priv *)rt5670;
rt5670->hp_gpio.jack_status_check = rt5670_irq_detection;
ret = snd_soc_jack_add_gpios(rt5670->jack, 1,
&rt5670->hp_gpio);
if (ret) {
dev_err(codec->dev, "Adding jack GPIO failed\n");
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(rt5670_set_jack_detect);
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
...@@ -517,11 +700,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, ...@@ -517,11 +700,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink) struct snd_soc_dapm_widget *sink)
{ {
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
unsigned int val; struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
val = snd_soc_read(codec, RT5670_GLB_CLK); if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
val &= RT5670_SCLK_SRC_MASK;
if (val == RT5670_SCLK_SRC_PLL1)
return 1; return 1;
else else
return 0; return 0;
...@@ -2271,16 +2452,6 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, ...@@ -2271,16 +2452,6 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0; unsigned int reg_val = 0;
if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src)
return 0;
if (rt5670->pdata.jd_mode) {
if (clk_id == RT5670_SCLK_S_PLL1)
snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
else
snd_soc_dapm_disable_pin(&codec->dapm, "PLL1");
snd_soc_dapm_sync(&codec->dapm);
}
switch (clk_id) { switch (clk_id) {
case RT5670_SCLK_S_MCLK: case RT5670_SCLK_S_MCLK:
reg_val |= RT5670_SCLK_SRC_MCLK; reg_val |= RT5670_SCLK_SRC_MCLK;
...@@ -2298,7 +2469,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, ...@@ -2298,7 +2469,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
snd_soc_update_bits(codec, RT5670_GLB_CLK, snd_soc_update_bits(codec, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, reg_val); RT5670_SCLK_SRC_MASK, reg_val);
rt5670->sysclk = freq; rt5670->sysclk = freq;
rt5670->sysclk_src = clk_id; if (clk_id != RT5670_SCLK_S_RCCLK)
rt5670->sysclk_src = clk_id;
dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
...@@ -2517,6 +2689,7 @@ static int rt5670_remove(struct snd_soc_codec *codec) ...@@ -2517,6 +2689,7 @@ static int rt5670_remove(struct snd_soc_codec *codec)
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
regmap_write(rt5670->regmap, RT5670_RESET, 0); regmap_write(rt5670->regmap, RT5670_RESET, 0);
snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio);
return 0; return 0;
} }
...@@ -2676,6 +2849,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, ...@@ -2676,6 +2849,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
if (dmi_check_system(dmi_platform_intel_braswell)) { if (dmi_check_system(dmi_platform_intel_braswell)) {
rt5670->pdata.dmic_en = true; rt5670->pdata.dmic_en = true;
rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
rt5670->pdata.dev_gpio = true;
rt5670->pdata.jd_mode = 1; rt5670->pdata.jd_mode = 1;
} }
...@@ -2717,12 +2891,17 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, ...@@ -2717,12 +2891,17 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5670->regmap, RT5670_IN2, regmap_update_bits(rt5670->regmap, RT5670_IN2,
RT5670_IN_DF2, RT5670_IN_DF2); RT5670_IN_DF2, RT5670_IN_DF2);
if (i2c->irq) { if (rt5670->pdata.dev_gpio) {
/* for push button */
regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000);
regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010);
regmap_write(rt5670->regmap, RT5670_IL_CMD3, 0x0014);
/* for irq */
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8);
} }
if (rt5670->pdata.jd_mode) { if (rt5670->pdata.jd_mode) {
......
...@@ -1988,6 +1988,8 @@ struct rt5670_priv { ...@@ -1988,6 +1988,8 @@ struct rt5670_priv {
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct rt5670_platform_data pdata; struct rt5670_platform_data pdata;
struct regmap *regmap; struct regmap *regmap;
struct snd_soc_jack *jack;
struct snd_soc_jack_gpio hp_gpio;
int sysclk; int sysclk;
int sysclk_src; int sysclk_src;
...@@ -2002,6 +2004,11 @@ struct rt5670_priv { ...@@ -2002,6 +2004,11 @@ struct rt5670_priv {
int dsp_sw; /* expected parameter setting */ int dsp_sw; /* expected parameter setting */
int dsp_rate; int dsp_rate;
int jack_type; int jack_type;
int jack_type_saved;
}; };
void rt5670_jack_suspend(struct snd_soc_codec *codec);
void rt5670_jack_resume(struct snd_soc_codec *codec);
int rt5670_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack);
#endif /* __RT5670_H__ */ #endif /* __RT5670_H__ */
# Core support # Core support
snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
sst-mfld-platform-compress.o sst-atom-controls.o
snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
# Platform Support # Platform Support
snd-soc-sst-haswell-pcm-objs := \ obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
snd-soc-sst-baytrail-pcm-objs := \ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += atom/
sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
# Machine support # Machine support
snd-soc-sst-haswell-objs := haswell.o obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/
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-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.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_BROADWELL_MACH) += snd-soc-sst-broadwell.o
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
# DSP driver
obj-$(CONFIG_SND_SST_IPC) += sst/
snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
sst-mfld-platform-compress.o sst-atom-controls.o
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
# DSP driver
obj-$(CONFIG_SND_SST_IPC) += sst/
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <asm/platform_sst_audio.h> #include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "sst.h" #include "sst.h"
#include "../sst-dsp.h" #include "../../common/sst-dsp.h"
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#include <acpi/actypes.h> #include <acpi/actypes.h>
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "../sst-dsp.h" #include "../../common/sst-dsp.h"
#include "sst.h" #include "sst.h"
struct sst_machines { struct sst_machines {
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <asm/platform_sst_audio.h> #include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "sst.h" #include "sst.h"
#include "../sst-dsp.h" #include "../../common/sst-dsp.h"
...@@ -381,7 +381,7 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, ...@@ -381,7 +381,7 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
tstamp->copied_total = fw_tstamp.ring_buffer_counter; tstamp->copied_total = fw_tstamp.ring_buffer_counter;
tstamp->pcm_frames = fw_tstamp.frames_decoded; tstamp->pcm_frames = fw_tstamp.frames_decoded;
tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
(u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24))); (u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24));
tstamp->sampling_rate = fw_tstamp.sampling_frequency; tstamp->sampling_rate = fw_tstamp.sampling_frequency;
dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames); dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames);
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <asm/platform_sst_audio.h> #include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "sst.h" #include "sst.h"
#include "../sst-dsp.h" #include "../../common/sst-dsp.h"
struct sst_block *sst_create_block(struct intel_sst_drv *ctx, struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
u32 msg_id, u32 drv_id) u32 msg_id, u32 drv_id)
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
#include <asm/platform_sst_audio.h> #include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "sst.h" #include "sst.h"
#include "../sst-dsp.h" #include "../../common/sst-dsp.h"
void memcpy32_toio(void __iomem *dst, const void *src, int count) void memcpy32_toio(void __iomem *dst, const void *src, int count)
{ {
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#include <asm/platform_sst_audio.h> #include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "sst.h" #include "sst.h"
#include "../sst-dsp.h" #include "../../common/sst-dsp.h"
int sst_shim_write(void __iomem *addr, int offset, int value) int sst_shim_write(void __iomem *addr, int offset, int value)
{ {
...@@ -111,30 +111,6 @@ int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, ...@@ -111,30 +111,6 @@ int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
} }
unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
{
unsigned long long val = 0;
switch (sst->dev_id) {
case SST_MRFLD_PCI_ID:
case SST_BYT_ACPI_ID:
val = sst_shim_read64(sst->shim, addr);
break;
}
return val;
}
void write_shim_data(struct intel_sst_drv *sst, int addr,
unsigned long long data)
{
switch (sst->dev_id) {
case SST_MRFLD_PCI_ID:
case SST_BYT_ACPI_ID:
sst_shim_write64(sst->shim, addr, (u64) data);
break;
}
}
/* /*
* sst_wait_timeout - wait on event for timeout * sst_wait_timeout - wait on event for timeout
* *
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#include <asm/platform_sst_audio.h> #include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "sst.h" #include "sst.h"
#include "../sst-dsp.h" #include "../../common/sst-dsp.h"
int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
{ {
......
snd-soc-sst-baytrail-pcm-objs := \
sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#include "sst-dsp-priv.h" #include "../common/sst-dsp-priv.h"
#include "sst-baytrail-ipc.h" #include "sst-baytrail-ipc.h"
#define SST_BYT_FW_SIGNATURE_SIZE 4 #define SST_BYT_FW_SIGNATURE_SIZE 4
......
...@@ -29,8 +29,9 @@ ...@@ -29,8 +29,9 @@
#include <asm/div64.h> #include <asm/div64.h>
#include "sst-baytrail-ipc.h" #include "sst-baytrail-ipc.h"
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#include "sst-dsp-priv.h" #include "../common/sst-dsp-priv.h"
#include "../common/sst-ipc.h"
/* IPC message timeout */ /* IPC message timeout */
#define IPC_TIMEOUT_MSECS 300 #define IPC_TIMEOUT_MSECS 300
...@@ -142,23 +143,6 @@ struct sst_byt_fw_init { ...@@ -142,23 +143,6 @@ struct sst_byt_fw_init {
u8 debug_info; u8 debug_info;
} __packed; } __packed;
/* driver internal IPC message structure */
struct ipc_message {
struct list_head list;
u64 header;
/* direction wrt host CPU */
char tx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
size_t tx_size;
char rx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
size_t rx_size;
wait_queue_head_t waitq;
bool complete;
bool wait;
int errno;
};
struct sst_byt_stream; struct sst_byt_stream;
struct sst_byt; struct sst_byt;
...@@ -195,14 +179,7 @@ struct sst_byt { ...@@ -195,14 +179,7 @@ struct sst_byt {
struct sst_fw *fw; struct sst_fw *fw;
/* IPC messaging */ /* IPC messaging */
struct list_head tx_list; struct sst_generic_ipc ipc;
struct list_head rx_list;
struct list_head empty_list;
wait_queue_head_t wait_txq;
struct task_struct *tx_thread;
struct kthread_worker kworker;
struct kthread_work kwork;
struct ipc_message *msg;
}; };
static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id) static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id)
...@@ -246,209 +223,6 @@ static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt, ...@@ -246,209 +223,6 @@ static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt,
return NULL; return NULL;
} }
static void sst_byt_ipc_shim_dbg(struct sst_byt *byt, const char *text)
{
struct sst_dsp *sst = byt->dsp;
u64 isr, ipcd, imrx, ipcx;
ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
dev_err(byt->dev,
"ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
text, ipcx, isr, ipcd, imrx);
}
/* locks held by caller */
static struct ipc_message *sst_byt_msg_get_empty(struct sst_byt *byt)
{
struct ipc_message *msg = NULL;
if (!list_empty(&byt->empty_list)) {
msg = list_first_entry(&byt->empty_list,
struct ipc_message, list);
list_del(&msg->list);
}
return msg;
}
static void sst_byt_ipc_tx_msgs(struct kthread_work *work)
{
struct sst_byt *byt =
container_of(work, struct sst_byt, kwork);
struct ipc_message *msg;
u64 ipcx;
unsigned long flags;
spin_lock_irqsave(&byt->dsp->spinlock, flags);
if (list_empty(&byt->tx_list)) {
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
return;
}
/* if the DSP is busy we will TX messages after IRQ */
ipcx = sst_dsp_shim_read64_unlocked(byt->dsp, SST_IPCX);
if (ipcx & SST_BYT_IPCX_BUSY) {
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
return;
}
msg = list_first_entry(&byt->tx_list, struct ipc_message, list);
list_move(&msg->list, &byt->rx_list);
/* send the message */
if (msg->header & IPC_HEADER_LARGE(true))
sst_dsp_outbox_write(byt->dsp, msg->tx_data, msg->tx_size);
sst_dsp_shim_write64_unlocked(byt->dsp, SST_IPCX, msg->header);
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
}
static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt,
struct ipc_message *msg)
{
msg->complete = true;
if (!msg->wait)
list_add_tail(&msg->list, &byt->empty_list);
else
wake_up(&msg->waitq);
}
static void sst_byt_drop_all(struct sst_byt *byt)
{
struct ipc_message *msg, *tmp;
unsigned long flags;
/* drop all TX and Rx messages before we stall + reset DSP */
spin_lock_irqsave(&byt->dsp->spinlock, flags);
list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) {
list_move(&msg->list, &byt->empty_list);
}
list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) {
list_move(&msg->list, &byt->empty_list);
}
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
}
static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg,
void *rx_data)
{
unsigned long flags;
int ret;
/* wait for DSP completion */
ret = wait_event_timeout(msg->waitq, msg->complete,
msecs_to_jiffies(IPC_TIMEOUT_MSECS));
spin_lock_irqsave(&byt->dsp->spinlock, flags);
if (ret == 0) {
list_del(&msg->list);
sst_byt_ipc_shim_dbg(byt, "message timeout");
ret = -ETIMEDOUT;
} else {
/* copy the data returned from DSP */
if (msg->rx_size)
memcpy(rx_data, msg->rx_data, msg->rx_size);
ret = msg->errno;
}
list_add_tail(&msg->list, &byt->empty_list);
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
return ret;
}
static int sst_byt_ipc_tx_message(struct sst_byt *byt, u64 header,
void *tx_data, size_t tx_bytes,
void *rx_data, size_t rx_bytes, int wait)
{
unsigned long flags;
struct ipc_message *msg;
spin_lock_irqsave(&byt->dsp->spinlock, flags);
msg = sst_byt_msg_get_empty(byt);
if (msg == NULL) {
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
return -EBUSY;
}
msg->header = header;
msg->tx_size = tx_bytes;
msg->rx_size = rx_bytes;
msg->wait = wait;
msg->errno = 0;
msg->complete = false;
if (tx_bytes) {
/* msg content = lower 32-bit of the header + data */
*(u32 *)msg->tx_data = (u32)(header & (u32)-1);
memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes);
msg->tx_size += sizeof(u32);
}
list_add_tail(&msg->list, &byt->tx_list);
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
queue_kthread_work(&byt->kworker, &byt->kwork);
if (wait)
return sst_byt_tx_wait_done(byt, msg, rx_data);
else
return 0;
}
static inline int sst_byt_ipc_tx_msg_wait(struct sst_byt *byt, u64 header,
void *tx_data, size_t tx_bytes,
void *rx_data, size_t rx_bytes)
{
return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
rx_data, rx_bytes, 1);
}
static inline int sst_byt_ipc_tx_msg_nowait(struct sst_byt *byt, u64 header,
void *tx_data, size_t tx_bytes)
{
return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
NULL, 0, 0);
}
static struct ipc_message *sst_byt_reply_find_msg(struct sst_byt *byt,
u64 header)
{
struct ipc_message *msg = NULL, *_msg;
u64 mask;
/* match reply to message sent based on msg and stream IDs */
mask = IPC_HEADER_MSG_ID_MASK |
IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
header &= mask;
if (list_empty(&byt->rx_list)) {
dev_err(byt->dev,
"ipc: rx list is empty but received 0x%llx\n", header);
goto out;
}
list_for_each_entry(_msg, &byt->rx_list, list) {
if ((_msg->header & mask) == header) {
msg = _msg;
break;
}
}
out:
return msg;
}
static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg) static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg)
{ {
struct sst_byt_stream *stream; struct sst_byt_stream *stream;
...@@ -477,7 +251,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header) ...@@ -477,7 +251,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
{ {
struct ipc_message *msg; struct ipc_message *msg;
msg = sst_byt_reply_find_msg(byt, header); msg = sst_ipc_reply_find_msg(&byt->ipc, header);
if (msg == NULL) if (msg == NULL)
return 1; return 1;
...@@ -491,7 +265,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header) ...@@ -491,7 +265,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
list_del(&msg->list); list_del(&msg->list);
/* wake up */ /* wake up */
sst_byt_tx_msg_reply_complete(byt, msg); sst_ipc_tx_msg_reply_complete(&byt->ipc, msg);
return 1; return 1;
} }
...@@ -538,6 +312,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context) ...@@ -538,6 +312,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context)
{ {
struct sst_dsp *sst = (struct sst_dsp *) context; struct sst_dsp *sst = (struct sst_dsp *) context;
struct sst_byt *byt = sst_dsp_get_thread_context(sst); struct sst_byt *byt = sst_dsp_get_thread_context(sst);
struct sst_generic_ipc *ipc = &byt->ipc;
u64 header; u64 header;
unsigned long flags; unsigned long flags;
...@@ -569,7 +344,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context) ...@@ -569,7 +344,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context)
spin_unlock_irqrestore(&sst->spinlock, flags); spin_unlock_irqrestore(&sst->spinlock, flags);
/* continue to send any remaining messages... */ /* continue to send any remaining messages... */
queue_kthread_work(&byt->kworker, &byt->kwork); queue_kthread_work(&ipc->kworker, &ipc->kwork);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -656,7 +431,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) ...@@ -656,7 +431,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream)
header = sst_byt_header(IPC_IA_ALLOC_STREAM, header = sst_byt_header(IPC_IA_ALLOC_STREAM,
sizeof(*str_req) + sizeof(u32), sizeof(*str_req) + sizeof(u32),
true, stream->str_id); true, stream->str_id);
ret = sst_byt_ipc_tx_msg_wait(byt, header, str_req, sizeof(*str_req), ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req,
sizeof(*str_req),
reply, sizeof(*reply)); reply, sizeof(*reply));
if (ret < 0) { if (ret < 0) {
dev_err(byt->dev, "ipc: error stream commit failed\n"); dev_err(byt->dev, "ipc: error stream commit failed\n");
...@@ -679,7 +455,7 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) ...@@ -679,7 +455,7 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
goto out; goto out;
header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id);
ret = sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0);
if (ret < 0) { if (ret < 0) {
dev_err(byt->dev, "ipc: free stream %d failed\n", dev_err(byt->dev, "ipc: free stream %d failed\n",
stream->str_id); stream->str_id);
...@@ -703,9 +479,11 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type, ...@@ -703,9 +479,11 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type,
header = sst_byt_header(type, 0, false, stream_id); header = sst_byt_header(type, 0, false, stream_id);
if (wait) if (wait)
return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); return sst_ipc_tx_message_wait(&byt->ipc, header, NULL,
0, NULL, 0);
else else
return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0); return sst_ipc_tx_message_nowait(&byt->ipc, header,
NULL, 0);
} }
/* stream ALSA trigger operations */ /* stream ALSA trigger operations */
...@@ -725,7 +503,7 @@ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, ...@@ -725,7 +503,7 @@ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
tx_msg = &start_stream; tx_msg = &start_stream;
size = sizeof(start_stream); size = sizeof(start_stream);
ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size); ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size);
if (ret < 0) if (ret < 0)
dev_err(byt->dev, "ipc: error failed to start stream %d\n", dev_err(byt->dev, "ipc: error failed to start stream %d\n",
stream->str_id); stream->str_id);
...@@ -790,23 +568,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt, ...@@ -790,23 +568,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt,
return do_div(fw_tstamp.ring_buffer_counter, buffer_size); return do_div(fw_tstamp.ring_buffer_counter, buffer_size);
} }
static int msg_empty_list_init(struct sst_byt *byt)
{
struct ipc_message *msg;
int i;
byt->msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
if (byt->msg == NULL)
return -ENOMEM;
for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
init_waitqueue_head(&byt->msg[i].waitq);
list_add(&byt->msg[i].list, &byt->empty_list);
}
return 0;
}
struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt) struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt)
{ {
return byt->dsp; return byt->dsp;
...@@ -823,7 +584,7 @@ int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) ...@@ -823,7 +584,7 @@ int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
dev_dbg(byt->dev, "dsp reset\n"); dev_dbg(byt->dev, "dsp reset\n");
sst_dsp_reset(byt->dsp); sst_dsp_reset(byt->dsp);
sst_byt_drop_all(byt); sst_ipc_drop_all(&byt->ipc);
dev_dbg(byt->dev, "dsp in reset\n"); dev_dbg(byt->dev, "dsp in reset\n");
dev_dbg(byt->dev, "free all blocks and unload fw\n"); dev_dbg(byt->dev, "free all blocks and unload fw\n");
...@@ -876,9 +637,52 @@ int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) ...@@ -876,9 +637,52 @@ int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
} }
EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
{
if (msg->header & IPC_HEADER_LARGE(true))
sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header);
}
static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
{
struct sst_dsp *sst = ipc->dsp;
u64 isr, ipcd, imrx, ipcx;
ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
dev_err(ipc->dev,
"ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
text, ipcx, isr, ipcd, imrx);
}
static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data,
size_t tx_size)
{
/* msg content = lower 32-bit of the header + data */
*(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1);
memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size);
msg->tx_size += sizeof(u32);
}
static u64 byt_reply_msg_match(u64 header, u64 *mask)
{
/* match reply to message sent based on msg and stream IDs */
*mask = IPC_HEADER_MSG_ID_MASK |
IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
header &= *mask;
return header;
}
int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
{ {
struct sst_byt *byt; struct sst_byt *byt;
struct sst_generic_ipc *ipc;
struct sst_fw *byt_sst_fw; struct sst_fw *byt_sst_fw;
struct sst_byt_fw_init init; struct sst_byt_fw_init init;
int err; int err;
...@@ -889,39 +693,30 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -889,39 +693,30 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
if (byt == NULL) if (byt == NULL)
return -ENOMEM; return -ENOMEM;
byt->dev = dev; ipc = &byt->ipc;
INIT_LIST_HEAD(&byt->stream_list); ipc->dev = dev;
INIT_LIST_HEAD(&byt->tx_list); ipc->ops.tx_msg = byt_tx_msg;
INIT_LIST_HEAD(&byt->rx_list); ipc->ops.shim_dbg = byt_shim_dbg;
INIT_LIST_HEAD(&byt->empty_list); ipc->ops.tx_data_copy = byt_tx_data_copy;
init_waitqueue_head(&byt->boot_wait); ipc->ops.reply_msg_match = byt_reply_msg_match;
init_waitqueue_head(&byt->wait_txq);
err = msg_empty_list_init(byt); err = sst_ipc_init(ipc);
if (err < 0) if (err != 0)
return -ENOMEM; goto ipc_init_err;
/* start the IPC message thread */
init_kthread_worker(&byt->kworker);
byt->tx_thread = kthread_run(kthread_worker_fn,
&byt->kworker, "%s",
dev_name(byt->dev));
if (IS_ERR(byt->tx_thread)) {
err = PTR_ERR(byt->tx_thread);
dev_err(byt->dev, "error failed to create message TX task\n");
goto err_free_msg;
}
init_kthread_work(&byt->kwork, sst_byt_ipc_tx_msgs);
INIT_LIST_HEAD(&byt->stream_list);
init_waitqueue_head(&byt->boot_wait);
byt_dev.thread_context = byt; byt_dev.thread_context = byt;
/* init SST shim */ /* init SST shim */
byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
if (byt->dsp == NULL) { if (byt->dsp == NULL) {
err = -ENODEV; err = -ENODEV;
goto dsp_err; goto dsp_new_err;
} }
ipc->dsp = byt->dsp;
/* keep the DSP in reset state for base FW loading */ /* keep the DSP in reset state for base FW loading */
sst_dsp_reset(byt->dsp); sst_dsp_reset(byt->dsp);
...@@ -961,10 +756,10 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -961,10 +756,10 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
sst_fw_free(byt_sst_fw); sst_fw_free(byt_sst_fw);
fw_err: fw_err:
sst_dsp_free(byt->dsp); sst_dsp_free(byt->dsp);
dsp_err: dsp_new_err:
kthread_stop(byt->tx_thread); sst_ipc_fini(ipc);
err_free_msg: ipc_init_err:
kfree(byt->msg); kfree(byt);
return err; return err;
} }
...@@ -977,7 +772,6 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) ...@@ -977,7 +772,6 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
sst_dsp_reset(byt->dsp); sst_dsp_reset(byt->dsp);
sst_fw_free_all(byt->dsp); sst_fw_free_all(byt->dsp);
sst_dsp_free(byt->dsp); sst_dsp_free(byt->dsp);
kthread_stop(byt->tx_thread); sst_ipc_fini(&byt->ipc);
kfree(byt->msg);
} }
EXPORT_SYMBOL_GPL(sst_byt_dsp_free); EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include "sst-baytrail-ipc.h" #include "sst-baytrail-ipc.h"
#include "sst-dsp-priv.h" #include "../common/sst-dsp-priv.h"
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#define BYT_PCM_COUNT 2 #define BYT_PCM_COUNT 2
......
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-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.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_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_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
#include <sound/jack.h> #include <sound/jack.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#include "sst-haswell-ipc.h" #include "../haswell/sst-haswell-ipc.h"
#include "../codecs/rt286.h" #include "../../codecs/rt286.h"
static struct snd_soc_jack broadwell_headset; static struct snd_soc_jack broadwell_headset;
/* Headset jack detection DAPM pins */ /* Headset jack detection DAPM pins */
...@@ -219,6 +219,32 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { ...@@ -219,6 +219,32 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
}, },
}; };
static int broadwell_suspend(struct snd_soc_card *card){
struct snd_soc_codec *codec;
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
rt286_mic_detect(codec, NULL);
break;
}
}
return 0;
}
static int broadwell_resume(struct snd_soc_card *card){
struct snd_soc_codec *codec;
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
dev_dbg(codec->dev, "enabling jack detect for resume.\n");
rt286_mic_detect(codec, &broadwell_headset);
break;
}
}
return 0;
}
/* broadwell audio machine driver for WPT + RT286S */ /* broadwell audio machine driver for WPT + RT286S */
static struct snd_soc_card broadwell_rt286 = { static struct snd_soc_card broadwell_rt286 = {
.name = "broadwell-rt286", .name = "broadwell-rt286",
...@@ -232,6 +258,8 @@ static struct snd_soc_card broadwell_rt286 = { ...@@ -232,6 +258,8 @@ static struct snd_soc_card broadwell_rt286 = {
.dapm_routes = broadwell_rt286_map, .dapm_routes = broadwell_rt286_map,
.num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map), .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
.fully_routed = true, .fully_routed = true,
.suspend_pre = broadwell_suspend,
.resume_post = broadwell_resume,
}; };
static int broadwell_audio_probe(struct platform_device *pdev) static int broadwell_audio_probe(struct platform_device *pdev)
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/jack.h> #include <sound/jack.h>
#include "../codecs/max98090.h" #include "../../codecs/max98090.h"
struct byt_max98090_private { struct byt_max98090_private {
struct snd_soc_jack jack; struct snd_soc_jack jack;
......
...@@ -23,9 +23,9 @@ ...@@ -23,9 +23,9 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/jack.h> #include <sound/jack.h>
#include "../codecs/rt5640.h" #include "../../codecs/rt5640.h"
#include "sst-dsp.h" #include "../common/sst-dsp.h"
static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_HP("Headphone", NULL),
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include "../codecs/rt5640.h" #include "../../codecs/rt5640.h"
#include "sst-atom-controls.h" #include "../atom/sst-atom-controls.h"
static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_HP("Headphone", NULL),
......
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/jack.h> #include <sound/jack.h>
#include "../codecs/rt5645.h" #include "../../codecs/rt5645.h"
#include "sst-atom-controls.h" #include "../atom/sst-atom-controls.h"
#define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "rt5645-aif1" #define CHT_CODEC_DAI "rt5645-aif1"
......
...@@ -22,13 +22,28 @@ ...@@ -22,13 +22,28 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include "../codecs/rt5670.h" #include <sound/jack.h>
#include "sst-atom-controls.h" #include "../../codecs/rt5670.h"
#include "../atom/sst-atom-controls.h"
/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
#define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "rt5670-aif1" #define CHT_CODEC_DAI "rt5670-aif1"
static struct snd_soc_jack cht_bsw_headset;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
};
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
{ {
int i; int i;
...@@ -50,6 +65,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, ...@@ -50,6 +65,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card; struct snd_soc_card *card = dapm->card;
struct snd_soc_dai *codec_dai; struct snd_soc_dai *codec_dai;
int ret;
codec_dai = cht_get_codec_dai(card); codec_dai = cht_get_codec_dai(card);
if (!codec_dai) { if (!codec_dai) {
...@@ -57,17 +73,31 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, ...@@ -57,17 +73,31 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
return -EIO; return -EIO;
} }
if (!SND_SOC_DAPM_EVENT_OFF(event)) if (SND_SOC_DAPM_EVENT_ON(event)) {
return 0; /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
/* Set codec sysclk source to its internal clock because codec PLL will CHT_PLAT_CLK_3_HZ, 48000 * 512);
* be off when idle and MCLK will also be off by ACPI when codec is if (ret < 0) {
* runtime suspended. Codec needs clock for jack detection and button dev_err(card->dev, "can't set codec pll: %d\n", ret);
* press. return ret;
*/ }
snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
0, SND_SOC_CLOCK_IN); /* set codec sysclk source to PLL */
ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
48000 * 512, SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
return ret;
}
} else {
/* Set codec sysclk source to its internal clock because codec
* PLL will be off when idle and MCLK will also be off by ACPI
* when codec is runtime suspended. Codec needs clock for jack
* detection and button press.
*/
snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
48000 * 512, SND_SOC_CLOCK_IN);
}
return 0; return 0;
} }
...@@ -77,7 +107,8 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { ...@@ -77,7 +107,8 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
platform_clock_control, SND_SOC_DAPM_POST_PMD), platform_clock_control, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD),
}; };
static const struct snd_soc_dapm_route cht_audio_map[] = { static const struct snd_soc_dapm_route cht_audio_map[] = {
...@@ -162,6 +193,15 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) ...@@ -162,6 +193,15 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
| RT5670_AD_MONO_L_FILTER | RT5670_AD_MONO_L_FILTER
| RT5670_AD_MONO_R_FILTER, | RT5670_AD_MONO_R_FILTER,
RT5670_CLK_SEL_I2S1_ASRC); RT5670_CLK_SEL_I2S1_ASRC);
ret = snd_soc_card_jack_new(runtime->card, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset,
cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins));
if (ret)
return ret;
rt5670_set_jack_detect(codec, &cht_bsw_headset);
return 0; return 0;
} }
...@@ -251,6 +291,35 @@ static struct snd_soc_dai_link cht_dailink[] = { ...@@ -251,6 +291,35 @@ static struct snd_soc_dai_link cht_dailink[] = {
}, },
}; };
static int cht_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
rt5670_jack_suspend(codec);
break;
}
}
return 0;
}
static int cht_resume_post(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
dev_dbg(codec->dev, "enabling jack detect for resume.\n");
rt5670_jack_resume(codec);
break;
}
}
return 0;
}
/* SoC card */ /* SoC card */
static struct snd_soc_card snd_soc_card_cht = { static struct snd_soc_card snd_soc_card_cht = {
.name = "cherrytrailcraudio", .name = "cherrytrailcraudio",
...@@ -262,6 +331,8 @@ static struct snd_soc_card snd_soc_card_cht = { ...@@ -262,6 +331,8 @@ static struct snd_soc_card snd_soc_card_cht = {
.num_dapm_routes = ARRAY_SIZE(cht_audio_map), .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
.controls = cht_mc_controls, .controls = cht_mc_controls,
.num_controls = ARRAY_SIZE(cht_mc_controls), .num_controls = ARRAY_SIZE(cht_mc_controls),
.suspend_pre = cht_suspend_pre,
.resume_post = cht_resume_post,
}; };
static int snd_cht_mc_probe(struct platform_device *pdev) static int snd_cht_mc_probe(struct platform_device *pdev)
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#include "sst-haswell-ipc.h" #include "../haswell/sst-haswell-ipc.h"
#include "../codecs/rt5640.h" #include "../../codecs/rt5640.h"
/* Haswell ULT platforms have a Headphone and Mic jack */ /* Haswell ULT platforms have a Headphone and Mic jack */
static const struct snd_soc_dapm_widget haswell_widgets[] = { static const struct snd_soc_dapm_widget haswell_widgets[] = {
......
snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-ipc-objs := sst-ipc.o
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
...@@ -172,6 +172,16 @@ struct sst_module_runtime_context { ...@@ -172,6 +172,16 @@ struct sst_module_runtime_context {
u32 *buffer; u32 *buffer;
}; };
/*
* Audio DSP Module State
*/
enum sst_module_state {
SST_MODULE_STATE_UNLOADED = 0, /* default state */
SST_MODULE_STATE_LOADED,
SST_MODULE_STATE_INITIALIZED, /* and inactive */
SST_MODULE_STATE_ACTIVE,
};
/* /*
* Audio DSP Generic Module. * Audio DSP Generic Module.
* *
...@@ -203,6 +213,9 @@ struct sst_module { ...@@ -203,6 +213,9 @@ struct sst_module {
struct list_head list; /* DSP list of modules */ struct list_head list; /* DSP list of modules */
struct list_head list_fw; /* FW list of modules */ struct list_head list_fw; /* FW list of modules */
struct list_head runtime_list; /* list of runtime module objects*/ struct list_head runtime_list; /* list of runtime module objects*/
/* state */
enum sst_module_state state;
}; };
/* /*
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
/* Supported SST DMA Devices */ /* Supported SST DMA Devices */
#define SST_DMA_TYPE_DW 1 #define SST_DMA_TYPE_DW 1
#define SST_DMA_TYPE_MID 2
/* autosuspend delay 5s*/ /* autosuspend delay 5s*/
#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000) #define SST_RUNTIME_SUSPEND_DELAY (5 * 1000)
......
...@@ -221,8 +221,6 @@ int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) ...@@ -221,8 +221,6 @@ int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
dma_cap_mask_t mask; dma_cap_mask_t mask;
int ret; int ret;
/* The Intel MID DMA engine driver needs the slave config set but
* Synopsis DMA engine driver safely ignores the slave config */
dma_cap_zero(mask); dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_SLAVE, mask);
dma_cap_set(DMA_MEMCPY, mask); dma_cap_set(DMA_MEMCPY, mask);
...@@ -281,9 +279,6 @@ int sst_dma_new(struct sst_dsp *sst) ...@@ -281,9 +279,6 @@ int sst_dma_new(struct sst_dsp *sst)
case SST_DMA_TYPE_DW: case SST_DMA_TYPE_DW:
dma_dev_name = "dw_dmac"; dma_dev_name = "dw_dmac";
break; break;
case SST_DMA_TYPE_MID:
dma_dev_name = "Intel MID DMA";
break;
default: default:
dev_err(sst->dev, "error: invalid DMA engine %d\n", dev_err(sst->dev, "error: invalid DMA engine %d\n",
sst->pdata->dma_engine); sst->pdata->dma_engine);
...@@ -502,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw, ...@@ -502,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
sst_module->scratch_size = template->scratch_size; sst_module->scratch_size = template->scratch_size;
sst_module->persistent_size = template->persistent_size; sst_module->persistent_size = template->persistent_size;
sst_module->entry = template->entry; sst_module->entry = template->entry;
sst_module->state = SST_MODULE_STATE_UNLOADED;
INIT_LIST_HEAD(&sst_module->block_list); INIT_LIST_HEAD(&sst_module->block_list);
INIT_LIST_HEAD(&sst_module->runtime_list); INIT_LIST_HEAD(&sst_module->runtime_list);
......
/*
* Intel SST generic IPC Support
*
* Copyright (C) 2015, Intel Corporation. All rights reserved.
*
* 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/types.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <sound/asound.h>
#include "sst-dsp.h"
#include "sst-dsp-priv.h"
#include "sst-ipc.h"
/* IPC message timeout (msecs) */
#define IPC_TIMEOUT_MSECS 300
#define IPC_EMPTY_LIST_SIZE 8
/* locks held by caller */
static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc)
{
struct ipc_message *msg = NULL;
if (!list_empty(&ipc->empty_list)) {
msg = list_first_entry(&ipc->empty_list, struct ipc_message,
list);
list_del(&msg->list);
}
return msg;
}
static int tx_wait_done(struct sst_generic_ipc *ipc,
struct ipc_message *msg, void *rx_data)
{
unsigned long flags;
int ret;
/* wait for DSP completion (in all cases atm inc pending) */
ret = wait_event_timeout(msg->waitq, msg->complete,
msecs_to_jiffies(IPC_TIMEOUT_MSECS));
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
if (ret == 0) {
if (ipc->ops.shim_dbg != NULL)
ipc->ops.shim_dbg(ipc, "message timeout");
list_del(&msg->list);
ret = -ETIMEDOUT;
} else {
/* copy the data returned from DSP */
if (msg->rx_size)
memcpy(rx_data, msg->rx_data, msg->rx_size);
ret = msg->errno;
}
list_add_tail(&msg->list, &ipc->empty_list);
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return ret;
}
static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes, void *rx_data,
size_t rx_bytes, int wait)
{
struct ipc_message *msg;
unsigned long flags;
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
msg = msg_get_empty(ipc);
if (msg == NULL) {
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return -EBUSY;
}
msg->header = header;
msg->tx_size = tx_bytes;
msg->rx_size = rx_bytes;
msg->wait = wait;
msg->errno = 0;
msg->pending = false;
msg->complete = false;
if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL))
ipc->ops.tx_data_copy(msg, tx_data, tx_bytes);
list_add_tail(&msg->list, &ipc->tx_list);
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
queue_kthread_work(&ipc->kworker, &ipc->kwork);
if (wait)
return tx_wait_done(ipc, msg, rx_data);
else
return 0;
}
static int msg_empty_list_init(struct sst_generic_ipc *ipc)
{
int i;
ipc->msg = kzalloc(sizeof(struct ipc_message) *
IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
if (ipc->msg == NULL)
return -ENOMEM;
for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
init_waitqueue_head(&ipc->msg[i].waitq);
list_add(&ipc->msg[i].list, &ipc->empty_list);
}
return 0;
}
static void ipc_tx_msgs(struct kthread_work *work)
{
struct sst_generic_ipc *ipc =
container_of(work, struct sst_generic_ipc, kwork);
struct ipc_message *msg;
unsigned long flags;
u64 ipcx;
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
if (list_empty(&ipc->tx_list) || ipc->pending) {
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return;
}
/* if the DSP is busy, we will TX messages after IRQ.
* also postpone if we are in the middle of procesing completion irq*/
ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX);
if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return;
}
msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
list_move(&msg->list, &ipc->rx_list);
if (ipc->ops.tx_msg != NULL)
ipc->ops.tx_msg(ipc, msg);
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
}
int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
{
return ipc_tx_message(ipc, header, tx_data, tx_bytes,
rx_data, rx_bytes, 1);
}
EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes)
{
return ipc_tx_message(ipc, header, tx_data, tx_bytes,
NULL, 0, 0);
}
EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
u64 header)
{
struct ipc_message *msg;
u64 mask;
if (ipc->ops.reply_msg_match != NULL)
header = ipc->ops.reply_msg_match(header, &mask);
if (list_empty(&ipc->rx_list)) {
dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n",
header);
return NULL;
}
list_for_each_entry(msg, &ipc->rx_list, list) {
if ((msg->header & mask) == header)
return msg;
}
return NULL;
}
EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg);
/* locks held by caller */
void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
struct ipc_message *msg)
{
msg->complete = true;
if (!msg->wait)
list_add_tail(&msg->list, &ipc->empty_list);
else
wake_up(&msg->waitq);
}
EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete);
void sst_ipc_drop_all(struct sst_generic_ipc *ipc)
{
struct ipc_message *msg, *tmp;
unsigned long flags;
int tx_drop_cnt = 0, rx_drop_cnt = 0;
/* drop all TX and Rx messages before we stall + reset DSP */
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) {
list_move(&msg->list, &ipc->empty_list);
tx_drop_cnt++;
}
list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) {
list_move(&msg->list, &ipc->empty_list);
rx_drop_cnt++;
}
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
if (tx_drop_cnt || rx_drop_cnt)
dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n",
tx_drop_cnt, rx_drop_cnt);
}
EXPORT_SYMBOL_GPL(sst_ipc_drop_all);
int sst_ipc_init(struct sst_generic_ipc *ipc)
{
int ret;
INIT_LIST_HEAD(&ipc->tx_list);
INIT_LIST_HEAD(&ipc->rx_list);
INIT_LIST_HEAD(&ipc->empty_list);
init_waitqueue_head(&ipc->wait_txq);
ret = msg_empty_list_init(ipc);
if (ret < 0)
return -ENOMEM;
/* start the IPC message thread */
init_kthread_worker(&ipc->kworker);
ipc->tx_thread = kthread_run(kthread_worker_fn,
&ipc->kworker, "%s",
dev_name(ipc->dev));
if (IS_ERR(ipc->tx_thread)) {
dev_err(ipc->dev, "error: failed to create message TX task\n");
ret = PTR_ERR(ipc->tx_thread);
kfree(ipc->msg);
return ret;
}
init_kthread_work(&ipc->kwork, ipc_tx_msgs);
return 0;
}
EXPORT_SYMBOL_GPL(sst_ipc_init);
void sst_ipc_fini(struct sst_generic_ipc *ipc)
{
if (ipc->tx_thread)
kthread_stop(ipc->tx_thread);
if (ipc->msg)
kfree(ipc->msg);
}
EXPORT_SYMBOL_GPL(sst_ipc_fini);
/* Module information */
MODULE_AUTHOR("Jin Yao");
MODULE_DESCRIPTION("Intel SST IPC generic");
MODULE_LICENSE("GPL v2");
/*
* Intel SST generic IPC Support
*
* Copyright (C) 2015, Intel Corporation. All rights reserved.
*
* 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.
*
*/
#ifndef __SST_GENERIC_IPC_H
#define __SST_GENERIC_IPC_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#define IPC_MAX_MAILBOX_BYTES 256
struct ipc_message {
struct list_head list;
u64 header;
/* direction wrt host CPU */
char tx_data[IPC_MAX_MAILBOX_BYTES];
size_t tx_size;
char rx_data[IPC_MAX_MAILBOX_BYTES];
size_t rx_size;
wait_queue_head_t waitq;
bool pending;
bool complete;
bool wait;
int errno;
};
struct sst_generic_ipc;
struct sst_plat_ipc_ops {
void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *);
void (*shim_dbg)(struct sst_generic_ipc *, const char *);
void (*tx_data_copy)(struct ipc_message *, char *, size_t);
u64 (*reply_msg_match)(u64 header, u64 *mask);
};
/* SST generic IPC data */
struct sst_generic_ipc {
struct device *dev;
struct sst_dsp *dsp;
/* IPC messaging */
struct list_head tx_list;
struct list_head rx_list;
struct list_head empty_list;
wait_queue_head_t wait_txq;
struct task_struct *tx_thread;
struct kthread_worker kworker;
struct kthread_work kwork;
bool pending;
struct ipc_message *msg;
struct sst_plat_ipc_ops ops;
};
int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes);
int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes);
struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
u64 header);
void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
struct ipc_message *msg);
void sst_ipc_drop_all(struct sst_generic_ipc *ipc);
int sst_ipc_init(struct sst_generic_ipc *ipc);
void sst_ipc_fini(struct sst_generic_ipc *ipc);
#endif
snd-soc-sst-haswell-pcm-objs := \
sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
...@@ -28,9 +28,9 @@ ...@@ -28,9 +28,9 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#include "sst-dsp-priv.h" #include "../common/sst-dsp-priv.h"
#include "sst-haswell-ipc.h" #include "../haswell/sst-haswell-ipc.h"
#include <trace/events/hswadsp.h> #include <trace/events/hswadsp.h>
...@@ -100,6 +100,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, ...@@ -100,6 +100,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
&& module->type != SST_HSW_MODULE_PCM && module->type != SST_HSW_MODULE_PCM
&& module->type != SST_HSW_MODULE_PCM_REFERENCE && module->type != SST_HSW_MODULE_PCM_REFERENCE
&& module->type != SST_HSW_MODULE_PCM_CAPTURE && module->type != SST_HSW_MODULE_PCM_CAPTURE
&& module->type != SST_HSW_MODULE_WAVES
&& module->type != SST_HSW_MODULE_LPAL) && module->type != SST_HSW_MODULE_LPAL)
return 0; return 0;
...@@ -139,6 +140,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, ...@@ -139,6 +140,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
mod->type = SST_MEM_IRAM; mod->type = SST_MEM_IRAM;
break; break;
case SST_HSW_DRAM: case SST_HSW_DRAM:
case SST_HSW_REGS:
ram = dsp->addr.lpe; ram = dsp->addr.lpe;
mod->offset = block->ram_offset; mod->offset = block->ram_offset;
mod->type = SST_MEM_DRAM; mod->type = SST_MEM_DRAM;
...@@ -169,6 +171,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, ...@@ -169,6 +171,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
block = (void *)block + sizeof(*block) + block->size; block = (void *)block + sizeof(*block) + block->size;
} }
mod->state = SST_MODULE_STATE_LOADED;
return 0; return 0;
} }
......
...@@ -34,8 +34,9 @@ ...@@ -34,8 +34,9 @@
#include <sound/asound.h> #include <sound/asound.h>
#include "sst-haswell-ipc.h" #include "sst-haswell-ipc.h"
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#include "sst-dsp-priv.h" #include "../common/sst-dsp-priv.h"
#include "../common/sst-ipc.h"
/* Global Message - Generic */ /* Global Message - Generic */
#define IPC_GLB_TYPE_SHIFT 24 #define IPC_GLB_TYPE_SHIFT 24
...@@ -79,6 +80,15 @@ ...@@ -79,6 +80,15 @@
#define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT) #define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT)
#define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT) #define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT)
/* Module Message */
#define IPC_MODULE_OPERATION_SHIFT 20
#define IPC_MODULE_OPERATION_MASK (0xf << IPC_MODULE_OPERATION_SHIFT)
#define IPC_MODULE_OPERATION(x) (x << IPC_MODULE_OPERATION_SHIFT)
#define IPC_MODULE_ID_SHIFT 16
#define IPC_MODULE_ID_MASK (0xf << IPC_MODULE_ID_SHIFT)
#define IPC_MODULE_ID(x) (x << IPC_MODULE_ID_SHIFT)
/* IPC message timeout (msecs) */ /* IPC message timeout (msecs) */
#define IPC_TIMEOUT_MSECS 300 #define IPC_TIMEOUT_MSECS 300
#define IPC_BOOT_MSECS 200 #define IPC_BOOT_MSECS 200
...@@ -115,6 +125,7 @@ enum ipc_glb_type { ...@@ -115,6 +125,7 @@ enum ipc_glb_type {
IPC_GLB_ENTER_DX_STATE = 12, IPC_GLB_ENTER_DX_STATE = 12,
IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */ IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */
IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */ IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */
IPC_GLB_MODULE_OPERATION = 15, /* Message to loadable fw module */
IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */ IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */
IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */ IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */
}; };
...@@ -133,6 +144,16 @@ enum ipc_glb_reply { ...@@ -133,6 +144,16 @@ enum ipc_glb_reply {
IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */ IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */
}; };
enum ipc_module_operation {
IPC_MODULE_NOTIFICATION = 0,
IPC_MODULE_ENABLE = 1,
IPC_MODULE_DISABLE = 2,
IPC_MODULE_GET_PARAMETER = 3,
IPC_MODULE_SET_PARAMETER = 4,
IPC_MODULE_GET_INFO = 5,
IPC_MODULE_MAX_MESSAGE
};
/* Stream Message - Types */ /* Stream Message - Types */
enum ipc_str_operation { enum ipc_str_operation {
IPC_STR_RESET = 0, IPC_STR_RESET = 0,
...@@ -190,23 +211,6 @@ struct sst_hsw_ipc_fw_ready { ...@@ -190,23 +211,6 @@ struct sst_hsw_ipc_fw_ready {
u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)]; u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
} __attribute__((packed)); } __attribute__((packed));
struct ipc_message {
struct list_head list;
u32 header;
/* direction wrt host CPU */
char tx_data[IPC_MAX_MAILBOX_BYTES];
size_t tx_size;
char rx_data[IPC_MAX_MAILBOX_BYTES];
size_t rx_size;
wait_queue_head_t waitq;
bool pending;
bool complete;
bool wait;
int errno;
};
struct sst_hsw_stream; struct sst_hsw_stream;
struct sst_hsw; struct sst_hsw;
...@@ -305,18 +309,19 @@ struct sst_hsw { ...@@ -305,18 +309,19 @@ struct sst_hsw {
bool shutdown; bool shutdown;
/* IPC messaging */ /* IPC messaging */
struct list_head tx_list; struct sst_generic_ipc ipc;
struct list_head rx_list;
struct list_head empty_list;
wait_queue_head_t wait_txq;
struct task_struct *tx_thread;
struct kthread_worker kworker;
struct kthread_work kwork;
bool pending;
struct ipc_message *msg;
/* FW log stream */ /* FW log stream */
struct sst_hsw_log_stream log_stream; struct sst_hsw_log_stream log_stream;
/* flags bit field to track module state when resume from RTD3,
* each bit represent state (enabled/disabled) of single module */
u32 enabled_modules_rtd3;
/* buffer to store parameter lines */
u32 param_idx_w; /* write index */
u32 param_idx_r; /* read index */
u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT];
}; };
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
...@@ -352,6 +357,16 @@ static inline u32 msg_get_notify_reason(u32 msg) ...@@ -352,6 +357,16 @@ static inline u32 msg_get_notify_reason(u32 msg)
return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
} }
static inline u32 msg_get_module_operation(u32 msg)
{
return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT;
}
static inline u32 msg_get_module_id(u32 msg)
{
return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT;
}
u32 create_channel_map(enum sst_hsw_channel_config config) u32 create_channel_map(enum sst_hsw_channel_config config)
{ {
switch (config) { switch (config) {
...@@ -417,159 +432,6 @@ static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw, ...@@ -417,159 +432,6 @@ static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw,
return NULL; return NULL;
} }
static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text)
{
struct sst_dsp *sst = hsw->dsp;
u32 isr, ipcd, imrx, ipcx;
ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
text, ipcx, isr, ipcd, imrx);
}
/* locks held by caller */
static struct ipc_message *msg_get_empty(struct sst_hsw *hsw)
{
struct ipc_message *msg = NULL;
if (!list_empty(&hsw->empty_list)) {
msg = list_first_entry(&hsw->empty_list, struct ipc_message,
list);
list_del(&msg->list);
}
return msg;
}
static void ipc_tx_msgs(struct kthread_work *work)
{
struct sst_hsw *hsw =
container_of(work, struct sst_hsw, kwork);
struct ipc_message *msg;
unsigned long flags;
u32 ipcx;
spin_lock_irqsave(&hsw->dsp->spinlock, flags);
if (list_empty(&hsw->tx_list) || hsw->pending) {
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
return;
}
/* if the DSP is busy, we will TX messages after IRQ.
* also postpone if we are in the middle of procesing completion irq*/
ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX);
if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
return;
}
msg = list_first_entry(&hsw->tx_list, struct ipc_message, list);
list_move(&msg->list, &hsw->rx_list);
/* send the message */
sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size);
sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY);
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
}
/* locks held by caller */
static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg)
{
msg->complete = true;
trace_ipc_reply("completed", msg->header);
if (!msg->wait)
list_add_tail(&msg->list, &hsw->empty_list);
else
wake_up(&msg->waitq);
}
static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
void *rx_data)
{
unsigned long flags;
int ret;
/* wait for DSP completion (in all cases atm inc pending) */
ret = wait_event_timeout(msg->waitq, msg->complete,
msecs_to_jiffies(IPC_TIMEOUT_MSECS));
spin_lock_irqsave(&hsw->dsp->spinlock, flags);
if (ret == 0) {
ipc_shim_dbg(hsw, "message timeout");
trace_ipc_error("error message timeout for", msg->header);
list_del(&msg->list);
ret = -ETIMEDOUT;
} else {
/* copy the data returned from DSP */
if (msg->rx_size)
memcpy(rx_data, msg->rx_data, msg->rx_size);
ret = msg->errno;
}
list_add_tail(&msg->list, &hsw->empty_list);
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
return ret;
}
static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data,
size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait)
{
struct ipc_message *msg;
unsigned long flags;
spin_lock_irqsave(&hsw->dsp->spinlock, flags);
msg = msg_get_empty(hsw);
if (msg == NULL) {
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
return -EBUSY;
}
if (tx_bytes)
memcpy(msg->tx_data, tx_data, tx_bytes);
msg->header = header;
msg->tx_size = tx_bytes;
msg->rx_size = rx_bytes;
msg->wait = wait;
msg->errno = 0;
msg->pending = false;
msg->complete = false;
list_add_tail(&msg->list, &hsw->tx_list);
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
queue_kthread_work(&hsw->kworker, &hsw->kwork);
if (wait)
return tx_wait_done(hsw, msg, rx_data);
else
return 0;
}
static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header,
void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
{
return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data,
rx_bytes, 1);
}
static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header,
void *tx_data, size_t tx_bytes)
{
return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0);
}
static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
{ {
struct sst_hsw_ipc_fw_ready fw_ready; struct sst_hsw_ipc_fw_ready fw_ready;
...@@ -604,7 +466,7 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) ...@@ -604,7 +466,7 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
/* log the FW version info got from the mailbox here. */ /* log the FW version info got from the mailbox here. */
memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size); memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
pinfo = &fw_info[0]; pinfo = &fw_info[0];
for (i = 0; i < sizeof(tmp) / sizeof(char *); i++) for (i = 0; i < ARRAY_SIZE(tmp); i++)
tmp[i] = strsep(&pinfo, " "); tmp[i] = strsep(&pinfo, " ");
dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - " dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
"version: %s.%s, build %s, source commit id: %s\n", "version: %s.%s, build %s, source commit id: %s\n",
...@@ -657,27 +519,6 @@ static void hsw_notification_work(struct work_struct *work) ...@@ -657,27 +519,6 @@ static void hsw_notification_work(struct work_struct *work)
sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
} }
static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header)
{
struct ipc_message *msg;
/* clear reply bits & status bits */
header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
if (list_empty(&hsw->rx_list)) {
dev_err(hsw->dev, "error: rx list empty but received 0x%x\n",
header);
return NULL;
}
list_for_each_entry(msg, &hsw->rx_list, list) {
if (msg->header == header)
return msg;
}
return NULL;
}
static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg) static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
{ {
struct sst_hsw_stream *stream; struct sst_hsw_stream *stream;
...@@ -716,7 +557,7 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) ...@@ -716,7 +557,7 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
trace_ipc_reply("processing -->", header); trace_ipc_reply("processing -->", header);
msg = reply_find_msg(hsw, header); msg = sst_ipc_reply_find_msg(&hsw->ipc, header);
if (msg == NULL) { if (msg == NULL) {
trace_ipc_error("error: can't find message header", header); trace_ipc_error("error: can't find message header", header);
return -EIO; return -EIO;
...@@ -727,14 +568,14 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) ...@@ -727,14 +568,14 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
case IPC_GLB_REPLY_PENDING: case IPC_GLB_REPLY_PENDING:
trace_ipc_pending_reply("received", header); trace_ipc_pending_reply("received", header);
msg->pending = true; msg->pending = true;
hsw->pending = true; hsw->ipc.pending = true;
return 1; return 1;
case IPC_GLB_REPLY_SUCCESS: case IPC_GLB_REPLY_SUCCESS:
if (msg->pending) { if (msg->pending) {
trace_ipc_pending_reply("completed", header); trace_ipc_pending_reply("completed", header);
sst_dsp_inbox_read(hsw->dsp, msg->rx_data, sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
msg->rx_size); msg->rx_size);
hsw->pending = false; hsw->ipc.pending = false;
} else { } else {
/* copy data from the DSP */ /* copy data from the DSP */
sst_dsp_outbox_read(hsw->dsp, msg->rx_data, sst_dsp_outbox_read(hsw->dsp, msg->rx_data,
...@@ -790,11 +631,36 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) ...@@ -790,11 +631,36 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
/* wake up and return the error if we have waiters on this message ? */ /* wake up and return the error if we have waiters on this message ? */
list_del(&msg->list); list_del(&msg->list);
tx_msg_reply_complete(hsw, msg); sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg);
return 1; return 1;
} }
static int hsw_module_message(struct sst_hsw *hsw, u32 header)
{
u32 operation, module_id;
int handled = 0;
operation = msg_get_module_operation(header);
module_id = msg_get_module_id(header);
dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n",
header);
dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n",
operation, module_id);
switch (operation) {
case IPC_MODULE_NOTIFICATION:
dev_dbg(hsw->dev, "module notification received");
handled = 1;
break;
default:
handled = hsw_process_reply(hsw, header);
break;
}
return handled;
}
static int hsw_stream_message(struct sst_hsw *hsw, u32 header) static int hsw_stream_message(struct sst_hsw *hsw, u32 header)
{ {
u32 stream_msg, stream_id, stage_type; u32 stream_msg, stream_id, stage_type;
...@@ -890,6 +756,9 @@ static int hsw_process_notification(struct sst_hsw *hsw) ...@@ -890,6 +756,9 @@ static int hsw_process_notification(struct sst_hsw *hsw)
case IPC_GLB_DEBUG_LOG_MESSAGE: case IPC_GLB_DEBUG_LOG_MESSAGE:
handled = hsw_log_message(hsw, header); handled = hsw_log_message(hsw, header);
break; break;
case IPC_GLB_MODULE_OPERATION:
handled = hsw_module_message(hsw, header);
break;
default: default:
dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n", dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n",
type, header); type, header);
...@@ -903,6 +772,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) ...@@ -903,6 +772,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
{ {
struct sst_dsp *sst = (struct sst_dsp *) context; struct sst_dsp *sst = (struct sst_dsp *) context;
struct sst_hsw *hsw = sst_dsp_get_thread_context(sst); struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
struct sst_generic_ipc *ipc = &hsw->ipc;
u32 ipcx, ipcd; u32 ipcx, ipcd;
int handled; int handled;
unsigned long flags; unsigned long flags;
...@@ -949,7 +819,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) ...@@ -949,7 +819,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
spin_unlock_irqrestore(&sst->spinlock, flags); spin_unlock_irqrestore(&sst->spinlock, flags);
/* continue to send any remaining messages... */ /* continue to send any remaining messages... */
queue_kthread_work(&hsw->kworker, &hsw->kwork); queue_kthread_work(&ipc->kworker, &ipc->kwork);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -959,7 +829,8 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw, ...@@ -959,7 +829,8 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw,
{ {
int ret; int ret;
ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), ret = sst_ipc_tx_message_wait(&hsw->ipc,
IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
NULL, 0, version, sizeof(*version)); NULL, 0, version, sizeof(*version));
if (ret < 0) if (ret < 0)
dev_err(hsw->dev, "error: get version failed\n"); dev_err(hsw->dev, "error: get version failed\n");
...@@ -1023,7 +894,8 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw, ...@@ -1023,7 +894,8 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
req->channel = channel; req->channel = channel;
} }
ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req,
sizeof(*req), NULL, 0);
if (ret < 0) { if (ret < 0) {
dev_err(hsw->dev, "error: set stream volume failed\n"); dev_err(hsw->dev, "error: set stream volume failed\n");
return ret; return ret;
...@@ -1088,7 +960,8 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, ...@@ -1088,7 +960,8 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
req.curve_type = hsw->curve_type; req.curve_type = hsw->curve_type;
req.target_volume = volume; req.target_volume = volume;
ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req,
sizeof(req), NULL, 0);
if (ret < 0) { if (ret < 0) {
dev_err(hsw->dev, "error: set mixer volume failed\n"); dev_err(hsw->dev, "error: set mixer volume failed\n");
return ret; return ret;
...@@ -1146,7 +1019,7 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) ...@@ -1146,7 +1019,7 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
stream->free_req.stream_id = stream->reply.stream_hw_id; stream->free_req.stream_id = stream->reply.stream_hw_id;
header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM);
ret = ipc_tx_message_wait(hsw, header, &stream->free_req, ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req,
sizeof(stream->free_req), NULL, 0); sizeof(stream->free_req), NULL, 0);
if (ret < 0) { if (ret < 0) {
dev_err(hsw->dev, "error: free stream %d failed\n", dev_err(hsw->dev, "error: free stream %d failed\n",
...@@ -1338,8 +1211,8 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) ...@@ -1338,8 +1211,8 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM);
ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req), ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req,
reply, sizeof(*reply)); sizeof(*str_req), reply, sizeof(*reply));
if (ret < 0) { if (ret < 0) {
dev_err(hsw->dev, "error: stream commit failed\n"); dev_err(hsw->dev, "error: stream commit failed\n");
return ret; return ret;
...@@ -1388,7 +1261,8 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw) ...@@ -1388,7 +1261,8 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
trace_ipc_request("get global mixer info", 0); trace_ipc_request("get global mixer info", 0);
ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply)); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0,
reply, sizeof(*reply));
if (ret < 0) { if (ret < 0) {
dev_err(hsw->dev, "error: get stream info failed\n"); dev_err(hsw->dev, "error: get stream info failed\n");
return ret; return ret;
...@@ -1409,9 +1283,10 @@ static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, ...@@ -1409,9 +1283,10 @@ static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type,
header |= (stream_id << IPC_STR_ID_SHIFT); header |= (stream_id << IPC_STR_ID_SHIFT);
if (wait) if (wait)
return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0); return sst_ipc_tx_message_wait(&hsw->ipc, header,
NULL, 0, NULL, 0);
else else
return ipc_tx_message_nowait(hsw, header, NULL, 0); return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0);
} }
/* Stream ALSA trigger operations */ /* Stream ALSA trigger operations */
...@@ -1538,8 +1413,8 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, ...@@ -1538,8 +1413,8 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS);
ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config), ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config,
NULL, 0); sizeof(config), NULL, 0);
if (ret < 0) if (ret < 0)
dev_err(hsw->dev, "error: set device formats failed\n"); dev_err(hsw->dev, "error: set device formats failed\n");
...@@ -1559,8 +1434,8 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, ...@@ -1559,8 +1434,8 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
trace_ipc_request("PM enter Dx state", state); trace_ipc_request("PM enter Dx state", state);
ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_), ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_,
dx, sizeof(*dx)); sizeof(state_), dx, sizeof(*dx));
if (ret < 0) { if (ret < 0) {
dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
return ret; return ret;
...@@ -1703,32 +1578,6 @@ static int sst_hsw_dx_state_restore(struct sst_hsw *hsw) ...@@ -1703,32 +1578,6 @@ static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
return 0; return 0;
} }
static void sst_hsw_drop_all(struct sst_hsw *hsw)
{
struct ipc_message *msg, *tmp;
unsigned long flags;
int tx_drop_cnt = 0, rx_drop_cnt = 0;
/* drop all TX and Rx messages before we stall + reset DSP */
spin_lock_irqsave(&hsw->dsp->spinlock, flags);
list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) {
list_move(&msg->list, &hsw->empty_list);
tx_drop_cnt++;
}
list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) {
list_move(&msg->list, &hsw->empty_list);
rx_drop_cnt++;
}
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
if (tx_drop_cnt || rx_drop_cnt)
dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n",
tx_drop_cnt, rx_drop_cnt);
}
int sst_hsw_dsp_load(struct sst_hsw *hsw) int sst_hsw_dsp_load(struct sst_hsw *hsw)
{ {
struct sst_dsp *dsp = hsw->dsp; struct sst_dsp *dsp = hsw->dsp;
...@@ -1808,7 +1657,7 @@ int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw) ...@@ -1808,7 +1657,7 @@ int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
if (ret < 0) if (ret < 0)
return ret; return ret;
sst_hsw_drop_all(hsw); sst_ipc_drop_all(&hsw->ipc);
return 0; return 0;
} }
...@@ -1844,6 +1693,8 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) ...@@ -1844,6 +1693,8 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
if (ret < 0) if (ret < 0)
dev_err(dev, "error: audio DSP boot failure\n"); dev_err(dev, "error: audio DSP boot failure\n");
sst_hsw_init_module_state(hsw);
ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
msecs_to_jiffies(IPC_BOOT_MSECS)); msecs_to_jiffies(IPC_BOOT_MSECS));
if (ret == 0) { if (ret == 0) {
...@@ -1864,26 +1715,345 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) ...@@ -1864,26 +1715,345 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
} }
#endif #endif
static int msg_empty_list_init(struct sst_hsw *hsw) struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
{ {
int i; return hsw->dsp;
}
hsw->msg = kzalloc(sizeof(struct ipc_message) * void sst_hsw_init_module_state(struct sst_hsw *hsw)
IPC_EMPTY_LIST_SIZE, GFP_KERNEL); {
if (hsw->msg == NULL) struct sst_module *module;
return -ENOMEM; enum sst_hsw_module_id id;
/* the base fw contains several modules */
for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) {
module = sst_module_get_from_id(hsw->dsp, id);
if (module) {
/* module waves is active only after being enabled */
if (id == SST_HSW_MODULE_WAVES)
module->state = SST_MODULE_STATE_INITIALIZED;
else
module->state = SST_MODULE_STATE_ACTIVE;
}
}
}
bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id)
{
struct sst_module *module;
module = sst_module_get_from_id(hsw->dsp, module_id);
if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED)
return false;
else
return true;
}
bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id)
{
struct sst_module *module;
module = sst_module_get_from_id(hsw->dsp, module_id);
if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE)
return true;
else
return false;
}
for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
init_waitqueue_head(&hsw->msg[i].waitq); {
list_add(&hsw->msg[i].list, &hsw->empty_list); hsw->enabled_modules_rtd3 |= (1 << module_id);
}
void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id)
{
hsw->enabled_modules_rtd3 &= ~(1 << module_id);
}
bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
{
return hsw->enabled_modules_rtd3 & (1 << module_id);
}
void sst_hsw_reset_param_buf(struct sst_hsw *hsw)
{
hsw->param_idx_w = 0;
hsw->param_idx_r = 0;
memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf));
}
int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf)
{
/* save line to the first available position of param buffer */
if (hsw->param_idx_w > WAVES_PARAM_LINES - 1) {
dev_warn(hsw->dev, "warning: param buffer overflow!\n");
return -EPERM;
}
memcpy(hsw->param_buf[hsw->param_idx_w], buf, WAVES_PARAM_COUNT);
hsw->param_idx_w++;
return 0;
}
int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf)
{
u8 id = 0;
/* read the first matching line from param buffer */
while (hsw->param_idx_r < WAVES_PARAM_LINES) {
id = hsw->param_buf[hsw->param_idx_r][0];
hsw->param_idx_r++;
if (buf[0] == id) {
memcpy(buf, hsw->param_buf[hsw->param_idx_r],
WAVES_PARAM_COUNT);
break;
}
} }
if (hsw->param_idx_r > WAVES_PARAM_LINES - 1) {
dev_dbg(hsw->dev, "end of buffer, roll to the beginning\n");
hsw->param_idx_r = 0;
return 0;
}
return 0;
}
int sst_hsw_launch_param_buf(struct sst_hsw *hsw)
{
int ret, idx;
if (!sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
dev_dbg(hsw->dev, "module waves is not active\n");
return 0;
}
/* put all param lines to DSP through ipc */
for (idx = 0; idx < hsw->param_idx_w; idx++) {
ret = sst_hsw_module_set_param(hsw,
SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0],
WAVES_PARAM_COUNT, hsw->param_buf[idx]);
if (ret < 0)
return ret;
}
return 0; return 0;
} }
struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) int sst_hsw_module_load(struct sst_hsw *hsw,
u32 module_id, u32 instance_id, char *name)
{ {
return hsw->dsp; int ret = 0;
const struct firmware *fw = NULL;
struct sst_fw *hsw_sst_fw;
struct sst_module *module;
struct device *dev = hsw->dev;
struct sst_dsp *dsp = hsw->dsp;
dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name);
module = sst_module_get_from_id(dsp, module_id);
if (module == NULL) {
/* loading for the first time */
if (module_id == SST_HSW_MODULE_BASE_FW) {
/* for base module: use fw requested in acpi probe */
fw = dsp->pdata->fw;
if (!fw) {
dev_err(dev, "request Base fw failed\n");
return -ENODEV;
}
} else {
/* try and load any other optional modules if they are
* available. Use dev_info instead of dev_err in case
* request firmware failed */
ret = request_firmware(&fw, name, dev);
if (ret) {
dev_info(dev, "fw image %s not available(%d)\n",
name, ret);
return ret;
}
}
hsw_sst_fw = sst_fw_new(dsp, fw, hsw);
if (hsw_sst_fw == NULL) {
dev_err(dev, "error: failed to load firmware\n");
ret = -ENOMEM;
goto out;
}
module = sst_module_get_from_id(dsp, module_id);
if (module == NULL) {
dev_err(dev, "error: no module %d in firmware %s\n",
module_id, name);
}
} else
dev_info(dev, "module %d (%s) already loaded\n",
module_id, name);
out:
/* release fw, but base fw should be released by acpi driver */
if (fw && module_id != SST_HSW_MODULE_BASE_FW)
release_firmware(fw);
return ret;
}
int sst_hsw_module_enable(struct sst_hsw *hsw,
u32 module_id, u32 instance_id)
{
int ret;
u32 header = 0;
struct sst_hsw_ipc_module_config config;
struct sst_module *module;
struct sst_module_runtime *runtime;
struct device *dev = hsw->dev;
struct sst_dsp *dsp = hsw->dsp;
if (!sst_hsw_is_module_loaded(hsw, module_id)) {
dev_dbg(dev, "module %d not loaded\n", module_id);
return 0;
}
if (sst_hsw_is_module_active(hsw, module_id)) {
dev_info(dev, "module %d already enabled\n", module_id);
return 0;
}
module = sst_module_get_from_id(dsp, module_id);
if (module == NULL) {
dev_err(dev, "module %d not valid\n", module_id);
return -ENXIO;
}
runtime = sst_module_runtime_get_from_id(module, module_id);
if (runtime == NULL) {
dev_err(dev, "runtime %d not valid", module_id);
return -ENXIO;
}
header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) |
IPC_MODULE_ID(module_id);
dev_dbg(dev, "module enable header: %x\n", header);
config.map.module_entries_count = 1;
config.map.module_entries[0].module_id = module->id;
config.map.module_entries[0].entry_point = module->entry;
config.persistent_mem.offset =
sst_dsp_get_offset(dsp,
runtime->persistent_offset, SST_MEM_DRAM);
config.persistent_mem.size = module->persistent_size;
config.scratch_mem.offset =
sst_dsp_get_offset(dsp,
dsp->scratch_offset, SST_MEM_DRAM);
config.scratch_mem.size = module->scratch_size;
dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x",
config.map.module_entries[0].module_id,
config.persistent_mem.size,
config.persistent_mem.offset,
config.scratch_mem.size, config.scratch_mem.offset,
config.map.module_entries[0].entry_point);
ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
&config, sizeof(config), NULL, 0);
if (ret < 0)
dev_err(dev, "ipc: module enable failed - %d\n", ret);
else
module->state = SST_MODULE_STATE_ACTIVE;
return ret;
}
int sst_hsw_module_disable(struct sst_hsw *hsw,
u32 module_id, u32 instance_id)
{
int ret;
u32 header;
struct sst_module *module;
struct device *dev = hsw->dev;
struct sst_dsp *dsp = hsw->dsp;
if (!sst_hsw_is_module_loaded(hsw, module_id)) {
dev_dbg(dev, "module %d not loaded\n", module_id);
return 0;
}
if (!sst_hsw_is_module_active(hsw, module_id)) {
dev_info(dev, "module %d already disabled\n", module_id);
return 0;
}
module = sst_module_get_from_id(dsp, module_id);
if (module == NULL) {
dev_err(dev, "module %d not valid\n", module_id);
return -ENXIO;
}
header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) |
IPC_MODULE_ID(module_id);
ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, 0);
if (ret < 0)
dev_err(dev, "module disable failed - %d\n", ret);
else
module->state = SST_MODULE_STATE_INITIALIZED;
return ret;
}
int sst_hsw_module_set_param(struct sst_hsw *hsw,
u32 module_id, u32 instance_id, u32 parameter_id,
u32 param_size, char *param)
{
int ret;
unsigned char *data = NULL;
u32 header = 0;
u32 payload_size = 0, transfer_parameter_size = 0;
dma_addr_t dma_addr = 0;
struct sst_hsw_transfer_parameter *parameter;
struct device *dev = hsw->dev;
header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) |
IPC_MODULE_ID(module_id);
dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", header);
payload_size = param_size +
sizeof(struct sst_hsw_transfer_parameter) -
sizeof(struct sst_hsw_transfer_list);
dev_dbg(dev, "parameter size : %d\n", param_size);
dev_dbg(dev, "payload size : %d\n", payload_size);
if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) {
/* short parameter, mailbox can contain data */
dev_dbg(dev, "transfer parameter size : %d\n",
transfer_parameter_size);
transfer_parameter_size = ALIGN(payload_size, 4);
dev_dbg(dev, "transfer parameter aligned size : %d\n",
transfer_parameter_size);
parameter = kzalloc(transfer_parameter_size, GFP_KERNEL);
if (parameter == NULL)
return -ENOMEM;
memcpy(parameter->data, param, param_size);
} else {
dev_warn(dev, "transfer parameter size too large!");
return 0;
}
parameter->parameter_id = parameter_id;
parameter->data_size = param_size;
ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
parameter, transfer_parameter_size , NULL, 0);
if (ret < 0)
dev_err(dev, "ipc: module set parameter failed - %d\n", ret);
kfree(parameter);
if (data)
dma_free_coherent(hsw->dsp->dma_dev,
param_size, (void *)data, dma_addr);
return ret;
} }
static struct sst_dsp_device hsw_dev = { static struct sst_dsp_device hsw_dev = {
...@@ -1891,10 +2061,48 @@ static struct sst_dsp_device hsw_dev = { ...@@ -1891,10 +2061,48 @@ static struct sst_dsp_device hsw_dev = {
.ops = &haswell_ops, .ops = &haswell_ops,
}; };
static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
{
/* send the message */
sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
sst_dsp_ipc_msg_tx(ipc->dsp, msg->header);
}
static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
{
struct sst_dsp *sst = ipc->dsp;
u32 isr, ipcd, imrx, ipcx;
ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
dev_err(ipc->dev,
"ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
text, ipcx, isr, ipcd, imrx);
}
static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data,
size_t tx_size)
{
memcpy(msg->tx_data, tx_data, tx_size);
}
static u64 hsw_reply_msg_match(u64 header, u64 *mask)
{
/* clear reply bits & status bits */
header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
*mask = (u64)-1;
return header;
}
int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
{ {
struct sst_hsw_ipc_fw_version version; struct sst_hsw_ipc_fw_version version;
struct sst_hsw *hsw; struct sst_hsw *hsw;
struct sst_generic_ipc *ipc;
int ret; int ret;
dev_dbg(dev, "initialising Audio DSP IPC\n"); dev_dbg(dev, "initialising Audio DSP IPC\n");
...@@ -1903,39 +2111,30 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -1903,39 +2111,30 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
if (hsw == NULL) if (hsw == NULL)
return -ENOMEM; return -ENOMEM;
hsw->dev = dev; ipc = &hsw->ipc;
INIT_LIST_HEAD(&hsw->stream_list); ipc->dev = dev;
INIT_LIST_HEAD(&hsw->tx_list); ipc->ops.tx_msg = hsw_tx_msg;
INIT_LIST_HEAD(&hsw->rx_list); ipc->ops.shim_dbg = hsw_shim_dbg;
INIT_LIST_HEAD(&hsw->empty_list); ipc->ops.tx_data_copy = hsw_tx_data_copy;
init_waitqueue_head(&hsw->boot_wait); ipc->ops.reply_msg_match = hsw_reply_msg_match;
init_waitqueue_head(&hsw->wait_txq);
ret = msg_empty_list_init(hsw);
if (ret < 0)
return -ENOMEM;
/* start the IPC message thread */ ret = sst_ipc_init(ipc);
init_kthread_worker(&hsw->kworker); if (ret != 0)
hsw->tx_thread = kthread_run(kthread_worker_fn, goto ipc_init_err;
&hsw->kworker, "%s",
dev_name(hsw->dev));
if (IS_ERR(hsw->tx_thread)) {
ret = PTR_ERR(hsw->tx_thread);
dev_err(hsw->dev, "error: failed to create message TX task\n");
goto err_free_msg;
}
init_kthread_work(&hsw->kwork, ipc_tx_msgs);
INIT_LIST_HEAD(&hsw->stream_list);
init_waitqueue_head(&hsw->boot_wait);
hsw_dev.thread_context = hsw; hsw_dev.thread_context = hsw;
/* init SST shim */ /* init SST shim */
hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata); hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
if (hsw->dsp == NULL) { if (hsw->dsp == NULL) {
ret = -ENODEV; ret = -ENODEV;
goto dsp_err; goto dsp_new_err;
} }
ipc->dsp = hsw->dsp;
/* allocate DMA buffer for context storage */ /* allocate DMA buffer for context storage */
hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev, hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL); SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
...@@ -1947,18 +2146,22 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -1947,18 +2146,22 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
/* keep the DSP in reset state for base FW loading */ /* keep the DSP in reset state for base FW loading */
sst_dsp_reset(hsw->dsp); sst_dsp_reset(hsw->dsp);
hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); /* load base module and other modules in base firmware image */
if (hsw->sst_fw == NULL) { ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base");
ret = -ENODEV; if (ret < 0)
dev_err(dev, "error: failed to load firmware\n");
goto fw_err; goto fw_err;
}
/* try to load module waves */
sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin");
/* allocate scratch mem regions */ /* allocate scratch mem regions */
ret = sst_block_alloc_scratch(hsw->dsp); ret = sst_block_alloc_scratch(hsw->dsp);
if (ret < 0) if (ret < 0)
goto boot_err; goto boot_err;
/* init param buffer */
sst_hsw_reset_param_buf(hsw);
/* wait for DSP boot completion */ /* wait for DSP boot completion */
sst_dsp_boot(hsw->dsp); sst_dsp_boot(hsw->dsp);
ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
...@@ -1971,6 +2174,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -1971,6 +2174,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
goto boot_err; goto boot_err;
} }
/* init module state after boot */
sst_hsw_init_module_state(hsw);
/* get the FW version */ /* get the FW version */
sst_hsw_fw_get_version(hsw, &version); sst_hsw_fw_get_version(hsw, &version);
...@@ -1986,17 +2192,16 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -1986,17 +2192,16 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
boot_err: boot_err:
sst_dsp_reset(hsw->dsp); sst_dsp_reset(hsw->dsp);
sst_fw_free(hsw->sst_fw); sst_fw_free_all(hsw->dsp);
fw_err: fw_err:
dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
hsw->dx_context, hsw->dx_context_paddr); hsw->dx_context, hsw->dx_context_paddr);
dma_err: dma_err:
sst_dsp_free(hsw->dsp); sst_dsp_free(hsw->dsp);
dsp_err: dsp_new_err:
kthread_stop(hsw->tx_thread); sst_ipc_fini(ipc);
err_free_msg: ipc_init_err:
kfree(hsw->msg); kfree(hsw);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(sst_hsw_dsp_init); EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
...@@ -2010,7 +2215,6 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata) ...@@ -2010,7 +2215,6 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
hsw->dx_context, hsw->dx_context_paddr); hsw->dx_context, hsw->dx_context_paddr);
sst_dsp_free(hsw->dsp); sst_dsp_free(hsw->dsp);
kthread_stop(hsw->tx_thread); sst_ipc_fini(&hsw->ipc);
kfree(hsw->msg);
} }
EXPORT_SYMBOL_GPL(sst_hsw_dsp_free); EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
#define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400 #define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400
#define SST_HSW_MAX_INFO_SIZE 64 #define SST_HSW_MAX_INFO_SIZE 64
#define SST_HSW_BUILD_HASH_LENGTH 40 #define SST_HSW_BUILD_HASH_LENGTH 40
#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500
#define WAVES_PARAM_COUNT 128
#define WAVES_PARAM_LINES 160
struct sst_hsw; struct sst_hsw;
struct sst_hsw_stream; struct sst_hsw_stream;
...@@ -187,6 +190,28 @@ enum sst_hsw_performance_action { ...@@ -187,6 +190,28 @@ enum sst_hsw_performance_action {
SST_HSW_PERF_STOP = 1, SST_HSW_PERF_STOP = 1,
}; };
struct sst_hsw_transfer_info {
uint32_t destination; /* destination address */
uint32_t reverse:1; /* if 1 data flows from destination */
uint32_t size:31; /* transfer size in bytes.*/
uint16_t first_page_offset; /* offset to data in the first page. */
uint8_t packed_pages; /* page addresses. Each occupies 20 bits */
} __attribute__((packed));
struct sst_hsw_transfer_list {
uint32_t transfers_count;
struct sst_hsw_transfer_info transfers;
} __attribute__((packed));
struct sst_hsw_transfer_parameter {
uint32_t parameter_id;
uint32_t data_size;
union {
uint8_t data[1];
struct sst_hsw_transfer_list transfer_list;
};
} __attribute__((packed));
/* SST firmware module info */ /* SST firmware module info */
struct sst_hsw_module_info { struct sst_hsw_module_info {
u8 name[SST_HSW_MAX_INFO_SIZE]; u8 name[SST_HSW_MAX_INFO_SIZE];
...@@ -215,6 +240,12 @@ struct sst_hsw_fx_enable { ...@@ -215,6 +240,12 @@ struct sst_hsw_fx_enable {
struct sst_hsw_memory_info persistent_mem; struct sst_hsw_memory_info persistent_mem;
} __attribute__((packed)); } __attribute__((packed));
struct sst_hsw_ipc_module_config {
struct sst_hsw_module_map map;
struct sst_hsw_memory_info persistent_mem;
struct sst_hsw_memory_info scratch_mem;
} __attribute__((packed));
struct sst_hsw_get_fx_param { struct sst_hsw_get_fx_param {
u32 parameter_id; u32 parameter_id;
u32 param_size; u32 param_size;
...@@ -467,6 +498,28 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); ...@@ -467,6 +498,28 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
/* fw module function */
void sst_hsw_init_module_state(struct sst_hsw *hsw);
bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id);
bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id);
void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id);
bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
void sst_hsw_reset_param_buf(struct sst_hsw *hsw);
int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf);
int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf);
int sst_hsw_launch_param_buf(struct sst_hsw *hsw);
int sst_hsw_module_load(struct sst_hsw *hsw,
u32 module_id, u32 instance_id, char *name);
int sst_hsw_module_enable(struct sst_hsw *hsw,
u32 module_id, u32 instance_id);
int sst_hsw_module_disable(struct sst_hsw *hsw,
u32 module_id, u32 instance_id);
int sst_hsw_module_set_param(struct sst_hsw *hsw,
u32 module_id, u32 instance_id, u32 parameter_id,
u32 param_size, char *param);
/* runtime module management */ /* runtime module management */
struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
int mod_id, int offset); int mod_id, int offset);
......
...@@ -29,9 +29,9 @@ ...@@ -29,9 +29,9 @@
#include <sound/tlv.h> #include <sound/tlv.h>
#include <sound/compress_driver.h> #include <sound/compress_driver.h>
#include "sst-haswell-ipc.h" #include "../haswell/sst-haswell-ipc.h"
#include "sst-dsp-priv.h" #include "../common/sst-dsp-priv.h"
#include "sst-dsp.h" #include "../common/sst-dsp.h"
#define HSW_PCM_COUNT 6 #define HSW_PCM_COUNT 6
#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ #define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */
...@@ -137,6 +137,7 @@ struct hsw_priv_data { ...@@ -137,6 +137,7 @@ struct hsw_priv_data {
struct device *dev; struct device *dev;
enum hsw_pm_state pm_state; enum hsw_pm_state pm_state;
struct snd_soc_card *soc_card; struct snd_soc_card *soc_card;
struct sst_module_runtime *runtime_waves; /* sound effect module */
/* page tables */ /* page tables */
struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
...@@ -318,6 +319,93 @@ static int hsw_volume_get(struct snd_kcontrol *kcontrol, ...@@ -318,6 +319,93 @@ static int hsw_volume_get(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
ucontrol->value.integer.value[0] =
(sst_hsw_is_module_active(hsw, id) ||
sst_hsw_is_module_enabled_rtd3(hsw, id));
return 0;
}
static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
int ret = 0;
enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
bool switch_on = (bool)ucontrol->value.integer.value[0];
/* if module is in RAM on the DSP, apply user settings to module through
* ipc. If module is not in RAM on the DSP, store user setting for
* track */
if (sst_hsw_is_module_loaded(hsw, id)) {
if (switch_on == sst_hsw_is_module_active(hsw, id))
return 0;
if (switch_on)
ret = sst_hsw_module_enable(hsw, id, 0);
else
ret = sst_hsw_module_disable(hsw, id, 0);
} else {
if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id))
return 0;
if (switch_on)
sst_hsw_set_module_enabled_rtd3(hsw, id);
else
sst_hsw_set_module_disabled_rtd3(hsw, id);
}
return ret;
}
static int hsw_waves_param_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
/* return a matching line from param buffer */
return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data);
}
static int hsw_waves_param_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
int ret;
enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
int param_id = ucontrol->value.bytes.data[0];
int param_size = WAVES_PARAM_COUNT;
/* clear param buffer and reset buffer index */
if (param_id == 0xFF) {
sst_hsw_reset_param_buf(hsw);
return 0;
}
/* store params into buffer */
ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data);
if (ret < 0)
return ret;
if (sst_hsw_is_module_active(hsw, id))
ret = sst_hsw_module_set_param(hsw, id, 0, param_id,
param_size, ucontrol->value.bytes.data);
return ret;
}
/* TLV used by both global and stream volumes */ /* TLV used by both global and stream volumes */
static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1); static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
...@@ -339,6 +427,12 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { ...@@ -339,6 +427,12 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = {
SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
ARRAY_SIZE(volume_map) - 1, 0, ARRAY_SIZE(volume_map) - 1, 0,
hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
/* enable/disable module waves */
SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
hsw_waves_switch_get, hsw_waves_switch_put),
/* set parameters to module waves */
SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT,
hsw_waves_param_get, hsw_waves_param_put),
}; };
/* Create DMA buffer page table for DSP */ /* Create DMA buffer page table for DSP */
...@@ -807,6 +901,14 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) ...@@ -807,6 +901,14 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
pcm_data->runtime->persistent_offset; pcm_data->runtime->persistent_offset;
} }
/* create runtime blocks for module waves */
if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
pdata->runtime_waves = sst_hsw_runtime_module_create(hsw,
SST_HSW_MODULE_WAVES, 0);
if (pdata->runtime_waves == NULL)
goto err;
}
return 0; return 0;
err: err:
...@@ -820,14 +922,17 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) ...@@ -820,14 +922,17 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
{ {
struct sst_hsw *hsw = pdata->hsw;
struct hsw_pcm_data *pcm_data; struct hsw_pcm_data *pcm_data;
int i; int i;
for (i = 0; i < ARRAY_SIZE(mod_map); i++) { for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
sst_hsw_runtime_module_free(pcm_data->runtime); sst_hsw_runtime_module_free(pcm_data->runtime);
} }
if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
sst_hsw_runtime_module_free(pdata->runtime_waves);
}
} }
static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
...@@ -984,7 +1089,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) ...@@ -984,7 +1089,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
} }
/* allocate runtime modules */ /* allocate runtime modules */
hsw_pcm_create_modules(priv_data); ret = hsw_pcm_create_modules(priv_data);
if (ret < 0)
goto err;
/* enable runtime PM with auto suspend */ /* enable runtime PM with auto suspend */
pm_runtime_set_autosuspend_delay(platform->dev, pm_runtime_set_autosuspend_delay(platform->dev,
...@@ -996,7 +1103,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) ...@@ -996,7 +1103,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
return 0; return 0;
err: err:
for (;i >= 0; i--) { for (--i; i >= 0; i--) {
if (hsw_dais[i].playback.channels_min) if (hsw_dais[i].playback.channels_min)
snd_dma_free_pages(&priv_data->dmab[i][0]); snd_dma_free_pages(&priv_data->dmab[i][0]);
if (hsw_dais[i].capture.channels_min) if (hsw_dais[i].capture.channels_min)
...@@ -1101,10 +1208,18 @@ static int hsw_pcm_runtime_suspend(struct device *dev) ...@@ -1101,10 +1208,18 @@ static int hsw_pcm_runtime_suspend(struct device *dev)
{ {
struct hsw_priv_data *pdata = dev_get_drvdata(dev); struct hsw_priv_data *pdata = dev_get_drvdata(dev);
struct sst_hsw *hsw = pdata->hsw; struct sst_hsw *hsw = pdata->hsw;
int ret;
if (pdata->pm_state >= HSW_PM_STATE_RTD3) if (pdata->pm_state >= HSW_PM_STATE_RTD3)
return 0; return 0;
/* fw modules will be unloaded on RTD3, set flag to track */
if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0);
if (ret < 0)
return ret;
sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
}
sst_hsw_dsp_runtime_suspend(hsw); sst_hsw_dsp_runtime_suspend(hsw);
sst_hsw_dsp_runtime_sleep(hsw); sst_hsw_dsp_runtime_sleep(hsw);
pdata->pm_state = HSW_PM_STATE_RTD3; pdata->pm_state = HSW_PM_STATE_RTD3;
...@@ -1139,6 +1254,19 @@ static int hsw_pcm_runtime_resume(struct device *dev) ...@@ -1139,6 +1254,19 @@ static int hsw_pcm_runtime_resume(struct device *dev)
else if (ret == 1) /* no action required */ else if (ret == 1) /* no action required */
return 0; return 0;
/* check flag when resume */
if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) {
ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0);
if (ret < 0)
return ret;
/* put parameters from buffer to dsp */
ret = sst_hsw_launch_param_buf(hsw);
if (ret < 0)
return ret;
/* unset flag */
sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
}
pdata->pm_state = HSW_PM_STATE_D0; pdata->pm_state = HSW_PM_STATE_D0;
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment