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

This commit is contained in:
Mark Brown 2016-07-24 22:07:22 +01:00
commit 44d624622e
34 changed files with 1997 additions and 455 deletions

View file

@ -1124,8 +1124,10 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
}
hdac_hdmi_parse_eld(edev, pin);
print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
pin->eld.eld_buffer, pin->eld.eld_size);
print_hex_dump_debug("ELD: ",
DUMP_PREFIX_OFFSET, 16, 1,
pin->eld.eld_buffer, pin->eld.eld_size,
true);
} else {
pin->eld.monitor_present = false;
pin->eld.eld_valid = false;
@ -1816,6 +1818,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = {
static const struct hda_device_id hdmi_list[] = {
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
{}
};

View file

@ -1100,6 +1100,13 @@ static const struct dmi_system_id force_combo_jack_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
}
},
{
.ident = "Intel Kabylake RVP",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
}
},
{ }
};

View file

@ -7,7 +7,7 @@ config SND_MFLD_MACHINE
help
This adds support for ASoC machine driver for Intel(R) MID Medfield platform
used as alsa device in audio substem in Intel(R) MID devices
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SST_MFLD_PLATFORM
@ -25,7 +25,6 @@ config SND_SST_IPC_ACPI
tristate
select SND_SST_IPC
select SND_SOC_INTEL_SST
depends on ACPI
config SND_SOC_INTEL_SST
tristate
@ -33,6 +32,12 @@ config SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_MATCH if ACPI
depends on (X86 || COMPILE_TEST)
# firmware stuff depends DW_DMAC_CORE; since there is no depends-on from
# the reverse selection, each machine driver needs to select
# SND_SOC_INTEL_SST_FIRMWARE carefully depending on DW_DMAC_CORE
config SND_SOC_INTEL_SST_FIRMWARE
tristate
config SND_SOC_INTEL_SST_ACPI
tristate
@ -48,16 +53,33 @@ config SND_SOC_INTEL_BAYTRAIL
config SND_SOC_INTEL_HASWELL_MACH
tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
depends on DW_DMAC_CORE=y
depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT5640
help
This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell
Ultrabook platforms.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH
tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode"
depends on X86 && ACPI && I2C
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SKYLAKE
select SND_SOC_DA7219
select SND_SOC_MAX98357A
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
select SND_HDA_DSP_LOADER
help
This adds support for ASoC machine driver for Broxton-P platforms
with DA7219 + MAX98357A I2S audio codec.
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BXT_RT298_MACH
tristate "ASoC Audio driver for Broxton with RT298 I2S mode"
depends on X86 && ACPI && I2C
@ -70,26 +92,28 @@ config SND_SOC_INTEL_BXT_RT298_MACH
help
This adds support for ASoC machine driver for Broxton platforms
with RT286 I2S audio codec.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BYT_RT5640_MACH
tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
depends on X86_INTEL_LPSS && I2C
depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n)
depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_BAYTRAIL
select SND_SOC_RT5640
help
This adds audio driver for Intel Baytrail platform based boards
with the RT5640 audio codec. This driver is deprecated, use
SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality
SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality.
config SND_SOC_INTEL_BYT_MAX98090_MACH
tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
depends on X86_INTEL_LPSS && I2C
depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n)
depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_BAYTRAIL
select SND_SOC_MAX98090
help
@ -100,19 +124,20 @@ config SND_SOC_INTEL_BROADWELL_MACH
tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
I2C_DESIGNWARE_PLATFORM
depends on DW_DMAC_CORE=y
depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT286
help
This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
Ultrabook platforms.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BYTCR_RT5640_MACH
tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"
depends on X86 && I2C
depends on X86 && I2C && ACPI
select SND_SOC_RT5640
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@ -120,12 +145,12 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH
help
This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
platforms with RT5640 audio codec.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BYTCR_RT5651_MACH
tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec"
depends on X86 && I2C
depends on X86 && I2C && ACPI
select SND_SOC_RT5651
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@ -133,12 +158,12 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
help
This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
platforms with RT5651 audio codec.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
depends on X86_INTEL_LPSS && I2C
depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_RT5670
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@ -146,12 +171,12 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
help
This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
platforms with RT5672 audio codec.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
depends on X86_INTEL_LPSS && I2C
depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_RT5645
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@ -163,16 +188,16 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec"
depends on X86_INTEL_LPSS && I2C
depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
select SND_SOC_INTEL_SST_MATCH if ACPI
help
This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
If unsure select "N".
This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
If unsure select "N".
config SND_SOC_INTEL_SKYLAKE
tristate
@ -192,7 +217,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH
help
This adds support for ASoC machine driver for Skylake platforms
with RT286 I2S audio codec.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
@ -207,7 +232,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + SSM4567.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
@ -222,5 +247,5 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + MAX98357A.
Say Y if you have such a device
Say Y if you have such a device.
If unsure select "N".

View file

@ -28,6 +28,7 @@
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <asm/platform_sst_audio.h>
#include <sound/core.h>
@ -237,6 +238,9 @@ static int sst_acpi_probe(struct platform_device *pdev)
dev_err(dev, "No matching machine driver found\n");
return -ENODEV;
}
if (mach->machine_quirk)
mach = mach->machine_quirk(mach);
pdata = mach->pdata;
ret = kstrtouint(id->id, 16, &dev_id);
@ -320,6 +324,44 @@ static int sst_acpi_remove(struct platform_device *pdev)
return 0;
}
static unsigned long cht_machine_id;
#define CHT_SURFACE_MACH 1
static int cht_surface_quirk_cb(const struct dmi_system_id *id)
{
cht_machine_id = CHT_SURFACE_MACH;
return 1;
}
static const struct dmi_system_id cht_table[] = {
{
.callback = cht_surface_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
},
},
};
static struct sst_acpi_mach cht_surface_mach = {
"10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data };
static struct sst_acpi_mach *cht_quirk(void *arg)
{
struct sst_acpi_mach *mach = arg;
dmi_check_system(cht_table);
if (cht_machine_id == CHT_SURFACE_MACH)
return &cht_surface_mach;
else
return mach;
}
static struct sst_acpi_mach sst_acpi_bytcr[] = {
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
&byt_rvp_platform_data },
@ -343,7 +385,7 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
&chv_platform_data },
{},

View file

@ -2,6 +2,7 @@ snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
snd-soc-sst-broadwell-objs := broadwell.o
snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
@ -15,6 +16,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o

View file

