Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
This commit is contained in:
commit
aab0bb17ef
|
@ -14,6 +14,7 @@
|
|||
struct rt5670_platform_data {
|
||||
int jd_mode;
|
||||
bool in2_diff;
|
||||
bool dev_gpio;
|
||||
|
||||
bool dmic_en;
|
||||
unsigned int dmic1_data_pin;
|
||||
|
|
|
@ -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(dac_vol_tlv, -65625, 375, 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,
|
|||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
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);
|
||||
val &= RT5670_SCLK_SRC_MASK;
|
||||
if (val == RT5670_SCLK_SRC_PLL1)
|
||||
if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
@ -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);
|
||||
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) {
|
||||
case RT5670_SCLK_S_MCLK:
|
||||
reg_val |= RT5670_SCLK_SRC_MCLK;
|
||||
|
@ -2298,7 +2469,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
|
|||
snd_soc_update_bits(codec, RT5670_GLB_CLK,
|
||||
RT5670_SCLK_SRC_MASK, reg_val);
|
||||
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);
|
||||
|
||||
|
@ -2517,6 +2689,7 @@ static int rt5670_remove(struct snd_soc_codec *codec)
|
|||
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_write(rt5670->regmap, RT5670_RESET, 0);
|
||||
snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2676,6 +2849,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
|
|||
if (dmi_check_system(dmi_platform_intel_braswell)) {
|
||||
rt5670->pdata.dmic_en = true;
|
||||
rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
|
||||
rt5670->pdata.dev_gpio = true;
|
||||
rt5670->pdata.jd_mode = 1;
|
||||
}
|
||||
|
||||
|
@ -2717,12 +2891,17 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
|
|||
regmap_update_bits(rt5670->regmap, RT5670_IN2,
|
||||
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,
|
||||
RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
|
||||
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
|
||||
RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
|
||||
|
||||
regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8);
|
||||
}
|
||||
|
||||
if (rt5670->pdata.jd_mode) {
|
||||
|
|
|
@ -1988,6 +1988,8 @@ struct rt5670_priv {
|
|||
struct snd_soc_codec *codec;
|
||||
struct rt5670_platform_data pdata;
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_jack *jack;
|
||||
struct snd_soc_jack_gpio hp_gpio;
|
||||
|
||||
int sysclk;
|
||||
int sysclk_src;
|
||||
|
@ -2002,6 +2004,11 @@ struct rt5670_priv {
|
|||
int dsp_sw; /* expected parameter setting */
|
||||
int dsp_rate;
|
||||
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__ */
|
||||
|
|
|
@ -1,42 +1,10 @@
|
|||
# Core support
|
||||
snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
|
||||
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
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
|
||||
|
||||
# Platform Support
|
||||
snd-soc-sst-haswell-pcm-objs := \
|
||||
sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
|
||||
snd-soc-sst-baytrail-pcm-objs := \
|
||||
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
|
||||
obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += atom/
|
||||
|
||||
# Machine support
|
||||
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-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/
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
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 @@
|
|||
#include <asm/platform_sst_audio.h>
|
||||
#include "../sst-mfld-platform.h"
|
||||
#include "sst.h"
|
||||
#include "../sst-dsp.h"
|
||||
#include "../../common/sst-dsp.h"
|
||||
|
||||
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
|
||||
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
|
|
@ -39,7 +39,7 @@
|
|||
#include <acpi/actypes.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include "../sst-mfld-platform.h"
|
||||
#include "../sst-dsp.h"
|
||||
#include "../../common/sst-dsp.h"
|
||||
#include "sst.h"
|
||||
|
||||
struct sst_machines {
|
|
@ -32,7 +32,7 @@
|
|||
#include <asm/platform_sst_audio.h>
|
||||
#include "../sst-mfld-platform.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,
|
|||
tstamp->copied_total = fw_tstamp.ring_buffer_counter;
|
||||
tstamp->pcm_frames = fw_tstamp.frames_decoded;
|
||||
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;
|
||||
|
||||
dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames);
|
|
@ -32,7 +32,7 @@
|
|||
#include <asm/platform_sst_audio.h>
|
||||
#include "../sst-mfld-platform.h"
|
||||
#include "sst.h"
|
||||
#include "../sst-dsp.h"
|
||||
#include "../../common/sst-dsp.h"
|
||||
|
||||
struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
|
||||
u32 msg_id, u32 drv_id)
|
|
@ -37,7 +37,7 @@
|
|||
#include <asm/platform_sst_audio.h>
|
||||
#include "../sst-mfld-platform.h"
|
||||
#include "sst.h"
|
||||
#include "../sst-dsp.h"
|
||||
#include "../../common/sst-dsp.h"
|
||||
|
||||
void memcpy32_toio(void __iomem *dst, const void *src, int count)
|
||||
{
|
|
@ -34,7 +34,7 @@
|
|||
#include <asm/platform_sst_audio.h>
|
||||
#include "../sst-mfld-platform.h"
|
||||
#include "sst.h"
|
||||
#include "../sst-dsp.h"
|
||||
#include "../../common/sst-dsp.h"
|
||||
|
||||
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,
|
|||
|
||||
}
|
||||
|
||||
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
|
||||
*
|
|
@ -31,7 +31,7 @@
|
|||
#include <asm/platform_sst_audio.h>
|
||||
#include "../sst-mfld-platform.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)
|
||||
{
|
|
@ -0,0 +1,4 @@
|
|||
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 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "sst-dsp.h"
|
||||
#include "sst-dsp-priv.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "sst-baytrail-ipc.h"
|
||||
|
||||
#define SST_BYT_FW_SIGNATURE_SIZE 4
|
|
@ -29,8 +29,9 @@
|
|||
#include <asm/div64.h>
|
||||
|
||||
#include "sst-baytrail-ipc.h"
|
||||
#include "sst-dsp.h"
|
||||
#include "sst-dsp-priv.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "../common/sst-ipc.h"
|
||||
|
||||
/* IPC message timeout */
|
||||
#define IPC_TIMEOUT_MSECS 300
|
||||
|
@ -142,23 +143,6 @@ struct sst_byt_fw_init {
|
|||
u8 debug_info;
|
||||
} __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;
|
||||
|
||||
|
@ -195,14 +179,7 @@ struct sst_byt {
|
|||
struct sst_fw *fw;
|
||||
|
||||
/* 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;
|
||||
struct ipc_message *msg;
|
||||
struct sst_generic_ipc ipc;
|
||||
};
|
||||
|
||||
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,
|
|||
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)
|
||||
{
|
||||
struct sst_byt_stream *stream;
|
||||
|
@ -477,7 +251,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
|
|||
{
|
||||
struct ipc_message *msg;
|
||||
|
||||
msg = sst_byt_reply_find_msg(byt, header);
|
||||
msg = sst_ipc_reply_find_msg(&byt->ipc, header);
|
||||
if (msg == NULL)
|
||||
return 1;
|
||||
|
||||
|
@ -491,7 +265,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
|
|||
|
||||
list_del(&msg->list);
|
||||
/* wake up */
|
||||
sst_byt_tx_msg_reply_complete(byt, msg);
|
||||
sst_ipc_tx_msg_reply_complete(&byt->ipc, msg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -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_byt *byt = sst_dsp_get_thread_context(sst);
|
||||
struct sst_generic_ipc *ipc = &byt->ipc;
|
||||
u64 header;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -569,7 +344,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context)
|
|||
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||
|
||||
/* continue to send any remaining messages... */
|
||||
queue_kthread_work(&byt->kworker, &byt->kwork);
|
||||
queue_kthread_work(&ipc->kworker, &ipc->kwork);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -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,
|
||||
sizeof(*str_req) + sizeof(u32),
|
||||
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));
|
||||
if (ret < 0) {
|
||||
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)
|
|||
goto out;
|
||||
|
||||
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) {
|
||||
dev_err(byt->dev, "ipc: free stream %d failed\n",
|
||||
stream->str_id);
|
||||
|
@ -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);
|
||||
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
|
||||
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 */
|
||||
|
@ -725,7 +503,7 @@ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
|
|||
tx_msg = &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)
|
||||
dev_err(byt->dev, "ipc: error failed to start stream %d\n",
|
||||
stream->str_id);
|
||||
|
@ -790,23 +568,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt,
|
|||
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)
|
||||
{
|
||||
return byt->dsp;
|
||||
|
@ -823,7 +584,7 @@ int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
|
|||
|
||||
dev_dbg(byt->dev, "dsp reset\n");
|
||||
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, "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)
|
|||
}
|
||||
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)
|
||||
{
|
||||
struct sst_byt *byt;
|
||||
struct sst_generic_ipc *ipc;
|
||||
struct sst_fw *byt_sst_fw;
|
||||
struct sst_byt_fw_init init;
|
||||
int err;
|
||||
|
@ -889,39 +693,30 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
|
|||
if (byt == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
byt->dev = dev;
|
||||
ipc = &byt->ipc;
|
||||
ipc->dev = dev;
|
||||
ipc->ops.tx_msg = byt_tx_msg;
|
||||
ipc->ops.shim_dbg = byt_shim_dbg;
|
||||
ipc->ops.tx_data_copy = byt_tx_data_copy;
|
||||
ipc->ops.reply_msg_match = byt_reply_msg_match;
|
||||
|
||||
err = sst_ipc_init(ipc);
|
||||
if (err != 0)
|
||||
goto ipc_init_err;
|
||||
|
||||
INIT_LIST_HEAD(&byt->stream_list);
|
||||
INIT_LIST_HEAD(&byt->tx_list);
|
||||
INIT_LIST_HEAD(&byt->rx_list);
|
||||
INIT_LIST_HEAD(&byt->empty_list);
|
||||
init_waitqueue_head(&byt->boot_wait);
|
||||
init_waitqueue_head(&byt->wait_txq);
|
||||
|
||||
err = msg_empty_list_init(byt);
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/* 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);
|
||||
|
||||
byt_dev.thread_context = byt;
|
||||
|
||||
/* init SST shim */
|
||||
byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
|
||||
if (byt->dsp == NULL) {
|
||||
err = -ENODEV;
|
||||
goto dsp_err;
|
||||
goto dsp_new_err;
|
||||
}
|
||||
|
||||
ipc->dsp = byt->dsp;
|
||||
|
||||
/* keep the DSP in reset state for base FW loading */
|
||||
sst_dsp_reset(byt->dsp);
|
||||
|
||||
|
@ -961,10 +756,10 @@ boot_err:
|
|||
sst_fw_free(byt_sst_fw);
|
||||
fw_err:
|
||||
sst_dsp_free(byt->dsp);
|
||||
dsp_err:
|
||||
kthread_stop(byt->tx_thread);
|
||||
err_free_msg:
|
||||
kfree(byt->msg);
|
||||
dsp_new_err:
|
||||
sst_ipc_fini(ipc);
|
||||
ipc_init_err:
|
||||
kfree(byt);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -977,7 +772,6 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
|
|||
sst_dsp_reset(byt->dsp);
|
||||
sst_fw_free_all(byt->dsp);
|
||||
sst_dsp_free(byt->dsp);
|
||||
kthread_stop(byt->tx_thread);
|
||||
kfree(byt->msg);
|
||||
sst_ipc_fini(&byt->ipc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
|
|
@ -20,8 +20,8 @@
|
|||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include "sst-baytrail-ipc.h"
|
||||
#include "sst-dsp-priv.h"
|
||||
#include "sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
|
||||
#define BYT_PCM_COUNT 2
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
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 @@
|
|||
#include <sound/jack.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "sst-dsp.h"
|
||||
#include "sst-haswell-ipc.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../haswell/sst-haswell-ipc.h"
|
||||
|
||||
#include "../codecs/rt286.h"
|
||||
#include "../../codecs/rt286.h"
|
||||
|
||||
static struct snd_soc_jack broadwell_headset;
|
||||
/* Headset jack detection DAPM pins */
|
||||
|
@ -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 */
|
||||
static struct snd_soc_card broadwell_rt286 = {
|
||||
.name = "broadwell-rt286",
|
||||
|
@ -232,6 +258,8 @@ static struct snd_soc_card broadwell_rt286 = {
|
|||
.dapm_routes = broadwell_rt286_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
|
||||
.fully_routed = true,
|
||||
.suspend_pre = broadwell_suspend,
|
||||
.resume_post = broadwell_resume,
|
||||
};
|
||||
|
||||
static int broadwell_audio_probe(struct platform_device *pdev)
|
|
@ -24,7 +24,7 @@
|
|||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include "../codecs/max98090.h"
|
||||
#include "../../codecs/max98090.h"
|
||||
|
||||
struct byt_max98090_private {
|
||||
struct snd_soc_jack jack;
|
|
@ -23,9 +23,9 @@
|
|||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.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[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
|
@ -26,8 +26,8 @@
|
|||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include "../codecs/rt5640.h"
|
||||
#include "sst-atom-controls.h"
|
||||
#include "../../codecs/rt5640.h"
|
||||
#include "../atom/sst-atom-controls.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
|
@ -27,8 +27,8 @@
|
|||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include "../codecs/rt5645.h"
|
||||
#include "sst-atom-controls.h"
|
||||
#include "../../codecs/rt5645.h"
|
||||
#include "../atom/sst-atom-controls.h"
|
||||
|
||||
#define CHT_PLAT_CLK_3_HZ 19200000
|
||||
#define CHT_CODEC_DAI "rt5645-aif1"
|
|
@ -22,13 +22,28 @@
|
|||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include "../codecs/rt5670.h"
|
||||
#include "sst-atom-controls.h"
|
||||
#include <sound/jack.h>
|
||||
#include "../../codecs/rt5670.h"
|
||||
#include "../atom/sst-atom-controls.h"
|
||||
|
||||
/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
|
||||
#define CHT_PLAT_CLK_3_HZ 19200000
|
||||
#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)
|
||||
{
|
||||
int i;
|
||||
|
@ -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_card *card = dapm->card;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
codec_dai = cht_get_codec_dai(card);
|
||||
if (!codec_dai) {
|
||||
|
@ -57,17 +73,31 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (!SND_SOC_DAPM_EVENT_OFF(event))
|
||||
return 0;
|
||||
|
||||
/* 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,
|
||||
0, SND_SOC_CLOCK_IN);
|
||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||
/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
|
||||
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
|
||||
CHT_PLAT_CLK_3_HZ, 48000 * 512);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "can't set codec pll: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -77,7 +107,8 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
|
|||
SND_SOC_DAPM_MIC("Int Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
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[] = {
|
||||
|
@ -162,6 +193,15 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
|
|||
| RT5670_AD_MONO_L_FILTER
|
||||
| RT5670_AD_MONO_R_FILTER,
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
static struct snd_soc_card snd_soc_card_cht = {
|
||||
.name = "cherrytrailcraudio",
|
||||
|
@ -262,6 +331,8 @@ static struct snd_soc_card snd_soc_card_cht = {
|
|||
.num_dapm_routes = ARRAY_SIZE(cht_audio_map),
|
||||
.controls = 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)
|
|
@ -21,10 +21,10 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "sst-dsp.h"
|
||||
#include "sst-haswell-ipc.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../haswell/sst-haswell-ipc.h"
|
||||
|
||||
#include "../codecs/rt5640.h"
|
||||
#include "../../codecs/rt5640.h"
|
||||
|
||||
/* Haswell ULT platforms have a Headphone and Mic jack */
|
||||
static const struct snd_soc_dapm_widget haswell_widgets[] = {
|
|
@ -0,0 +1,7 @@
|
|||
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 {
|
|||
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.
|
||||
*
|
||||
|
@ -203,6 +213,9 @@ struct sst_module {
|
|||
struct list_head list; /* DSP list of modules */
|
||||
struct list_head list_fw; /* FW list of modules */
|
||||
struct list_head runtime_list; /* list of runtime module objects*/
|
||||
|
||||
/* state */
|
||||
enum sst_module_state state;
|
||||
};
|
||||
|
||||
/*
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
/* Supported SST DMA Devices */
|
||||
#define SST_DMA_TYPE_DW 1
|
||||
#define SST_DMA_TYPE_MID 2
|
||||
|
||||
/* autosuspend delay 5s*/
|
||||
#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000)
|
|
@ -221,8 +221,6 @@ int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
|
|||
dma_cap_mask_t mask;
|
||||
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_set(DMA_SLAVE, mask);
|
||||
dma_cap_set(DMA_MEMCPY, mask);
|
||||
|
@ -281,9 +279,6 @@ int sst_dma_new(struct sst_dsp *sst)
|
|||
case SST_DMA_TYPE_DW:
|
||||
dma_dev_name = "dw_dmac";
|
||||
break;
|
||||
case SST_DMA_TYPE_MID:
|
||||
dma_dev_name = "Intel MID DMA";
|
||||
break;
|
||||
default:
|
||||
dev_err(sst->dev, "error: invalid DMA engine %d\n",
|
||||
sst->pdata->dma_engine);
|
||||
|
@ -502,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
|
|||
sst_module->scratch_size = template->scratch_size;
|
||||
sst_module->persistent_size = template->persistent_size;
|
||||
sst_module->entry = template->entry;
|
||||
sst_module->state = SST_MODULE_STATE_UNLOADED;
|
||||
|
||||
INIT_LIST_HEAD(&sst_module->block_list);
|
||||
INIT_LIST_HEAD(&sst_module->runtime_list);
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* 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");
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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
|
|
@ -0,0 +1,4 @@
|
|||
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 @@
|
|||
#include <linux/firmware.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "sst-dsp.h"
|
||||
#include "sst-dsp-priv.h"
|
||||
#include "sst-haswell-ipc.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "../haswell/sst-haswell-ipc.h"
|
||||
|
||||
#include <trace/events/hswadsp.h>
|
||||
|
||||
|
@ -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_REFERENCE
|
||||
&& module->type != SST_HSW_MODULE_PCM_CAPTURE
|
||||
&& module->type != SST_HSW_MODULE_WAVES
|
||||
&& module->type != SST_HSW_MODULE_LPAL)
|
||||
return 0;
|
||||
|
||||
|
@ -139,6 +140,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
|
|||
mod->type = SST_MEM_IRAM;
|
||||
break;
|
||||
case SST_HSW_DRAM:
|
||||
case SST_HSW_REGS:
|
||||
ram = dsp->addr.lpe;
|
||||
mod->offset = block->ram_offset;
|
||||
mod->type = SST_MEM_DRAM;
|
||||
|
@ -169,6 +171,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
|
|||
|
||||
block = (void *)block + sizeof(*block) + block->size;
|
||||
}
|
||||
mod->state = SST_MODULE_STATE_LOADED;
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -37,6 +37,9 @@
|
|||
#define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400
|
||||
#define SST_HSW_MAX_INFO_SIZE 64
|
||||
#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_stream;
|
||||
|
@ -187,6 +190,28 @@ enum sst_hsw_performance_action {
|
|||
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 */
|
||||
struct sst_hsw_module_info {
|
||||
u8 name[SST_HSW_MAX_INFO_SIZE];
|
||||
|
@ -215,6 +240,12 @@ struct sst_hsw_fx_enable {
|
|||
struct sst_hsw_memory_info persistent_mem;
|
||||
} __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 {
|
||||
u32 parameter_id;
|
||||
u32 param_size;
|
||||
|
@ -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);
|
||||
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 */
|
||||
struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
|
||||
int mod_id, int offset);
|
|
@ -29,9 +29,9 @@
|
|||
#include <sound/tlv.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
||||
#include "sst-haswell-ipc.h"
|
||||
#include "sst-dsp-priv.h"
|
||||
#include "sst-dsp.h"
|
||||
#include "../haswell/sst-haswell-ipc.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
|
||||
#define HSW_PCM_COUNT 6
|
||||
#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */
|
||||
|
@ -137,6 +137,7 @@ struct hsw_priv_data {
|
|||
struct device *dev;
|
||||
enum hsw_pm_state pm_state;
|
||||
struct snd_soc_card *soc_card;
|
||||
struct sst_module_runtime *runtime_waves; /* sound effect module */
|
||||
|
||||
/* page tables */
|
||||
struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
|
||||
|
@ -318,6 +319,93 @@ static int hsw_volume_get(struct snd_kcontrol *kcontrol,
|
|||
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 */
|
||||
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[] = {
|
|||
SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
|
||||
ARRAY_SIZE(volume_map) - 1, 0,
|
||||
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 */
|
||||
|
@ -807,6 +901,14 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
|
|||
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;
|
||||
|
||||
err:
|
||||
|
@ -820,14 +922,17 @@ err:
|
|||
|
||||
static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
|
||||
{
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
|
||||
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
|
||||
|
||||
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)
|
||||
|
@ -984,7 +1089,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
|
|||
}
|
||||
|
||||
/* 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 */
|
||||
pm_runtime_set_autosuspend_delay(platform->dev,
|
||||
|
@ -996,7 +1103,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
|
|||
return 0;
|
||||
|
||||
err:
|
||||
for (;i >= 0; i--) {
|
||||
for (--i; i >= 0; i--) {
|
||||
if (hsw_dais[i].playback.channels_min)
|
||||
snd_dma_free_pages(&priv_data->dmab[i][0]);
|
||||
if (hsw_dais[i].capture.channels_min)
|
||||
|
@ -1101,10 +1208,18 @@ static int hsw_pcm_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct hsw_priv_data *pdata = dev_get_drvdata(dev);
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
int ret;
|
||||
|
||||
if (pdata->pm_state >= HSW_PM_STATE_RTD3)
|
||||
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_sleep(hsw);
|
||||
pdata->pm_state = HSW_PM_STATE_RTD3;
|
||||
|
@ -1139,6 +1254,19 @@ static int hsw_pcm_runtime_resume(struct device *dev)
|
|||
else if (ret == 1) /* no action required */
|
||||
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;
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue