#include <sound/soc.h>
#include "skl.h"
#include "skl-topology.h"
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
#define HDA_MONO 1
#define HDA_STEREO 2
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
SNDRV_PCM_INFO_HAS_LINK_ATIME |
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
struct skl_dma_params *dma_params = NULL;
+ struct skl *skl = ebus_to_skl(ebus);
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
snd_soc_dai_set_dma_data(dai, substream, NULL);
skl_set_suspend_active(substream, dai, false);
+ /*
+ * check if close is for "Reference Pin" and set back the
+ * CGCTL.MISCBDCGE if disabled by driver
+ */
+ if (!strncmp(dai->name, "Reference Pin", 13) &&
+ skl->skl_sst->miscbdcg_disabled) {
+ skl->skl_sst->enable_miscbdcge(dai->dev, true);
+ skl->skl_sst->miscbdcg_disabled = false;
+ }
+
kfree(dma_params);
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
skl_pcm_prepare(substream, dai);
+ /*
+ * enable DMA Resume enable bit for the stream, set the dpib
+ * & lpib position to resune before starting the DMA
+ */
+ snd_hdac_ext_stream_drsm_enable(ebus, true,
+ hdac_stream(stream)->index);
+ snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib);
+ snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
+
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/*
return ret;
ret = skl_decoupled_trigger(substream, cmd);
- if (cmd == SNDRV_PCM_TRIGGER_SUSPEND)
+ if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
+ /* save the dpib and lpib positions */
+ stream->dpib = readl(ebus->bus.remap_addr +
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hdac_stream(stream)->index));
+
+ stream->lpib = snd_hdac_stream_get_pos_lpib(
+ hdac_stream(stream));
snd_hdac_ext_stream_decouple(ebus, stream, false);
+ }
break;
default:
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct hdac_ext_link *link;
- if (link_dev->link_prepared) {
- dev_dbg(dai->dev, "already stream is prepared - returning\n");
- return 0;
- }
-
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
if (dma_params)
dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n",
hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name);
- snd_hdac_ext_link_stream_reset(link_dev);
-
- snd_hdac_ext_link_stream_setup(link_dev, format_val);
-
link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
if (!link)
return -EINVAL;
+ snd_hdac_ext_bus_link_power_up(link);
+ snd_hdac_ext_link_stream_reset(link_dev);
+
+ snd_hdac_ext_link_stream_setup(link_dev, format_val);
+
snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
link_dev->link_prepared = 1;
{
struct hdac_ext_stream *link_dev =
snd_soc_dai_get_dma_data(dai, substream);
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ skl_link_pcm_prepare(substream, dai);
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
+ snd_hdac_ext_stream_decouple(ebus, stream, true);
snd_hdac_ext_link_stream_start(link_dev);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
snd_hdac_ext_link_stream_clear(link_dev);
+ if (cmd == SNDRV_PCM_TRIGGER_SUSPEND)
+ snd_hdac_ext_stream_decouple(ebus, stream, false);
break;
default:
else
delay += hstream->bufsize;
}
+ delay = (hstream->bufsize == delay) ? 0 : delay;
if (delay >= hstream->period_bytes) {
dev_info(bus->dev,
/*
* Each pipelines needs memory to be allocated. Check if we have free memory
- * from available pool. Then only add this to pool
- * This is freed when pipe is deleted
- * Note: DSP does actual memory management we only keep track for complete
- * pool
+ * from available pool.
*/
- static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
+ static bool skl_is_pipe_mem_avail(struct skl *skl,
struct skl_module_cfg *mconfig)
{
struct skl_sst *ctx = skl->skl_sst;
"exceeds ppl memory available %d mem %d\n",
skl->resource.max_mem, skl->resource.mem);
return false;
+ } else {
+ return true;
}
+ }
+ /*
+ * Add the mem to the mem pool. This is freed when pipe is deleted.
+ * Note: DSP does actual memory management we only keep track for complete
+ * pool
+ */
+ static void skl_tplg_alloc_pipe_mem(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+ {
skl->resource.mem += mconfig->pipe->memory_pages;
- return true;
}
/*
* quantified in MCPS (Million Clocks Per Second) required for module/pipe
*
* Each pipelines needs mcps to be allocated. Check if we have mcps for this
- * pipe. This adds the mcps to driver counter
- * This is removed on pipeline delete
+ * pipe.
*/
- static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
+
+ static bool skl_is_pipe_mcps_avail(struct skl *skl,
struct skl_module_cfg *mconfig)
{
struct skl_sst *ctx = skl->skl_sst;
"%s: module_id %d instance %d\n", __func__,
mconfig->id.module_id, mconfig->id.instance_id);
dev_err(ctx->dev,
- "exceeds ppl memory available %d > mem %d\n",
+ "exceeds ppl mcps available %d > mem %d\n",
skl->resource.max_mcps, skl->resource.mcps);
return false;
+ } else {
+ return true;
}
+ }
+ static void skl_tplg_alloc_pipe_mcps(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+ {
skl->resource.mcps += mconfig->mcps;
- return true;
}
/*
mconfig = w->priv;
/* check resource available */
- if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ if (!skl_is_pipe_mcps_avail(skl, mconfig))
return -ENOMEM;
if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
ret = skl_tplg_set_module_params(w, ctx);
if (ret < 0)
return ret;
+ skl_tplg_alloc_pipe_mcps(skl, mconfig);
}
return 0;
struct skl_sst *ctx = skl->skl_sst;
/* check resource available */
- if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ if (!skl_is_pipe_mcps_avail(skl, mconfig))
return -EBUSY;
- if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+ if (!skl_is_pipe_mem_avail(skl, mconfig))
return -ENOMEM;
/*
src_module = dst_module;
}
+ skl_tplg_alloc_pipe_mem(skl, mconfig);
+ skl_tplg_alloc_pipe_mcps(skl, mconfig);
+
return 0;
}
static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
struct skl *skl,
+ struct snd_soc_dapm_widget *src_w,
struct skl_module_cfg *src_mconfig)
{
struct snd_soc_dapm_path *p;
dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
next_sink = p->sink;
+
+ if (!is_skl_dsp_widget_type(p->sink))
+ return skl_tplg_bind_sinks(p->sink, skl, src_w, src_mconfig);
+
/*
* here we will check widgets in sink pipelines, so that
* can be any widgets type and we are only interested if
}
if (!sink)
- return skl_tplg_bind_sinks(next_sink, skl, src_mconfig);
+ return skl_tplg_bind_sinks(next_sink, skl, src_w, src_mconfig);
return 0;
}
* if sink is not started, start sink pipe first, then start
* this pipe
*/
- ret = skl_tplg_bind_sinks(w, skl, src_mconfig);
+ ret = skl_tplg_bind_sinks(w, skl, w, src_mconfig);
if (ret)
return ret;
continue;
}
- ret = skl_unbind_modules(ctx, src_module, dst_module);
- if (ret < 0)
- return ret;
-
+ skl_unbind_modules(ctx, src_module, dst_module);
src_module = dst_module;
}
* This is a connecter and if path is found that means
* unbind between source and sink has not happened yet
*/
- ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
- if (ret < 0)
- return ret;
ret = skl_unbind_modules(ctx, src_mconfig,
sink_mconfig);
}
case SND_SOC_DAPM_PRE_PMU:
return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+ case SND_SOC_DAPM_POST_PMU:
+ return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_PRE_PMD:
+ return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
+
case SND_SOC_DAPM_POST_PMD:
return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
}
skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
bc->max, bc->param_id, mconfig);
+ /* decrement size for TLV header */
+ size -= 2 * sizeof(u32);
+
+ /* check size as we don't want to send kernel data */
+ if (size > bc->max)
+ size = bc->max;
+
if (bc->params) {
if (copy_to_user(data, &bc->param_id, sizeof(u32)))
return -EFAULT;
*/
ret = snd_soc_tplg_component_load(&platform->component,
&skl_tplg_ops, fw, 0);
- release_firmware(fw);
if (ret < 0) {
dev_err(bus->dev, "tplg component load failed%d\n", ret);
+ release_firmware(fw);
return -EINVAL;
}
skl->resource.max_mcps = SKL_MAX_MCPS;
skl->resource.max_mem = SKL_FW_MAX_MEM;
+ skl->tplg = fw;
+
return 0;
}
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
+#include <linux/firmware.h>
#include <sound/pcm.h>
#include "../common/sst-acpi.h"
#include "skl.h"
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
/*
* initialize the PCI registers
skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0);
}
+static void update_pci_dword(struct pci_dev *pci,
+ unsigned int reg, u32 mask, u32 val)
+{
+ u32 data = 0;
+
+ pci_read_config_dword(pci, reg, &data);
+ data &= ~mask;
+ data |= (val & mask);
+ pci_write_config_dword(pci, reg, data);
+}
+
+/*
+ * skl_enable_miscbdcge - enable/dsiable CGCTL.MISCBDCGE bits
+ *
+ * @dev: device pointer
+ * @enable: enable/disable flag
+ */
+static void skl_enable_miscbdcge(struct device *dev, bool enable)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ u32 val;
+
+ val = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0;
+
+ update_pci_dword(pci, AZX_PCIREG_CGCTL, AZX_CGCTL_MISCBDCGE_MASK, val);
+}
+
+/*
+ * While performing reset, controller may not come back properly causing
+ * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset
+ * (init chip) and then again set CGCTL.MISCBDCGE to 1
+ */
+static int skl_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+ int ret;
+
+ skl_enable_miscbdcge(bus->dev, false);
+ ret = snd_hdac_bus_init_chip(bus, full_reset);
+ skl_enable_miscbdcge(bus->dev, true);
+
+ return ret;
+}
+
/* called from IRQ */
static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr)
{
return ret;
snd_hdac_bus_stop_chip(bus);
+ skl_enable_miscbdcge(bus->dev, false);
snd_hdac_bus_enter_link_reset(bus);
+ skl_enable_miscbdcge(bus->dev, true);
return 0;
}
struct hdac_bus *bus = ebus_to_hbus(ebus);
skl_init_pci(skl);
- snd_hdac_bus_init_chip(bus, true);
+ skl_init_chip(bus, true);
return skl_resume_dsp(skl);
}
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
/*
* Do not suspend if streams which are marked ignore suspend are
* running, we need to save the state for these and continue
*/
if (skl->supend_active) {
+ snd_hdac_ext_bus_link_power_down_all(ebus);
+ enable_irq_wake(bus->irq);
pci_save_state(pci);
pci_disable_device(pci);
return 0;
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
int ret;
/*
if (skl->supend_active) {
pci_restore_state(pci);
ret = pci_enable_device(pci);
+ snd_hdac_ext_bus_link_power_up_all(ebus);
+ disable_irq_wake(bus->irq);
} else {
ret = _skl_resume(ebus);
}
* back to the sanity state.
*/
snd_hdac_bus_stop_chip(bus);
- snd_hdac_bus_init_chip(bus, true);
+ skl_init_chip(bus, true);
}
}
}
/* initialize chip */
skl_init_pci(skl);
- snd_hdac_bus_init_chip(bus, true);
+ skl_init_chip(bus, true);
/* codec detection */
if (!bus->codec_mask) {
dev_dbg(bus->dev, "error failed to register dsp\n");
goto out_mach_free;
}
+ skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
+
}
if (ebus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(ebus);
goto out_unregister;
/*configure PM */
- pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY);
- pm_runtime_use_autosuspend(bus->dev);
pm_runtime_put_noidle(bus->dev);
pm_runtime_allow(bus->dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct skl *skl = ebus_to_skl(ebus);
+ if (skl->tplg)
+ release_firmware(skl->tplg);
+
if (pci_dev_run_wake(pci))
pm_runtime_get_noresume(&pci->dev);
pci_dev_put(pci);