@ -0,0 +1,460 @@
/*
* Intel Broxton-P I2S Machine Driver
*
* Copyright (C) 2016, Intel Corporation. All rights reserved.
*
* Modified from:
* Intel Skylake I2S Machine driver
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "../../codecs/hdac_hdmi.h"
#include "../../codecs/da7219.h"
#include "../../codecs/da7219-aad.h"
#define BXT_DIALOG_CODEC_DAI "da7219-hifi"
#define BXT_MAXIM_CODEC_DAI "HiFi"
#define DUAL_CHANNEL 2
static struct snd_soc_jack broxton_headset;
enum {
BXT_DPCM_AUDIO_PB = 0,
BXT_DPCM_AUDIO_CP,
BXT_DPCM_AUDIO_REF_CP,
BXT_DPCM_AUDIO_HDMI1_PB,
BXT_DPCM_AUDIO_HDMI2_PB,
BXT_DPCM_AUDIO_HDMI3_PB,
};
static const struct snd_kcontrol_new broxton_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Spk"),
};
static const struct snd_soc_dapm_widget broxton_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SPK("Spk", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
SND_SOC_DAPM_SPK("HDMI1", NULL),
SND_SOC_DAPM_SPK("HDMI2", NULL),
SND_SOC_DAPM_SPK("HDMI3", NULL),
};
static const struct snd_soc_dapm_route broxton_map[] = {
/* HP jack connectors - unknown if we have jack detection */
{"Headphone Jack", NULL, "HPL"},
{"Headphone Jack", NULL, "HPR"},
/* speaker */
{"Spk", NULL, "Speaker"},
/* other jacks */
{"MIC", NULL, "Headset Mic"},
/* digital mics */
{"DMic", NULL, "SoC DMIC"},
/* CODEC BE connections */
{"HiFi Playback", NULL, "ssp5 Tx"},
{"ssp5 Tx", NULL, "codec0_out"},
{"Playback", NULL, "ssp1 Tx"},
{"ssp1 Tx", NULL, "codec1_out"},
{"codec0_in", NULL, "ssp1 Rx"},
{"ssp1 Rx", NULL, "Capture"},
{"HDMI1", NULL, "hif5 Output"},
{"HDMI2", NULL, "hif6 Output"},
{"HDMI3", NULL, "hif7 Output"},
{"hifi3", NULL, "iDisp3 Tx"},
{"iDisp3 Tx", NULL, "iDisp3_out"},
{"hifi2", NULL, "iDisp2 Tx"},
{"iDisp2 Tx", NULL, "iDisp2_out"},
{"hifi1", NULL, "iDisp1 Tx"},
{"iDisp1 Tx", NULL, "iDisp1_out"},
/* DMIC */
{"dmic01_hifi", NULL, "DMIC01 Rx"},
{"DMIC01 Rx", NULL, "DMIC AIF"},
};
static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = DUAL_CHANNEL;
/* set SSP to 24 bit */
snd_mask_none(fmt);
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
{
int ret;
struct snd_soc_codec *codec = rtd->codec;
/*
* Headset buttons map to the google Reference headset.
* These can be configured by userspace.
*/
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset,
NULL, 0);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
}
da7219_aad_jack_det(codec, &broxton_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
return ret;
}
static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
}
static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dapm_context *dapm;
struct snd_soc_component *component = rtd->cpu_dai->component;
dapm = snd_soc_component_get_dapm(component);
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
return 0;
}
static unsigned int rates[] = {
48000,
};
static struct snd_pcm_hw_constraint_list constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static unsigned int channels[] = {
DUAL_CHANNEL,
};
static struct snd_pcm_hw_constraint_list constraints_channels = {
.count = ARRAY_SIZE(channels),
.list = channels,
.mask = 0,
};
static int bxt_fe_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
/*
* On this platform for PCM device we support,
* 48Khz
* stereo
* 16 bit audio
*/
runtime->hw.channels_max = DUAL_CHANNEL;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_channels);
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
return 0;
}
static const struct snd_soc_ops broxton_da7219_fe_ops = {
.startup = bxt_fe_startup,
};
static int broxton_da7219_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai,
DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
ret = snd_soc_dai_set_pll(codec_dai, 0,
DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304);
if (ret < 0) {
dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
return -EIO;
}
return ret;
}
static int broxton_da7219_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
ret = snd_soc_dai_set_pll(codec_dai, 0,
DA7219_SYSCLK_MCLK, 0, 0);
if (ret < 0) {
dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
return -EIO;
}
return ret;
}
static struct snd_soc_ops broxton_da7219_ops = {
.hw_params = broxton_da7219_hw_params,
.hw_free = broxton_da7219_hw_free,
};
/* broxton digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broxton_dais[] = {
/* Front End DAI links */
[BXT_DPCM_AUDIO_PB]
{
.name = "Bxt Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
.platform_name = "0000:00:0e.0",
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.nonatomic = 1,
.init = broxton_da7219_fe_init,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
.ops = &broxton_da7219_fe_ops,
},
[BXT_DPCM_AUDIO_CP]
{
.name = "Bxt Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
.platform_name = "0000:00:0e.0",
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.nonatomic = 1,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
.ops = &broxton_da7219_fe_ops,
},
[BXT_DPCM_AUDIO_REF_CP]
{
.name = "Bxt Audio Reference cap",
.stream_name = "Refcap",
.cpu_dai_name = "Reference Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.init = NULL,
.dpcm_capture = 1,
.ignore_suspend = 1,
.nonatomic = 1,
.dynamic = 1,
},
[BXT_DPCM_AUDIO_HDMI1_PB]
{
.name = "Bxt HDMI Port1",
.stream_name = "Hdmi1",
.cpu_dai_name = "HDMI1 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
[BXT_DPCM_AUDIO_HDMI2_PB]
{
.name = "Bxt HDMI Port2",
.stream_name = "Hdmi2",
.cpu_dai_name = "HDMI2 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
[BXT_DPCM_AUDIO_HDMI3_PB]
{
.name = "Bxt HDMI Port3",
.stream_name = "Hdmi3",
.cpu_dai_name = "HDMI3 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
/* Back End DAI links */
{
/* SSP5 - Codec */
.name = "SSP5-Codec",
.id = 0,
.cpu_dai_name = "SSP5 Pin",
.platform_name = "0000:00:0e.0",
.no_pcm = 1,
.codec_name = "MX98357A:00",
.codec_dai_name = BXT_MAXIM_CODEC_DAI,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broxton_ssp_fixup,
.dpcm_playback = 1,
},
{
/* SSP1 - Codec */
.name = "SSP1-Codec",
.id = 1,
.cpu_dai_name = "SSP1 Pin",
.platform_name = "0000:00:0e.0",
.no_pcm = 1,
.codec_name = "i2c-DLGS7219:00",
.codec_dai_name = BXT_DIALOG_CODEC_DAI,
.init = broxton_da7219_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broxton_ssp_fixup,
.ops = &broxton_da7219_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
{
.name = "dmic01",
.id = 2,
.cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
.platform_name = "0000:00:0e.0",
.ignore_suspend = 1,
.dpcm_capture = 1,
.no_pcm = 1,
},
{
.name = "iDisp1",
.id = 3,
.cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1",
.platform_name = "0000:00:0e.0",
.init = broxton_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp2",
.id = 4,
.cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2",
.platform_name = "0000:00:0e.0",
.init = broxton_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp3",
.id = 5,
.cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3",
.platform_name = "0000:00:0e.0",
.init = broxton_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
};
/* broxton audio machine driver for SPT + da7219 */
static struct snd_soc_card broxton_audio_card = {
.name = "bxtda7219max",
.owner = THIS_MODULE,
.dai_link = broxton_dais,
.num_links = ARRAY_SIZE(broxton_dais),
.controls = broxton_controls,
.num_controls = ARRAY_SIZE(broxton_controls),
.dapm_widgets = broxton_widgets,
.num_dapm_widgets = ARRAY_SIZE(broxton_widgets),
.dapm_routes = broxton_map,
.num_dapm_routes = ARRAY_SIZE(broxton_map),
.fully_routed = true,
};
static int broxton_audio_probe(struct platform_device *pdev)
{
broxton_audio_card.dev = &pdev->dev;
return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card);
}
static struct platform_driver broxton_audio = {
.probe = broxton_audio_probe,
.driver = {
.name = "bxt_da7219_max98357a_i2s",
.pm = &snd_soc_pm_ops,
},
};
module_platform_driver(broxton_audio)
/* Module information */
MODULE_DESCRIPTION("Audio Machine driver-DA7219 & MAX98357A in I2S mode");
MODULE_AUTHOR("Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>");
MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>");
MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>");
MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s");

View file

@ -33,6 +33,7 @@ enum {
BXT_DPCM_AUDIO_PB = 0,
BXT_DPCM_AUDIO_CP,
BXT_DPCM_AUDIO_REF_CP,
BXT_DPCM_AUDIO_DMIC_CP,
BXT_DPCM_AUDIO_HDMI1_PB,
BXT_DPCM_AUDIO_HDMI2_PB,
BXT_DPCM_AUDIO_HDMI3_PB,
@ -88,6 +89,7 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
/* CODEC BE connections */
{ "AIF1 Playback", NULL, "ssp5 Tx"},
{ "ssp5 Tx", NULL, "codec0_out"},
{ "ssp5 Tx", NULL, "codec1_out"},
{ "codec0_in", NULL, "ssp5 Rx" },
{ "ssp5 Rx", NULL, "AIF1 Capture" },
@ -104,6 +106,17 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
};
static int broxton_rt298_fe_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dapm_context *dapm;
struct snd_soc_component *component = rtd->cpu_dai->component;
dapm = snd_soc_component_get_dapm(component);
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
return 0;
}
static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
@ -118,6 +131,9 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret;
rt298_mic_detect(codec, &broxton_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
return 0;
}
@ -169,6 +185,89 @@ static struct snd_soc_ops broxton_rt298_ops = {
.hw_params = broxton_rt298_hw_params,
};
static unsigned int rates[] = {
48000,
};
static struct snd_pcm_hw_constraint_list constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (params_channels(params) == 2)
channels->min = channels->max = 2;
else
channels->min = channels->max = 4;
return 0;
}
static unsigned int channels_dmic[] = {
2, 4,
};
static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
.count = ARRAY_SIZE(channels_dmic),
.list = channels_dmic,
.mask = 0,
};
static int broxton_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw.channels_max = 4;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_dmic_channels);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
}
static struct snd_soc_ops broxton_dmic_ops = {
.startup = broxton_dmic_startup,
};
static unsigned int channels[] = {
2,
};
static struct snd_pcm_hw_constraint_list constraints_channels = {
.count = ARRAY_SIZE(channels),
.list = channels,
.mask = 0,
};
static int bxt_fe_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
/*
* on this platform for PCM device we support:
* 48Khz
* stereo
*/
runtime->hw.channels_max = 2;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_channels);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
return 0;
}
static const struct snd_soc_ops broxton_rt286_fe_ops = {
.startup = bxt_fe_startup,
};
/* broxton digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broxton_rt298_dais[] = {
/* Front End DAI links */
@ -182,8 +281,10 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.init = broxton_rt298_fe_init,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
.ops = &broxton_rt286_fe_ops,
},
[BXT_DPCM_AUDIO_CP]
{
@ -197,6 +298,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
.ops = &broxton_rt286_fe_ops,
},
[BXT_DPCM_AUDIO_REF_CP]
{
@ -211,6 +313,20 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1,
.dynamic = 1,
},
[BXT_DPCM_AUDIO_DMIC_CP]
{
.name = "Bxt Audio DMIC cap",
.stream_name = "dmiccap",
.cpu_dai_name = "DMIC Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.init = NULL,
.dpcm_capture = 1,
.nonatomic = 1,
.dynamic = 1,
.ops = &broxton_dmic_ops,
},
[BXT_DPCM_AUDIO_HDMI1_PB]
{
.name = "Bxt HDMI Port1",
@ -276,6 +392,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
.platform_name = "0000:00:0e.0",
.be_hw_params_fixup = broxton_dmic_fixup,
.ignore_suspend = 1,
.dpcm_capture = 1,
.no_pcm = 1,
@ -341,6 +458,7 @@ static struct platform_driver broxton_audio = {
.probe = broxton_audio_probe,
.driver = {
.name = "bxt_alc298s_i2s",
.pm = &snd_soc_pm_ops,
},
};
module_platform_driver(broxton_audio)

View file

@ -30,6 +30,7 @@
#include <sound/jack.h>
#include "../../codecs/rt5645.h"
#include "../atom/sst-atom-controls.h"
#include "../common/sst-acpi.h"
#define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "rt5645-aif1"
@ -340,10 +341,13 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {
};
static struct cht_acpi_card snd_soc_cards[] = {
{"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
};
static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
@ -351,6 +355,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
char codec_name[16];
struct sst_acpi_mach *mach;
const char *i2c_name = NULL;
int dai_index = 0;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv)
@ -366,12 +373,23 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
}
}
card->dev = &pdev->dev;
mach = card->dev->platform_data;
sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
/* set correct codec name */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00"))
if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
dai_index = i;
}
/* fixup codec name based on HID */
i2c_name = sst_acpi_find_name_from_hid(mach->id);
if (i2c_name != NULL) {
snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
"%s%s", "i2c-", i2c_name);
cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
}
snd_soc_card_set_drvdata(card, drv);
ret_val = devm_snd_soc_register_card(&pdev->dev, card);

View file

@ -23,12 +23,15 @@
#include <sound/soc.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
#include "../skylake/skl.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_MAXIM_CODEC_DAI "HiFi"
#define DMIC_CH(p) p->list[p->count-1]
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
static const struct snd_pcm_hw_constraint_list *dmic_constraints;
struct skl_hdmi_pcm {
struct list_head head;
@ -339,7 +342,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (params_channels(params) == 2)
if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
channels->min = channels->max = 2;
else
channels->min = channels->max = 4;
@ -357,13 +360,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
.mask = 0,
};
static const unsigned int dmic_2ch[] = {
2,
};
static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
.count = ARRAY_SIZE(dmic_2ch),
.list = dmic_2ch,
.mask = 0,
};
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw.channels_max = 4;
runtime->hw.channels_max = DMIC_CH(dmic_constraints);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_dmic_channels);
dmic_constraints);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@ -382,8 +395,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
.list = rates_16000,
};
static const unsigned int ch_mono[] = {
1,
};
static const struct snd_pcm_hw_constraint_list constraints_refcap = {
.count = ARRAY_SIZE(ch_mono),
.list = ch_mono,
};
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
{
substream->runtime->hw.channels_max = 1;
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_refcap);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_16000);
@ -610,6 +637,7 @@ static struct snd_soc_card skylake_audio_card = {
static int skylake_audio_probe(struct platform_device *pdev)
{
struct skl_nau8825_private *ctx;
struct skl_machine_pdata *pdata;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
@ -620,15 +648,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
pdata = dev_get_drvdata(&pdev->dev);
if (pdata)
dmic_constraints = pdata->dmic_num == 2 ?
&constraints_dmic_2ch : &constraints_dmic_channels;
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
}
static const struct platform_device_id skl_board_ids[] = {
{ .name = "skl_n88l25_m98357a" },
{ .name = "kbl_n88l25_m98357a" },
{ }
};
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
.name = "skl_nau88l25_max98357a_i2s",
.name = "skl_n88l25_m98357a",
.pm = &snd_soc_pm_ops,
},
.id_table = skl_board_ids,
};
module_platform_driver(skylake_audio)
@ -637,4 +677,5 @@ module_platform_driver(skylake_audio)
MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode");
MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s");
MODULE_ALIAS("platform:skl_n88l25_m98357a");
MODULE_ALIAS("platform:kbl_n88l25_m98357a");

View file

@ -27,12 +27,15 @@
#include <sound/pcm_params.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
#include "../skylake/skl.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
#define DMIC_CH(p) p->list[p->count-1]
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
static const struct snd_pcm_hw_constraint_list *dmic_constraints;
struct skl_hdmi_pcm {
struct list_head head;
@ -367,7 +370,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
{
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (params_channels(params) == 2)
if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
channels->min = channels->max = 2;
else
channels->min = channels->max = 4;
@ -405,13 +408,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
.mask = 0,
};
static const unsigned int dmic_2ch[] = {
2,
};
static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
.count = ARRAY_SIZE(dmic_2ch),
.list = dmic_2ch,
.mask = 0,
};
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw.channels_max = 4;
runtime->hw.channels_max = DMIC_CH(dmic_constraints);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_dmic_channels);
dmic_constraints);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@ -430,8 +443,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
.list = rates_16000,
};
static const unsigned int ch_mono[] = {
1,
};
static const struct snd_pcm_hw_constraint_list constraints_refcap = {
.count = ARRAY_SIZE(ch_mono),
.list = ch_mono,
};
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
{
substream->runtime->hw.channels_max = 1;
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_refcap);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_16000);
@ -662,6 +689,7 @@ static struct snd_soc_card skylake_audio_card = {
static int skylake_audio_probe(struct platform_device *pdev)
{
struct skl_nau88125_private *ctx;
struct skl_machine_pdata *pdata;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
@ -672,15 +700,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
pdata = dev_get_drvdata(&pdev->dev);
if (pdata)
dmic_constraints = pdata->dmic_num == 2 ?
&constraints_dmic_2ch : &constraints_dmic_channels;
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
}
static const struct platform_device_id skl_board_ids[] = {
{ .name = "skl_n88l25_s4567" },
{ .name = "kbl_n88l25_s4567" },
{ }
};
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
.name = "skl_nau88l25_ssm4567_i2s",
.name = "skl_n88l25_s4567",
.pm = &snd_soc_pm_ops,
},
.id_table = skl_board_ids,
};
module_platform_driver(skylake_audio)
@ -693,4 +733,5 @@ MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s");
MODULE_ALIAS("platform:skl_n88l25_s4567");
MODULE_ALIAS("platform:kbl_n88l25_s4567");

View file

@ -505,12 +505,20 @@ static int skylake_audio_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
}
static const struct platform_device_id skl_board_ids[] = {
{ .name = "skl_alc286s_i2s" },
{ .name = "kbl_alc286s_i2s" },
{ }
};
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
.name = "skl_alc286s_i2s",
.pm = &snd_soc_pm_ops,
},
.id_table = skl_board_ids,
};
module_platform_driver(skylake_audio)
@ -520,3 +528,4 @@ MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
MODULE_DESCRIPTION("Intel SST Audio for Skylake");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:skl_alc286s_i2s");
MODULE_ALIAS("platform:kbl_alc286s_i2s");

View file

@ -2,9 +2,9 @@ snd-soc-sst-dsp-objs := sst-dsp.o
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-match-objs := sst-match-acpi.o
snd-soc-sst-ipc-objs := sst-ipc.o
snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o
snd-soc-sst-firmware-objs := sst-firmware.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
obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o
obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o

View file

@ -20,7 +20,7 @@
#if IS_ENABLED(CONFIG_ACPI)
const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
#else
inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
{
return NULL;
}
@ -40,6 +40,6 @@ struct sst_acpi_mach {
/* board name */
const char *board;
void (*machine_quirk)(void);
struct sst_acpi_mach * (*machine_quirk)(void *arg);
void *pdata;
};

View file

@ -383,10 +383,6 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
u32 index, void *private);
void sst_mem_block_unregister_all(struct sst_dsp *dsp);
/* Create/Free DMA resources */
int sst_dma_new(struct sst_dsp *sst);
void sst_dma_free(struct sst_dma *dma);
u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
enum sst_mem_type type);
#endif

View file

@ -285,7 +285,7 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
}
reg = sst_dsp_shim_read_unlocked(ctx, offset);
dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
(time < timeout) ? "successful" : "timedout");
ret = time < timeout ? 0 : -ETIME;
@ -420,73 +420,6 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
}
EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
#ifdef CONFIG_DW_DMAC_CORE
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
{
struct sst_dsp *sst;
int err;
dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
if (sst == NULL)
return NULL;
spin_lock_init(&sst->spinlock);
mutex_init(&sst->mutex);
sst->dev = dev;
sst->dma_dev = pdata->dma_dev;
sst->thread_context = sst_dev->thread_context;
sst->sst_dev = sst_dev;
sst->id = pdata->id;
sst->irq = pdata->irq;
sst->ops = sst_dev->ops;
sst->pdata = pdata;
INIT_LIST_HEAD(&sst->used_block_list);
INIT_LIST_HEAD(&sst->free_block_list);
INIT_LIST_HEAD(&sst->module_list);
INIT_LIST_HEAD(&sst->fw_list);
INIT_LIST_HEAD(&sst->scratch_block_list);
/* Initialise SST Audio DSP */
if (sst->ops->init) {
err = sst->ops->init(sst, pdata);
if (err < 0)
return NULL;
}
/* Register the ISR */
err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
if (err)
goto irq_err;
err = sst_dma_new(sst);
if (err)
dev_warn(dev, "sst_dma_new failed %d\n", err);
return sst;
irq_err:
if (sst->ops->free)
sst->ops->free(sst);
return NULL;
}
EXPORT_SYMBOL_GPL(sst_dsp_new);
void sst_dsp_free(struct sst_dsp *sst)
{
free_irq(sst->irq, sst);
if (sst->ops->free)
sst->ops->free(sst);
sst_dma_free(sst->dma);
}
EXPORT_SYMBOL_GPL(sst_dsp_free);
#endif
/* Module information */
MODULE_AUTHOR("Liam Girdwood");
MODULE_DESCRIPTION("Intel SST Core");

View file

@ -216,7 +216,7 @@ struct sst_pdata {
void *dsp;
};
#ifdef CONFIG_DW_DMAC_CORE
#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
/* Initialization */
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);

View file

@ -1211,3 +1211,71 @@ u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
}
}
EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
{
struct sst_dsp *sst;
int err;
dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
if (sst == NULL)
return NULL;
spin_lock_init(&sst->spinlock);
mutex_init(&sst->mutex);
sst->dev = dev;
sst->dma_dev = pdata->dma_dev;
sst->thread_context = sst_dev->thread_context;
sst->sst_dev = sst_dev;
sst->id = pdata->id;
sst->irq = pdata->irq;
sst->ops = sst_dev->ops;
sst->pdata = pdata;
INIT_LIST_HEAD(&sst->used_block_list);
INIT_LIST_HEAD(&sst->free_block_list);
INIT_LIST_HEAD(&sst->module_list);
INIT_LIST_HEAD(&sst->fw_list);
INIT_LIST_HEAD(&sst->scratch_block_list);
/* Initialise SST Audio DSP */
if (sst->ops->init) {
err = sst->ops->init(sst, pdata);
if (err < 0)
return NULL;
}
/* Register the ISR */
err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
if (err)
goto irq_err;
err = sst_dma_new(sst);
if (err)
dev_warn(dev, "sst_dma_new failed %d\n", err);
return sst;
irq_err:
if (sst->ops->free)
sst->ops->free(sst);
return NULL;
}
EXPORT_SYMBOL_GPL(sst_dsp_new);
void sst_dsp_free(struct sst_dsp *sst)
{
free_irq(sst->irq, sst);
if (sst->ops->free)
sst->ops->free(sst);
sst_dma_free(sst->dma);
}
EXPORT_SYMBOL_GPL(sst_dsp_free);
MODULE_DESCRIPTION("Intel SST Firmware Loader");
MODULE_LICENSE("GPL v2");

View file

@ -819,7 +819,6 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
mutex_lock(&pcm_data->mutex);
pm_runtime_get_sync(pdata->dev);
snd_soc_pcm_set_drvdata(rtd, pcm_data);
pcm_data->substream = substream;
snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);

View file

@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
# Skylake IPC Support
snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
skl-sst.o bxt-sst.o
skl-sst.o bxt-sst.o skl-sst-utils.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o

View file

@ -37,11 +37,19 @@
#define BXT_ADSP_SRAM1_BASE 0xA0000
#define BXT_INSTANCE_ID 0
#define BXT_BASE_FW_MODULE_ID 0
static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
{
return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
}
/*
* First boot sequence has some extra steps. Core 0 waits for power
* status on core 1, so power up core 1 also momentarily, keep it in
* reset/stall and then turn it off
*/
static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
const void *fwdata, u32 fwsize)
{
@ -49,7 +57,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
u32 reg;
stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
if (stream_tag < 0) {
if (stream_tag <= 0) {
dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n",
stream_tag);
return stream_tag;
@ -58,17 +66,27 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
ctx->dsp_ops.stream_tag = stream_tag;
memcpy(ctx->dmab.area, fwdata, fwsize);
/* Purge FW request */
sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
BXT_IPC_PURGE_FW | (stream_tag - 1));
ret = skl_dsp_enable_core(ctx);
/* Step 1: Power up core 0 and core1 */
ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK |
SKL_DSP_CORE_MASK(1));
if (ret < 0) {
dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
dev_err(ctx->dev, "dsp core0/1 power up failed\n");
goto base_fw_load_failed;
}
/* Step 2: Purge FW request */
sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
(BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9)));
/* Step 3: Unset core0 reset state & unstall/run core0 */
ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret);
ret = -EIO;
goto base_fw_load_failed;
}
/* Step 4: Wait for DONE Bit */
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE);
@ -88,10 +106,18 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
SKL_ADSP_REG_HIPCIE_DONE);
}
/* enable Interrupt */
/* Step 5: power down core1 */
ret = skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
if (ret < 0) {
dev_err(ctx->dev, "dsp core1 power down failed\n");
goto base_fw_load_failed;
}
/* Step 6: Enable Interrupt */
skl_ipc_int_enable(ctx);
skl_ipc_op_int_enable(ctx);
/* Step 7: Wait for ROM init */
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
if (SKL_FW_INIT ==
(sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) &
@ -112,7 +138,8 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
base_fw_load_failed:
ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
skl_dsp_disable_core(ctx);
skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
return ret;
}
@ -130,23 +157,41 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
return ret;
}
#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
static int bxt_load_base_firmware(struct sst_dsp *ctx)
{
const struct firmware *fw = NULL;
struct firmware stripped_fw;
struct skl_sst *skl = ctx->thread_context;
int ret;
ret = request_firmware(&fw, ctx->fw_name, ctx->dev);
ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
if (ret < 0) {
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
goto sst_load_base_firmware_failed;
}
ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
/* check for extended manifest */
if (ctx->fw == NULL)
goto sst_load_base_firmware_failed;
ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET);
if (ret < 0)
goto sst_load_base_firmware_failed;
stripped_fw.data = ctx->fw->data;
stripped_fw.size = ctx->fw->size;
skl_dsp_strip_extended_manifest(&stripped_fw);
ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
/* Retry Enabling core and ROM load. Retry seemed to help */
if (ret < 0) {
ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
if (ret < 0) {
dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
goto sst_load_base_firmware_failed;
}
@ -159,83 +204,135 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
skl_dsp_disable_core(ctx);
skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
} else {
dev_dbg(ctx->dev, "Firmware download successful\n");
ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
if (ret == 0) {
dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n");
skl_dsp_disable_core(ctx);
skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
ret = -EIO;
} else {
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
ret = 0;
skl->fw_loaded = true;
}
}
sst_load_base_firmware_failed:
release_firmware(fw);
release_firmware(ctx->fw);
return ret;
}
static int bxt_set_dsp_D0(struct sst_dsp *ctx)
static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
{
struct skl_sst *skl = ctx->thread_context;
int ret;
struct skl_ipc_dxstate_info dx;
unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
skl->boot_complete = false;
ret = skl_dsp_enable_core(ctx);
if (ret < 0) {
dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret);
if (skl->fw_loaded == false) {
skl->boot_complete = false;
ret = bxt_load_base_firmware(ctx);
if (ret < 0)
dev_err(ctx->dev, "reload fw failed: %d\n", ret);
return ret;
}
/* enable interrupt */
skl_ipc_int_enable(ctx);
skl_ipc_op_int_enable(ctx);
/* If core 0 is being turned on, turn on core 1 as well */
if (core_id == SKL_DSP_CORE0_ID)
ret = skl_dsp_core_power_up(ctx, core_mask |
SKL_DSP_CORE_MASK(1));
else
ret = skl_dsp_core_power_up(ctx, core_mask);
ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
if (ret == 0) {
dev_err(ctx->dev, "ipc: error DSP boot timeout\n");
dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
return -EIO;
if (ret < 0)
goto err;
if (core_id == SKL_DSP_CORE0_ID) {
/*
* Enable interrupt after SPA is set and before
* DSP is unstalled
*/
skl_ipc_int_enable(ctx);
skl_ipc_op_int_enable(ctx);
skl->boot_complete = false;
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
ret = skl_dsp_start_core(ctx, core_mask);
if (ret < 0)
goto err;
if (core_id == SKL_DSP_CORE0_ID) {
ret = wait_event_timeout(skl->boot_wait,
skl->boot_complete,
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
/* If core 1 was turned on for booting core 0, turn it off */
skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
if (ret == 0) {
dev_err(ctx->dev, "%s: DSP boot timeout\n", __func__);
dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
dev_err(ctx->dev, "Failed to set core0 to D0 state\n");
ret = -EIO;
goto err;
}
}
/* Tell FW if additional core in now On */
if (core_id != SKL_DSP_CORE0_ID) {
dx.core_mask = core_mask;
dx.dx_mask = core_mask;
ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
BXT_BASE_FW_MODULE_ID, &dx);
if (ret < 0) {
dev_err(ctx->dev, "IPC set_dx for core %d fail: %d\n",
core_id, ret);
goto err;
}
}
skl->cores.state[core_id] = SKL_DSP_RUNNING;
return 0;
err:
if (core_id == SKL_DSP_CORE0_ID)
core_mask |= SKL_DSP_CORE_MASK(1);
skl_dsp_disable_core(ctx, core_mask);
return ret;
}
static int bxt_set_dsp_D3(struct sst_dsp *ctx)
static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
{
int ret;
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
int ret = 0;
unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
if (!is_skl_dsp_running(ctx))
return ret;
dx.core_mask = SKL_DSP_CORE0_MASK;
dx.core_mask = core_mask;
dx.dx_mask = SKL_IPC_D3_MASK;
ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
SKL_BASE_FW_MODULE_ID, &dx);
dev_dbg(ctx->dev, "core mask=%x dx_mask=%x\n",
dx.core_mask, dx.dx_mask);
ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
BXT_BASE_FW_MODULE_ID, &dx);
if (ret < 0)
dev_err(ctx->dev,
"Failed to set DSP to D3:core id = %d;Continue reset\n",
core_id);
ret = skl_dsp_disable_core(ctx, core_mask);
if (ret < 0) {
dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret);
dev_err(ctx->dev, "Failed to disable core %d", ret);
return ret;
}
ret = skl_dsp_disable_core(ctx);
if (ret < 0) {
dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret);
ret = -EIO;
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
skl->cores.state[core_id] = SKL_DSP_RESET;
return 0;
}
@ -274,6 +371,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
skl->dev = dev;
skl_dev.thread_context = skl;
INIT_LIST_HEAD(&skl->uuid_list);
skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
if (!skl->dsp) {
@ -296,6 +394,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
if (ret)
return ret;
skl->cores.count = 2;
skl->boot_complete = false;
init_waitqueue_head(&skl->boot_wait);
@ -305,6 +404,8 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
return ret;
}
skl_dsp_init_core_state(sst);
if (dsp)
*dsp = skl;
@ -315,6 +416,7 @@ EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
skl_freeup_uuid_list(ctx);
skl_ipc_free(&ctx->ipc);
ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);

View file

@ -205,6 +205,12 @@ static const struct skl_dsp_ops dsp_ops[] = {
.init = skl_sst_dsp_init,
.cleanup = skl_sst_dsp_cleanup
},
{
.id = 0x9d71,
.loader_ops = skl_get_loader_ops,
.init = skl_sst_dsp_init,
.cleanup = skl_sst_dsp_cleanup
},
{
.id = 0x5a98,
.loader_ops = bxt_get_loader_ops,
@ -730,7 +736,7 @@ static int skl_set_module_format(struct skl_sst *ctx,
dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n",
module_config->id.module_id, param_size);
print_hex_dump(KERN_DEBUG, "Module params:", DUMP_PREFIX_OFFSET, 8, 4,
print_hex_dump_debug("Module params:", DUMP_PREFIX_OFFSET, 8, 4,
*param_data, param_size, false);
return 0;
}
@ -1046,7 +1052,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
/* If pipe is not started, do not try to stop the pipe in FW. */
/* If pipe is started, do stop the pipe in FW. */
if (pipe->state > SKL_PIPE_STARTED) {
ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
if (ret < 0) {
@ -1055,18 +1061,20 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
}
pipe->state = SKL_PIPE_PAUSED;
} else {
/* If pipe was not created in FW, do not try to delete it */
if (pipe->state < SKL_PIPE_CREATED)
return 0;
ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
if (ret < 0)
dev_err(ctx->dev, "Failed to delete pipeline\n");
pipe->state = SKL_PIPE_INVALID;
}
/* If pipe was not created in FW, do not try to delete it */
if (pipe->state < SKL_PIPE_CREATED)
return 0;
ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
if (ret < 0) {
dev_err(ctx->dev, "Failed to delete pipeline\n");
return ret;
}
pipe->state = SKL_PIPE_INVALID;
return ret;
}
@ -1125,7 +1133,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
return ret;
}
pipe->state = SKL_PIPE_CREATED;
pipe->state = SKL_PIPE_PAUSED;
return 0;
}
/*
* Reset the pipeline by sending set pipe state IPC this will reset the DMA
* from the DSP side
*/
int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
{
int ret;
/* If pipe was not created in FW, do not try to pause or delete */
if (pipe->state < SKL_PIPE_PAUSED)
return 0;
ret = skl_set_pipe_state(ctx, pipe, PPL_RESET);
if (ret < 0) {
dev_dbg(ctx->dev, "Failed to reset pipe ret=%d\n", ret);
return ret;
}
pipe->state = SKL_PIPE_RESET;
return 0;
}

View file

@ -17,6 +17,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
#include <linux/pci.h>
#include "skl.h"
/* Unique identification for getting NHLT blobs */
@ -149,6 +150,45 @@ struct nhlt_specific_cfg
return NULL;
}
int skl_get_dmic_geo(struct skl *skl)
{
struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
struct nhlt_endpoint *epnt;
struct nhlt_dmic_array_config *cfg;
struct device *dev = &skl->pci->dev;
unsigned int dmic_geo = 0;
u8 j;
epnt = (struct nhlt_endpoint *)nhlt->desc;
for (j = 0; j < nhlt->endpoint_count; j++) {
if (epnt->linktype == NHLT_LINK_DMIC) {
cfg = (struct nhlt_dmic_array_config *)
(epnt->config.caps);
switch (cfg->array_type) {
case NHLT_MIC_ARRAY_2CH_SMALL:
case NHLT_MIC_ARRAY_2CH_BIG:
dmic_geo |= MIC_ARRAY_2CH;
break;
case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
case NHLT_MIC_ARRAY_4CH_L_SHAPED:
case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
dmic_geo |= MIC_ARRAY_4CH;
break;
default:
dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
cfg->array_type);
}
}
epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
}
return dmic_geo;
}
static void skl_nhlt_trim_space(struct skl *skl)
{
char *s = skl->tplg_name;

View file

@ -103,4 +103,26 @@ struct nhlt_resource_desc {
u64 length;
} __packed;
#define MIC_ARRAY_2CH 2
#define MIC_ARRAY_4CH 4
struct nhlt_tdm_config {
u8 virtual_slot;
u8 config_type;
} __packed;
struct nhlt_dmic_array_config {
struct nhlt_tdm_config tdm_config;
u8 array_type;
} __packed;
enum {
NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
NHLT_MIC_ARRAY_2CH_BIG = 0xb,
NHLT_MIC_ARRAY_4CH_1ST_GEOM = 0xc,
NHLT_MIC_ARRAY_4CH_L_SHAPED = 0xd,
NHLT_MIC_ARRAY_4CH_2ND_GEOM = 0xe,
NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
};
#endif

View file

@ -227,16 +227,25 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
struct skl *skl = get_skl_ctx(dai->dev);
unsigned int format_val;
int err;
struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
format_val = skl_get_format(substream, dai);
dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
hdac_stream(stream)->stream_tag, format_val);
snd_hdac_stream_reset(hdac_stream(stream));
/* In case of XRUN recovery, reset the FW pipe to clean state */
if (mconfig && (substream->runtime->status->state ==
SNDRV_PCM_STATE_XRUN))
skl_reset_pipe(skl->skl_sst, mconfig->pipe);
err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
if (err < 0)
return err;
@ -521,6 +530,8 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
struct skl_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct hdac_ext_link *link;
struct skl *skl = get_skl_ctx(dai->dev);
struct skl_module_cfg *mconfig = NULL;
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
@ -535,6 +546,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
snd_hdac_ext_link_stream_reset(link_dev);
/* In case of XRUN recovery, reset the FW pipe to clean state */
mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
if (mconfig && (substream->runtime->status->state ==
SNDRV_PCM_STATE_XRUN))
skl_reset_pipe(skl->skl_sst, mconfig->pipe);
snd_hdac_ext_link_stream_setup(link_dev, format_val);
snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
@ -1009,51 +1026,11 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
/* calculate runtime delay from LPIB */
static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
struct hdac_ext_stream *sstream,
unsigned int pos)
static snd_pcm_uframes_t skl_platform_pcm_pointer
(struct snd_pcm_substream *substream)
{
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct hdac_stream *hstream = hdac_stream(sstream);
struct snd_pcm_substream *substream = hstream->substream;
int stream = substream->stream;
unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream);
int delay;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
delay = pos - lpib_pos;
else
delay = lpib_pos - pos;
if (delay < 0) {
if (delay >= hstream->delay_negative_threshold)
delay = 0;
else
delay += hstream->bufsize;
}
if (hstream->bufsize == delay)
delay = 0;
if (delay >= hstream->period_bytes) {
dev_info(bus->dev,
"Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
delay, hstream->period_bytes);
delay = 0;
}
return bytes_to_frames(substream->runtime, delay);
}
static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
int codec_delay)
{
struct hdac_stream *hstr = hdac_stream(hstream);
struct snd_pcm_substream *substream = hstr->substream;
struct hdac_ext_bus *ebus;
struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
unsigned int pos;
int delay;
/* use the position buffer as default */
pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
@ -1061,23 +1038,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
if (pos >= hdac_stream(hstream)->bufsize)
pos = 0;
if (substream->runtime) {
ebus = get_bus_ctx(substream);
delay = skl_get_delay_from_lpib(ebus, hstream, pos)
+ codec_delay;
substream->runtime->delay += delay;
}
return pos;
}
static snd_pcm_uframes_t skl_platform_pcm_pointer
(struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
return bytes_to_frames(substream->runtime,
skl_get_position(hstream, 0));
return bytes_to_frames(substream->runtime, pos);
}
static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
@ -1180,9 +1141,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
static int skl_platform_soc_probe(struct snd_soc_platform *platform)
{
struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
struct skl *skl = ebus_to_skl(ebus);
int ret;
if (ebus->ppcap)
return skl_tplg_init(platform, ebus);
if (ebus->ppcap) {
ret = skl_tplg_init(platform, ebus);
if (ret < 0) {
dev_err(platform->dev, "Failed to init topology!\n");
return ret;
}
skl->platform = platform;
}
return 0;
}

View file

@ -34,33 +34,84 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
mutex_unlock(&ctx->mutex);
}
static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
/*
* Initialize core power state and usage count. To be called after
* successful first boot. Hence core 0 will be running and other cores
* will be reset
*/
void skl_dsp_init_core_state(struct sst_dsp *ctx)
{
struct skl_sst *skl = ctx->thread_context;
int i;
skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING;
skl->cores.usage_count[SKL_DSP_CORE0_ID] = 1;
for (i = SKL_DSP_CORE0_ID + 1; i < SKL_DSP_CORES_MAX; i++) {
skl->cores.state[i] = SKL_DSP_RESET;
skl->cores.usage_count[i] = 0;
}
}
/* Get the mask for all enabled cores */
unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx)
{
struct skl_sst *skl = ctx->thread_context;
unsigned int core_mask, en_cores_mask;
u32 val;
core_mask = SKL_DSP_CORES_MASK(skl->cores.count);
val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
/* Cores having CPA bit set */
en_cores_mask = (val & SKL_ADSPCS_CPA_MASK(core_mask)) >>
SKL_ADSPCS_CPA_SHIFT;
/* And cores having CRST bit cleared */
en_cores_mask &= (~val & SKL_ADSPCS_CRST_MASK(core_mask)) >>
SKL_ADSPCS_CRST_SHIFT;
/* And cores having CSTALL bit cleared */
en_cores_mask &= (~val & SKL_ADSPCS_CSTALL_MASK(core_mask)) >>
SKL_ADSPCS_CSTALL_SHIFT;
en_cores_mask &= core_mask;
dev_dbg(ctx->dev, "DSP enabled cores mask = %x\n", en_cores_mask);
return en_cores_mask;
}
static int
skl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx,
SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK,
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK));
SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK(core_mask),
SKL_ADSPCS_CRST_MASK(core_mask));
/* poll with timeout to check if operation successful */
ret = sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_CRST_MASK,
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK),
SKL_ADSPCS_CRST_MASK(core_mask),
SKL_ADSPCS_CRST_MASK(core_mask),
SKL_DSP_RESET_TO,
"Set reset");
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) !=
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) {
dev_err(ctx->dev, "Set reset state failed\n");
SKL_ADSPCS_CRST_MASK(core_mask)) !=
SKL_ADSPCS_CRST_MASK(core_mask)) {
dev_err(ctx->dev, "Set reset state failed: core_mask %x\n",
core_mask);
ret = -EIO;
}
return ret;
}
static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
int skl_dsp_core_unset_reset_state(
struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
@ -68,152 +119,160 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_CRST_MASK, 0);
SKL_ADSPCS_CRST_MASK(core_mask), 0);
/* poll with timeout to check if operation successful */
ret = sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_CRST_MASK,
SKL_ADSPCS_CRST_MASK(core_mask),
0,
SKL_DSP_RESET_TO,
"Unset reset");
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) {
dev_err(ctx->dev, "Unset reset state failed\n");
SKL_ADSPCS_CRST_MASK(core_mask)) != 0) {
dev_err(ctx->dev, "Unset reset state failed: core_mask %x\n",
core_mask);
ret = -EIO;
}
return ret;
}
static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
static bool
is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask)
{
int val;
bool is_enable;
val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) &&
(val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) &&
!(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) &&
!(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)));
is_enable = ((val & SKL_ADSPCS_CPA_MASK(core_mask)) &&
(val & SKL_ADSPCS_SPA_MASK(core_mask)) &&
!(val & SKL_ADSPCS_CRST_MASK(core_mask)) &&
!(val & SKL_ADSPCS_CSTALL_MASK(core_mask)));
dev_dbg(ctx->dev, "DSP core(s) enabled? %d : core_mask %x\n",
is_enable, core_mask);
dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable);
return is_enable;
}
static int skl_dsp_reset_core(struct sst_dsp *ctx)
static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask)
{
/* stall core */
sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_CSTALL_MASK(core_mask),
SKL_ADSPCS_CSTALL_MASK(core_mask));
/* set reset state */
return skl_dsp_core_set_reset_state(ctx);
return skl_dsp_core_set_reset_state(ctx, core_mask);
}
static int skl_dsp_start_core(struct sst_dsp *ctx)
int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* unset reset state */
ret = skl_dsp_core_unset_reset_state(ctx);
if (ret < 0) {
dev_dbg(ctx->dev, "dsp unset reset fails\n");
ret = skl_dsp_core_unset_reset_state(ctx, core_mask);
if (ret < 0)
return ret;
}
/* run core */
dev_dbg(ctx->dev, "run core...\n");
sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
dev_dbg(ctx->dev, "unstall/run core: core_mask = %x\n", core_mask);
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_CSTALL_MASK(core_mask), 0);
if (!is_skl_dsp_core_enable(ctx)) {
skl_dsp_reset_core(ctx);
dev_err(ctx->dev, "DSP core enable failed\n");
if (!is_skl_dsp_core_enable(ctx, core_mask)) {
skl_dsp_reset_core(ctx, core_mask);
dev_err(ctx->dev, "DSP start core failed: core_mask %x\n",
core_mask);
ret = -EIO;
}
return ret;
}
static int skl_dsp_core_power_up(struct sst_dsp *ctx)
int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK));
SKL_ADSPCS_SPA_MASK(core_mask),
SKL_ADSPCS_SPA_MASK(core_mask));
/* poll with timeout to check if operation successful */
ret = sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_CPA_MASK,
SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK),
SKL_ADSPCS_CPA_MASK(core_mask),
SKL_ADSPCS_CPA_MASK(core_mask),
SKL_DSP_PU_TO,
"Power up");
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) !=
SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) {
dev_err(ctx->dev, "DSP core power up failed\n");
SKL_ADSPCS_CPA_MASK(core_mask)) !=
SKL_ADSPCS_CPA_MASK(core_mask)) {
dev_err(ctx->dev, "DSP core power up failed: core_mask %x\n",
core_mask);
ret = -EIO;
}
return ret;
}
static int skl_dsp_core_power_down(struct sst_dsp *ctx)
int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask)
{
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_SPA_MASK, 0);
SKL_ADSPCS_SPA_MASK(core_mask), 0);
/* poll with timeout to check if operation successful */
return sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
SKL_ADSPCS_CPA_MASK,
SKL_ADSPCS_CPA_MASK(core_mask),
0,
SKL_DSP_PD_TO,
"Power down");
}
int skl_dsp_enable_core(struct sst_dsp *ctx)
int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* power up */
ret = skl_dsp_core_power_up(ctx);
ret = skl_dsp_core_power_up(ctx, core_mask);
if (ret < 0) {
dev_dbg(ctx->dev, "dsp core power up failed\n");
dev_err(ctx->dev, "dsp core power up failed: core_mask %x\n",
core_mask);
return ret;
}
return skl_dsp_start_core(ctx);
return skl_dsp_start_core(ctx, core_mask);
}
int skl_dsp_disable_core(struct sst_dsp *ctx)
int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
ret = skl_dsp_reset_core(ctx);
ret = skl_dsp_reset_core(ctx, core_mask);
if (ret < 0) {
dev_err(ctx->dev, "dsp core reset failed\n");
dev_err(ctx->dev, "dsp core reset failed: core_mask %x\n",
core_mask);
return ret;
}
/* power down core*/
ret = skl_dsp_core_power_down(ctx);
ret = skl_dsp_core_power_down(ctx, core_mask);
if (ret < 0) {
dev_err(ctx->dev, "dsp core power down failed\n");
dev_err(ctx->dev, "dsp core power down fail mask %x: %d\n",
core_mask, ret);
return ret;
}
if (is_skl_dsp_core_enable(ctx)) {
dev_err(ctx->dev, "DSP core disable failed\n");
if (is_skl_dsp_core_enable(ctx, core_mask)) {
dev_err(ctx->dev, "dsp core disable fail mask %x: %d\n",
core_mask, ret);
ret = -EIO;
}
@ -224,28 +283,25 @@ int skl_dsp_boot(struct sst_dsp *ctx)
{
int ret;
if (is_skl_dsp_core_enable(ctx)) {
dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n");
ret = skl_dsp_reset_core(ctx);
if (is_skl_dsp_core_enable(ctx, SKL_DSP_CORE0_MASK)) {
ret = skl_dsp_reset_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
dev_err(ctx->dev, "dsp reset failed\n");
dev_err(ctx->dev, "dsp core0 reset fail: %d\n", ret);
return ret;
}
ret = skl_dsp_start_core(ctx);
ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
dev_err(ctx->dev, "dsp start failed\n");
dev_err(ctx->dev, "dsp core0 start fail: %d\n", ret);
return ret;
}
} else {
dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n");
ret = skl_dsp_disable_core(ctx);
ret = skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
dev_err(ctx->dev, "dsp disable core failes\n");
dev_err(ctx->dev, "dsp core0 disable fail: %d\n", ret);
return ret;
}
ret = skl_dsp_enable_core(ctx);
ret = skl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK);
}
return ret;
@ -281,16 +337,74 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
return result;
}
/*
* skl_dsp_get_core/skl_dsp_put_core will be called inside DAPM context
* within the dapm mutex. Hence no separate lock is used.
*/
int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id)
{
struct skl_sst *skl = ctx->thread_context;
int ret = 0;
if (core_id >= skl->cores.count) {
dev_err(ctx->dev, "invalid core id: %d\n", core_id);
return -EINVAL;
}
if (skl->cores.state[core_id] == SKL_DSP_RESET) {
ret = ctx->fw_ops.set_state_D0(ctx, core_id);
if (ret < 0) {
dev_err(ctx->dev, "unable to get core%d\n", core_id);
return ret;
}
}
skl->cores.usage_count[core_id]++;
dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
core_id, skl->cores.state[core_id],
skl->cores.usage_count[core_id]);
return ret;
}
EXPORT_SYMBOL_GPL(skl_dsp_get_core);
int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id)
{
struct skl_sst *skl = ctx->thread_context;
int ret = 0;
if (core_id >= skl->cores.count) {
dev_err(ctx->dev, "invalid core id: %d\n", core_id);
return -EINVAL;
}
if (--skl->cores.usage_count[core_id] == 0) {
ret = ctx->fw_ops.set_state_D3(ctx, core_id);
if (ret < 0) {
dev_err(ctx->dev, "unable to put core %d: %d\n",
core_id, ret);
skl->cores.usage_count[core_id]++;
}
}
dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
core_id, skl->cores.state[core_id],
skl->cores.usage_count[core_id]);
return ret;
}
EXPORT_SYMBOL_GPL(skl_dsp_put_core);
int skl_dsp_wake(struct sst_dsp *ctx)
{
return ctx->fw_ops.set_state_D0(ctx);
return skl_dsp_get_core(ctx, SKL_DSP_CORE0_ID);
}
EXPORT_SYMBOL_GPL(skl_dsp_wake);
int skl_dsp_sleep(struct sst_dsp *ctx)
{
return ctx->fw_ops.set_state_D3(ctx);
return skl_dsp_put_core(ctx, SKL_DSP_CORE0_ID);
}
EXPORT_SYMBOL_GPL(skl_dsp_sleep);
@ -337,9 +451,7 @@ void skl_dsp_free(struct sst_dsp *dsp)
free_irq(dsp->irq, dsp);
skl_ipc_op_int_disable(dsp);
skl_ipc_int_disable(dsp);
skl_dsp_disable_core(dsp);
skl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK);
}
EXPORT_SYMBOL_GPL(skl_dsp_free);

View file

@ -19,6 +19,7 @@
#include <linux/interrupt.h>
#include <sound/memalloc.h>
#include "skl-sst-cldma.h"
#include "skl-tplg-interface.h"
struct sst_dsp;
struct skl_sst;
@ -76,35 +77,53 @@ struct sst_dsp_device;
#define SKL_ADSPIC_IPC 1
#define SKL_ADSPIS_IPC 1
/* Core ID of core0 */
#define SKL_DSP_CORE0_ID 0
/* Mask for a given core index, c = 0.. number of supported cores - 1 */
#define SKL_DSP_CORE_MASK(c) BIT(c)
/*
* Core 0 mask = SKL_DSP_CORE_MASK(0); Defined separately
* since Core0 is primary core and it is used often
*/
#define SKL_DSP_CORE0_MASK BIT(0)
/*
* Mask for a given number of cores
* nc = number of supported cores
*/
#define SKL_DSP_CORES_MASK(nc) GENMASK((nc - 1), 0)
/* ADSPCS - Audio DSP Control & Status */
#define SKL_DSP_CORES 1
#define SKL_DSP_CORE0_MASK 1
#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1)
/* Core Reset - asserted high */
#define SKL_ADSPCS_CRST_SHIFT 0
#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT)
#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK)
/*
* Core Reset - asserted high
* CRST Mask for a given core mask pattern, cm
*/
#define SKL_ADSPCS_CRST_SHIFT 0
#define SKL_ADSPCS_CRST_MASK(cm) ((cm) << SKL_ADSPCS_CRST_SHIFT)
/* Core run/stall - when set to '1' core is stalled */
#define SKL_ADSPCS_CSTALL_SHIFT 8
#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \
SKL_ADSPCS_CSTALL_SHIFT)
#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \
SKL_ADSPCS_CSTALL_MASK)
/*
* Core run/stall - when set to '1' core is stalled
* CSTALL Mask for a given core mask pattern, cm
*/
#define SKL_ADSPCS_CSTALL_SHIFT 8
#define SKL_ADSPCS_CSTALL_MASK(cm) ((cm) << SKL_ADSPCS_CSTALL_SHIFT)
/* Set Power Active - when set to '1' turn cores on */
#define SKL_ADSPCS_SPA_SHIFT 16
#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT)
#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK)
/*
* Set Power Active - when set to '1' turn cores on
* SPA Mask for a given core mask pattern, cm
*/
#define SKL_ADSPCS_SPA_SHIFT 16
#define SKL_ADSPCS_SPA_MASK(cm) ((cm) << SKL_ADSPCS_SPA_SHIFT)
/* Current Power Active - power status of cores, set by hardware */
#define SKL_ADSPCS_CPA_SHIFT 24
#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT)
#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK)
#define SST_DSP_POWER_D0 0x0 /* full On */
#define SST_DSP_POWER_D3 0x3 /* Off */
/*
* Current Power Active - power status of cores, set by hardware
* CPA Mask for a given core mask pattern, cm
*/
#define SKL_ADSPCS_CPA_SHIFT 24
#define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT)
enum skl_dsp_states {
SKL_DSP_RUNNING = 1,
@ -115,8 +134,8 @@ struct skl_dsp_fw_ops {
int (*load_fw)(struct sst_dsp *ctx);
/* FW module parser/loader */
int (*parse_fw)(struct sst_dsp *ctx);
int (*set_state_D0)(struct sst_dsp *ctx);
int (*set_state_D3)(struct sst_dsp *ctx);
int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id);
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
@ -157,14 +176,26 @@ int skl_cldma_prepare(struct sst_dsp *ctx);
void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
struct sst_dsp_device *sst_dev, int irq);
int skl_dsp_enable_core(struct sst_dsp *ctx);
int skl_dsp_disable_core(struct sst_dsp *ctx);
bool is_skl_dsp_running(struct sst_dsp *ctx);
unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx);
void skl_dsp_init_core_state(struct sst_dsp *ctx);
int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask);
int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask);
int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask);
int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask);
int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx,
unsigned int core_mask);
int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask);
irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
int skl_dsp_wake(struct sst_dsp *ctx);
int skl_dsp_sleep(struct sst_dsp *ctx);
void skl_dsp_free(struct sst_dsp *dsp);
int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id);
int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id);
int skl_dsp_boot(struct sst_dsp *ctx);
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
@ -175,4 +206,11 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
struct skl_dfw_module *dfw_config);
int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset);
void skl_freeup_uuid_list(struct skl_sst *ctx);
int skl_dsp_strip_extended_manifest(struct firmware *fw);
#endif /*__SKL_SST_DSP_H__*/

View file

@ -363,7 +363,7 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
/* first process the header */
switch (reply) {
case IPC_GLB_REPLY_SUCCESS:
dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary);
/* copy the rx data from the mailbox */
sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size);
break;
@ -692,7 +692,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
/* param_block_size must be in dwords */
u16 param_block_size = msg->param_data_size / sizeof(u32);
print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
print_hex_dump_debug("Param data:", DUMP_PREFIX_NONE,
16, 4, buffer, param_block_size, false);
header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);

View file

@ -45,6 +45,14 @@ struct skl_ipc_header {
u32 extension;
};
#define SKL_DSP_CORES_MAX 2
struct skl_dsp_cores {
unsigned int count;
enum skl_dsp_states state[SKL_DSP_CORES_MAX];
int usage_count[SKL_DSP_CORES_MAX];
};
struct skl_sst {
struct device *dev;
struct sst_dsp *dsp;
@ -60,6 +68,15 @@ struct skl_sst {
void (*enable_miscbdcge)(struct device *dev, bool enable);
/*Is CGCTL.MISCBDCGE disabled*/
bool miscbdcg_disabled;
/* Populate module information */
struct list_head uuid_list;
/* Is firmware loaded */
bool fw_loaded;
/* multi-core */
struct skl_dsp_cores cores;
};
struct skl_ipc_init_instance_msg {
@ -136,5 +153,6 @@ void skl_ipc_int_disable(struct sst_dsp *dsp);
bool skl_ipc_int_status(struct sst_dsp *dsp);
void skl_ipc_free(struct sst_generic_ipc *ipc);
int skl_ipc_init(struct device *dev, struct skl_sst *skl);
void skl_clear_module_cnt(struct sst_dsp *ctx);
#endif /* __SKL_IPC_H */

View file

@ -0,0 +1,256 @@
/*
* skl-sst-utils.c - SKL sst utils functions
*
* Copyright (C) 2016 Intel Corp
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as 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/device.h>
#include <linux/slab.h>
#include <linux/uuid.h>
#include "skl-sst-dsp.h"
#include "../common/sst-dsp.h"
#include "../common/sst-dsp-priv.h"
#include "skl-sst-ipc.h"
#define UUID_STR_SIZE 37
#define DEFAULT_HASH_SHA256_LEN 32
/* FW Extended Manifest Header id = $AE1 */
#define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124
struct skl_dfw_module_mod {
char name[100];
struct skl_dfw_module skl_dfw_mod;
};
struct UUID {
u8 id[16];
};
union seg_flags {
u32 ul;
struct {
u32 contents : 1;
u32 alloc : 1;
u32 load : 1;
u32 read_only : 1;
u32 code : 1;
u32 data : 1;
u32 _rsvd0 : 2;
u32 type : 4;
u32 _rsvd1 : 4;
u32 length : 16;
} r;
} __packed;
struct segment_desc {
union seg_flags flags;
u32 v_base_addr;
u32 file_offset;
};
struct module_type {
u32 load_type : 4;
u32 auto_start : 1;
u32 domain_ll : 1;
u32 domain_dp : 1;
u32 rsvd : 25;
} __packed;
struct adsp_module_entry {
u32 struct_id;
u8 name[8];
struct UUID uuid;
struct module_type type;
u8 hash1[DEFAULT_HASH_SHA256_LEN];
u32 entry_point;
u16 cfg_offset;
u16 cfg_count;
u32 affinity_mask;
u16 instance_max_count;
u16 instance_bss_size;
struct segment_desc segments[3];
} __packed;
struct adsp_fw_hdr {
u32 id;
u32 len;
u8 name[8];
u32 preload_page_count;
u32 fw_image_flags;
u32 feature_mask;
u16 major;
u16 minor;
u16 hotfix;
u16 build;
u32 num_modules;
u32 hw_buf_base;
u32 hw_buf_length;
u32 load_offset;
} __packed;
struct uuid_module {
uuid_le uuid;
int id;
int is_loadable;
struct list_head list;
};
struct skl_ext_manifest_hdr {
u32 id;
u32 len;
u16 version_major;
u16 version_minor;
u32 entries;
};
int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
struct skl_dfw_module *dfw_config)
{
struct uuid_module *module;
uuid_le *uuid_mod;
uuid_mod = (uuid_le *)uuid;
list_for_each_entry(module, &ctx->uuid_list, list) {
if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
dfw_config->module_id = module->id;
dfw_config->is_loadable = module->is_loadable;
return 0;
}
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(snd_skl_get_module_info);
/*
* Parse the firmware binary to get the UUID, module id
* and loadable flags
*/
int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
{
struct adsp_fw_hdr *adsp_hdr;
struct adsp_module_entry *mod_entry;
int i, num_entry;
uuid_le *uuid_bin;
const char *buf;
struct skl_sst *skl = ctx->thread_context;
struct uuid_module *module;
struct firmware stripped_fw;
unsigned int safe_file;
/* Get the FW pointer to derive ADSP header */
stripped_fw.data = ctx->fw->data;
stripped_fw.size = ctx->fw->size;
skl_dsp_strip_extended_manifest(&stripped_fw);
buf = stripped_fw.data;
/* check if we have enough space in file to move to header */
safe_file = sizeof(*adsp_hdr) + offset;
if (stripped_fw.size <= safe_file) {
dev_err(ctx->dev, "Small fw file size, No space for hdr\n");
return -EINVAL;
}
adsp_hdr = (struct adsp_fw_hdr *)(buf + offset);
/* check 1st module entry is in file */
safe_file += adsp_hdr->len + sizeof(*mod_entry);
if (stripped_fw.size <= safe_file) {
dev_err(ctx->dev, "Small fw file size, No module entry\n");
return -EINVAL;
}
mod_entry = (struct adsp_module_entry *)
(buf + offset + adsp_hdr->len);
num_entry = adsp_hdr->num_modules;
/* check all entries are in file */
safe_file += num_entry * sizeof(*mod_entry);
if (stripped_fw.size <= safe_file) {
dev_err(ctx->dev, "Small fw file size, No modules\n");
return -EINVAL;
}
/*
* Read the UUID(GUID) from FW Manifest.
*
* The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
* Populate the UUID table to store module_id and loadable flags
* for the module.
*/
for (i = 0; i < num_entry; i++, mod_entry++) {
module = kzalloc(sizeof(*module), GFP_KERNEL);
if (!module)
return -ENOMEM;
uuid_bin = (uuid_le *)mod_entry->uuid.id;
memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
module->id = i;
module->is_loadable = mod_entry->type.load_type;
list_add_tail(&module->list, &skl->uuid_list);
dev_dbg(ctx->dev,
"Adding uuid :%pUL mod id: %d Loadable: %d\n",
&module->uuid, module->id, module->is_loadable);
}
return 0;
}
void skl_freeup_uuid_list(struct skl_sst *ctx)
{
struct uuid_module *uuid, *_uuid;
list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) {
list_del(&uuid->list);
kfree(uuid);
}
}
/*
* some firmware binary contains some extended manifest. This needs
* to be stripped in that case before we load and use that image.
*
* Get the module id for the module by checking
* the table for the UUID for the module
*/
int skl_dsp_strip_extended_manifest(struct firmware *fw)
{
struct skl_ext_manifest_hdr *hdr;
/* check if fw file is greater than header we are looking */
if (fw->size < sizeof(hdr)) {
pr_err("%s: Firmware file small, no hdr\n", __func__);
return -EINVAL;
}
hdr = (struct skl_ext_manifest_hdr *)fw->data;
if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) {
fw->size -= hdr->len;
fw->data += hdr->len;
}
return 0;
}

View file

@ -68,10 +68,13 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
return ret;
}
#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284
static int skl_load_base_firmware(struct sst_dsp *ctx)
{
int ret = 0, i;
struct skl_sst *skl = ctx->thread_context;
struct firmware stripped_fw;
u32 reg;
skl->boot_complete = false;
@ -81,11 +84,25 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
if (ret < 0) {
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
skl_dsp_disable_core(ctx);
return -EIO;
}
}
ret = snd_skl_parse_uuids(ctx, SKL_ADSP_FW_BIN_HDR_OFFSET);
if (ret < 0) {
dev_err(ctx->dev,
"UUID parsing err: %d\n", ret);
release_firmware(ctx->fw);
skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
return ret;
}
/* check for extended manifest */
stripped_fw.data = ctx->fw->data;
stripped_fw.size = ctx->fw->size;
skl_dsp_strip_extended_manifest(&stripped_fw);
ret = skl_dsp_boot(ctx);
if (ret < 0) {
dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
@ -119,7 +136,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
goto transfer_firmware_failed;
}
ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size);
if (ret < 0) {
dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
goto transfer_firmware_failed;
@ -133,67 +150,87 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
}
dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
skl->fw_loaded = true;
}
return 0;
transfer_firmware_failed:
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
skl_load_base_firmware_failed:
skl_dsp_disable_core(ctx);
skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
release_firmware(ctx->fw);
ctx->fw = NULL;
return ret;
}
static int skl_set_dsp_D0(struct sst_dsp *ctx)
{
int ret;
ret = skl_load_base_firmware(ctx);
if (ret < 0) {
dev_err(ctx->dev, "unable to load firmware\n");
return ret;
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
return ret;
}
static int skl_set_dsp_D3(struct sst_dsp *ctx)
static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
{
int ret;
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
dev_dbg(ctx->dev, "In %s:\n", __func__);
mutex_lock(&ctx->mutex);
if (!is_skl_dsp_running(ctx)) {
mutex_unlock(&ctx->mutex);
return 0;
/* If core0 is being turned on, we need to load the FW */
if (core_id == SKL_DSP_CORE0_ID) {
ret = skl_load_base_firmware(ctx);
if (ret < 0) {
dev_err(ctx->dev, "unable to load firmware\n");
return ret;
}
}
mutex_unlock(&ctx->mutex);
dx.core_mask = SKL_DSP_CORE0_MASK;
/*
* If any core other than core 0 is being moved to D0, enable the
* core and send the set dx IPC for the core.
*/
if (core_id != SKL_DSP_CORE0_ID) {
ret = skl_dsp_enable_core(ctx, core_mask);
if (ret < 0)
return ret;
dx.core_mask = core_mask;
dx.dx_mask = core_mask;
ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
SKL_BASE_FW_MODULE_ID, &dx);
if (ret < 0) {
dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n",
core_id);
skl_dsp_disable_core(ctx, core_mask);
}
}
skl->cores.state[core_id] = SKL_DSP_RUNNING;
return ret;
}
static int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
{
int ret;
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
dx.core_mask = core_mask;
dx.dx_mask = SKL_IPC_D3_MASK;
ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
if (ret < 0)
dev_err(ctx->dev,
"D3 request to FW failed, continuing reset: %d", ret);
dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret);
/* disable Interrupt */
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
skl_cldma_int_disable(ctx);
skl_ipc_op_int_disable(ctx);
skl_ipc_int_disable(ctx);
ret = skl_dsp_disable_core(ctx);
if (ret < 0) {
dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret);
ret = -EIO;
if (core_id == SKL_DSP_CORE0_ID) {
/* disable Interrupt */
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
skl_cldma_int_disable(ctx);
skl_ipc_op_int_disable(ctx);
skl_ipc_int_disable(ctx);
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
ret = skl_dsp_disable_core(ctx, core_mask);
if (ret < 0)
return ret;
skl->cores.state[core_id] = SKL_DSP_RESET;
return ret;
}
@ -360,6 +397,19 @@ static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
return ret;
}
void skl_clear_module_cnt(struct sst_dsp *ctx)
{
struct skl_module_table *module;
if (list_empty(&ctx->module_list))
return;
list_for_each_entry(module, &ctx->module_list, list) {
module->usage_cnt = 0;
}
}
EXPORT_SYMBOL_GPL(skl_clear_module_cnt);
static void skl_clear_module_table(struct sst_dsp *ctx)
{
struct skl_module_table *module, *tmp;
@ -409,6 +459,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
skl->dev = dev;
skl_dev.thread_context = skl;
INIT_LIST_HEAD(&skl->uuid_list);
skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
if (!skl->dsp) {
@ -432,12 +483,16 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
if (ret)
return ret;
skl->cores.count = 2;
ret = sst->fw_ops.load_fw(sst);
if (ret < 0) {
dev_err(dev, "Load base fw failed : %d", ret);
goto cleanup;
}
skl_dsp_init_core_state(sst);
if (dsp)
*dsp = skl;
@ -452,6 +507,7 @@ EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
skl_clear_module_table(ctx->dsp);
skl_freeup_uuid_list(ctx);
skl_ipc_free(&ctx->ipc);
ctx->dsp->ops->free(ctx->dsp);
if (ctx->boot_complete) {

View file

@ -378,43 +378,6 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
skl_dump_mconfig(ctx, m_cfg);
}
/*
* A pipe can have multiple modules, each of them will be a DAPM widget as
* well. While managing a pipeline we need to get the list of all the
* widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
* to get the SKL type widgets in that pipeline
*/
static int skl_tplg_alloc_pipe_widget(struct device *dev,
struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
{
struct skl_module_cfg *src_module = NULL;
struct snd_soc_dapm_path *p = NULL;
struct skl_pipe_module *p_module = NULL;
p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
if (!p_module)
return -ENOMEM;
p_module->w = w;
list_add_tail(&p_module->node, &pipe->w_list);
snd_soc_dapm_widget_for_each_sink_path(w, p) {
if ((p->sink->priv == NULL)
&& (!is_skl_dsp_widget_type(w)))
continue;
if ((p->sink->priv != NULL) && p->connect
&& is_skl_dsp_widget_type(p->sink)) {
src_module = p->sink->priv;
if (pipe->ppl_id == src_module->pipe->ppl_id)
skl_tplg_alloc_pipe_widget(dev,
p->sink, pipe);
}
}
return 0;
}
/*
* some modules can have multiple params set from user control and
* need to be set after module is initialized. If set_param flag is
@ -514,8 +477,6 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
if (!skl_is_pipe_mcps_avail(skl, mconfig))
return -ENOMEM;
skl_tplg_alloc_pipe_mcps(skl, mconfig);
if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
mconfig->id.module_id, mconfig->guid);
@ -539,6 +500,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
if (ret < 0)
return ret;
skl_tplg_alloc_pipe_mcps(skl, mconfig);
ret = skl_tplg_set_module_params(w, ctx);
if (ret < 0)
return ret;
@ -591,9 +553,6 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
if (!skl_is_pipe_mem_avail(skl, mconfig))
return -ENOMEM;
skl_tplg_alloc_pipe_mem(skl, mconfig);
skl_tplg_alloc_pipe_mcps(skl, mconfig);
/*
* Create a list of modules for pipe.
* This list contains modules from source to sink
@ -602,19 +561,8 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
if (ret < 0)
return ret;
/*
* we create a w_list of all widgets in that pipe. This list is not
* freed on PMD event as widgets within a pipe are static. This
* saves us cycles to get widgets in pipe every time.
*
* So if we have already initialized all the widgets of a pipeline
* we skip, so check for list_empty and create the list if empty
*/
if (list_empty(&s_pipe->w_list)) {
ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
if (ret < 0)
return ret;
}
skl_tplg_alloc_pipe_mem(skl, mconfig);
skl_tplg_alloc_pipe_mcps(skl, mconfig);
/* Init all pipe modules from source to sink */
ret = skl_tplg_init_pipe_modules(skl, s_pipe);
@ -949,13 +897,17 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
struct skl_pipe *s_pipe = mconfig->pipe;
int ret = 0;
if (s_pipe->state == SKL_PIPE_INVALID)
return -EINVAL;
skl_tplg_free_pipe_mcps(skl, mconfig);
skl_tplg_free_pipe_mem(skl, mconfig);
list_for_each_entry(w_module, &s_pipe->w_list, node) {
dst_module = w_module->w->priv;
skl_tplg_free_pipe_mcps(skl, dst_module);
if (mconfig->m_state >= SKL_MODULE_INIT_DONE)
skl_tplg_free_pipe_mcps(skl, dst_module);
if (src_module == NULL) {
src_module = dst_module;
continue;
@ -1162,6 +1114,39 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
return 0;
}
/*
* Fill the dma id for host and link. In case of passthrough
* pipeline, this will both host and link in the same
* pipeline, so need to copy the link and host based on dev_type
*/
static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
struct skl_pipe_params *params)
{
struct skl_pipe *pipe = mcfg->pipe;
if (pipe->passthru) {
switch (mcfg->dev_type) {
case SKL_DEVICE_HDALINK:
pipe->p_params->link_dma_id = params->link_dma_id;
break;
case SKL_DEVICE_HDAHOST:
pipe->p_params->host_dma_id = params->host_dma_id;
break;
default:
break;
}
pipe->p_params->s_fmt = params->s_fmt;
pipe->p_params->ch = params->ch;
pipe->p_params->s_freq = params->s_freq;
pipe->p_params->stream = params->stream;
} else {
memcpy(pipe->p_params, params, sizeof(*params));
}
}
/*
* The FE params are passed by hw_params of the DAI.
* On hw_params, the params are stored in Gateway module of the FE and we
@ -1172,10 +1157,9 @@ int skl_tplg_update_pipe_params(struct device *dev,
struct skl_module_cfg *mconfig,
struct skl_pipe_params *params)
{
struct skl_pipe *pipe = mconfig->pipe;
struct skl_module_fmt *format = NULL;
memcpy(pipe->p_params, params, sizeof(*params));
skl_tplg_fill_dma_id(mconfig, params);
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
format = &mconfig->in_fmt[0];
@ -1362,12 +1346,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
struct skl_module_cfg *mconfig,
struct skl_pipe_params *params)
{
struct skl_pipe *pipe = mconfig->pipe;
struct nhlt_specific_cfg *cfg;
struct skl *skl = get_skl_ctx(dai->dev);
int link_type = skl_tplg_be_link_type(mconfig->dev_type);
memcpy(pipe->p_params, params, sizeof(*params));
skl_tplg_fill_dma_id(mconfig, params);
if (link_type == NHLT_LINK_HDA)
return 0;
@ -1558,6 +1541,55 @@ static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
}
}
static void skl_clear_pin_config(struct snd_soc_platform *platform,
struct snd_soc_dapm_widget *w)
{
int i;
struct skl_module_cfg *mconfig;
struct skl_pipe *pipe;
if (!strncmp(w->dapm->component->name, platform->component.name,
strlen(platform->component.name))) {
mconfig = w->priv;
pipe = mconfig->pipe;
for (i = 0; i < mconfig->max_in_queue; i++) {
mconfig->m_in_pin[i].in_use = false;
mconfig->m_in_pin[i].pin_state = SKL_PIN_UNBIND;
}
for (i = 0; i < mconfig->max_out_queue; i++) {
mconfig->m_out_pin[i].in_use = false;
mconfig->m_out_pin[i].pin_state = SKL_PIN_UNBIND;
}
pipe->state = SKL_PIPE_INVALID;
mconfig->m_state = SKL_MODULE_UNINIT;
}
}
void skl_cleanup_resources(struct skl *skl)
{
struct skl_sst *ctx = skl->skl_sst;
struct snd_soc_platform *soc_platform = skl->platform;
struct snd_soc_dapm_widget *w;
struct snd_soc_card *card;
if (soc_platform == NULL)
return;
card = soc_platform->component.card;
if (!card || !card->instantiated)
return;
skl->resource.mem = 0;
skl->resource.mcps = 0;
list_for_each_entry(w, &card->widgets, list) {
if (is_skl_dsp_widget_type(w) && (w->priv != NULL))
skl_clear_pin_config(soc_platform, w);
}
skl_clear_module_cnt(ctx->dsp);
}
/*
* Topology core widget load callback
*
@ -1589,6 +1621,10 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
w->priv = mconfig;
memcpy(&mconfig->guid, &dfw_config->uuid, 16);
ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config);
if (ret < 0)
return ret;
mconfig->id.module_id = dfw_config->module_id;
mconfig->id.instance_id = dfw_config->instance_id;
mconfig->mcps = dfw_config->max_mcps;
@ -1738,6 +1774,60 @@ static struct snd_soc_tplg_ops skl_tplg_ops = {
.bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
};
/*
* A pipe can have multiple modules, each of them will be a DAPM widget as
* well. While managing a pipeline we need to get the list of all the
* widgets in a pipelines, so this helper - skl_tplg_create_pipe_widget_list()
* helps to get the SKL type widgets in that pipeline
*/
static int skl_tplg_create_pipe_widget_list(struct snd_soc_platform *platform)
{
struct snd_soc_dapm_widget *w;
struct skl_module_cfg *mcfg = NULL;
struct skl_pipe_module *p_module = NULL;
struct skl_pipe *pipe;
list_for_each_entry(w, &platform->component.card->widgets, list) {
if (is_skl_dsp_widget_type(w) && w->priv != NULL) {
mcfg = w->priv;
pipe = mcfg->pipe;
p_module = devm_kzalloc(platform->dev,
sizeof(*p_module), GFP_KERNEL);
if (!p_module)
return -ENOMEM;
p_module->w = w;
list_add_tail(&p_module->node, &pipe->w_list);
}
}
return 0;
}
static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe)
{
struct skl_pipe_module *w_module;
struct snd_soc_dapm_widget *w;
struct skl_module_cfg *mconfig;
bool host_found = false, link_found = false;
list_for_each_entry(w_module, &pipe->w_list, node) {
w = w_module->w;
mconfig = w->priv;
if (mconfig->dev_type == SKL_DEVICE_HDAHOST)
host_found = true;
else if (mconfig->dev_type != SKL_DEVICE_NONE)
link_found = true;
}
if (host_found && link_found)
pipe->passthru = true;
else
pipe->passthru = false;
}
/* This will be read from topology manifest, currently defined here */
#define SKL_MAX_MCPS 30000000
#define SKL_FW_MAX_MEM 1000000
@ -1751,6 +1841,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
const struct firmware *fw;
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl *skl = ebus_to_skl(ebus);
struct skl_pipeline *ppl;
ret = request_firmware(&fw, skl->tplg_name, bus->dev);
if (ret < 0) {
@ -1780,6 +1871,12 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
skl->resource.max_mem = SKL_FW_MAX_MEM;
skl->tplg = fw;
ret = skl_tplg_create_pipe_widget_list(platform);
if (ret < 0)
return ret;
list_for_each_entry(ppl, &skl->ppl_list, node)
skl_tplg_set_pipe_type(skl, ppl->pipe);
return 0;
}

View file

@ -244,7 +244,8 @@ enum skl_pipe_state {
SKL_PIPE_INVALID = 0,
SKL_PIPE_CREATED = 1,
SKL_PIPE_PAUSED = 2,
SKL_PIPE_STARTED = 3
SKL_PIPE_STARTED = 3,
SKL_PIPE_RESET = 4
};
struct skl_pipe_module {
@ -270,6 +271,7 @@ struct skl_pipe {
struct skl_pipe_params *p_params;
enum skl_pipe_state state;
struct list_head w_list;
bool passthru;
};
enum skl_module_state {
@ -358,6 +360,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config);
int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg

View file

@ -35,6 +35,8 @@
#include "skl-sst-dsp.h"
#include "skl-sst-ipc.h"
static struct skl_machine_pdata skl_dmic_data;
/*
* initialize the PCI registers
*/
@ -184,6 +186,7 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
{
struct skl *skl = ebus_to_skl(ebus);
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct pci_dev *pci = to_pci_dev(bus->dev);
int ret;
snd_hdac_ext_bus_link_power_down_all(ebus);
@ -193,9 +196,12 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
return ret;
snd_hdac_bus_stop_chip(bus);
update_pci_dword(pci, AZX_PCIREG_PGCTL,
AZX_PGCTL_LSRMD_MASK, AZX_PGCTL_LSRMD_MASK);
skl_enable_miscbdcge(bus->dev, false);
snd_hdac_bus_enter_link_reset(bus);
skl_enable_miscbdcge(bus->dev, true);
skl_cleanup_resources(skl);
return 0;
}
@ -242,6 +248,7 @@ static int skl_suspend(struct device *dev)
ret = _skl_suspend(ebus);
if (ret < 0)
return ret;
skl->skl_sst->fw_loaded = false;
}
if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
@ -397,6 +404,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data)
platform_device_put(pdev);
return -EIO;
}
if (mach->pdata)
dev_set_drvdata(&pdev->dev, mach->pdata);
skl->i2s_dev = pdev;
return 0;
@ -657,6 +668,8 @@ static int skl_probe(struct pci_dev *pci,
skl->pci_id = pci->device;
device_disable_async_suspend(bus->dev);
skl->nhlt = skl_nhlt_init(bus->dev);
if (skl->nhlt == NULL)
@ -666,6 +679,8 @@ static int skl_probe(struct pci_dev *pci,
pci_set_drvdata(skl->pci, ebus);
skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
/* check if dsp is there */
if (ebus->ppcap) {
err = skl_machine_device_register(skl,
@ -713,7 +728,7 @@ static int skl_probe(struct pci_dev *pci,
list_for_each_entry(hlink, &ebus->hlink_list, list)
snd_hdac_ext_bus_link_put(ebus, hlink);
/*configure PM */
/* configure PM */
pm_runtime_put_noidle(bus->dev);
pm_runtime_allow(bus->dev);
@ -766,8 +781,7 @@ static void skl_remove(struct pci_dev *pci)
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct skl *skl = ebus_to_skl(ebus);
if (skl->tplg)
release_firmware(skl->tplg);
release_firmware(skl->tplg);
if (pci_dev_run_wake(pci))
pm_runtime_get_noresume(&pci->dev);
@ -786,15 +800,23 @@ static void skl_remove(struct pci_dev *pci)
static struct sst_acpi_mach sst_skl_devdata[] = {
{ "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL },
{ "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin",
NULL, NULL, NULL },
{ "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin",
NULL, NULL, NULL },
{ "INT343B", "skl_n88l25_s4567", "intel/dsp_fw_release.bin",
NULL, NULL, &skl_dmic_data },
{ "MX98357A", "skl_n88l25_m98357a", "intel/dsp_fw_release.bin",
NULL, NULL, &skl_dmic_data },
{}
};
static struct sst_acpi_mach sst_bxtp_devdata[] = {
{ "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
{ "DLGS7219", "bxt_da7219_max98357a_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
};
static struct sst_acpi_mach sst_kbl_devdata[] = {
{ "INT343A", "kbl_alc286s_i2s", "intel/dsp_fw_kbl.bin", NULL, NULL, NULL },
{ "INT343B", "kbl_n88l25_s4567", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
{ "MX98357A", "kbl_n88l25_m98357a", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
{}
};
/* PCI IDs */
@ -805,6 +827,9 @@ static const struct pci_device_id skl_ids[] = {
/* BXT-P */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = (unsigned long)&sst_bxtp_devdata},
/* KBL */
{ PCI_DEVICE(0x8086, 0x9D71),
.driver_data = (unsigned long)&sst_kbl_devdata},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, skl_ids);

View file

@ -48,6 +48,8 @@
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
#define AZX_PCIREG_PGCTL 0x44
#define AZX_PGCTL_LSRMD_MASK (1 << 4)
#define AZX_PCIREG_CGCTL 0x48
#define AZX_CGCTL_MISCBDCGE_MASK (1 << 6)
@ -65,6 +67,7 @@ struct skl {
unsigned int init_failed:1; /* delayed init failed */
struct platform_device *dmic_dev;
struct platform_device *i2s_dev;
struct snd_soc_platform *platform;
struct nhlt_acpi_table *nhlt; /* nhlt ptr */
struct skl_sst *skl_sst; /* sst skl ctx */
@ -90,6 +93,11 @@ struct skl_dma_params {
u8 stream_tag;
};
/* to pass dmic data */
struct skl_machine_pdata {
u32 dmic_num;
};
struct skl_dsp_ops {
int id;
struct skl_dsp_loader_ops (*loader_ops)(void);
@ -108,9 +116,11 @@ void skl_nhlt_free(struct nhlt_acpi_table *addr);
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
int skl_get_dmic_geo(struct skl *skl);
int skl_nhlt_update_topology_bin(struct skl *skl);
int skl_init_dsp(struct skl *skl);
int skl_free_dsp(struct skl *skl);
int skl_suspend_dsp(struct skl *skl);
int skl_resume_dsp(struct skl *skl);
void skl_cleanup_resources(struct skl *skl);
#endif /* __SOUND_SOC_SKL_H */