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; err = snd_hdac_stream_setup(hdac_stream(stream)); if (err < 0) return err; hdac_stream(stream)->prepared = 1; return err; }
static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *link_dev; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct hdac_ext_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct skl_pipe_params p_params = {0}; link_dev = snd_hdac_ext_stream_assign(ebus, substream, HDAC_EXT_STREAM_TYPE_LINK); if (!link_dev) return -EBUSY; snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); /* set the stream tag in the codec dai dma params */ dma_params = snd_soc_dai_get_dma_data(codec_dai, substream); if (dma_params) dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; return skl_tplg_be_update_params(dai, &p_params); }
static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus, struct snd_pcm_substream *substream, size_t size) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); hdac_stream(stream)->bufsize = 0; hdac_stream(stream)->period_bytes = 0; hdac_stream(stream)->format_val = 0; return snd_pcm_lib_malloc_pages(substream, size); }
static int skl_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; return skl_substream_free_pages(ebus_to_hbus(ebus), substream); }
static snd_pcm_uframes_t skl_platform_pcm_pointer (struct snd_pcm_substream *substream) { struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); unsigned int pos; /* use the position buffer as default */ pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream)); if (pos >= hdac_stream(hstream)->bufsize) pos = 0; return bytes_to_frames(substream->runtime, pos); }
static int skl_dsp_prepare(struct device *dev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab) { struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_ext_stream *estream; struct hdac_stream *stream; struct snd_pcm_substream substream; int ret; if (!bus) return -ENODEV; memset(&substream, 0, sizeof(substream)); substream.stream = SNDRV_PCM_STREAM_PLAYBACK; estream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST); if (!estream) return -ENODEV; stream = hdac_stream(estream); /* assign decouple host dma channel */ ret = snd_hdac_dsp_prepare(stream, format, size, dmab); if (ret < 0) return ret; skl_dsp_setup_spib(dev, size, stream->stream_tag, true); return stream->stream_tag; }
static int skl_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct skl_pipe_params p_params = {0}; struct skl_module_cfg *m_cfg; int ret, dma_id; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); ret = skl_substream_alloc_pages(ebus, substream, params_buffer_bytes(params)); if (ret < 0) return ret; dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n", runtime->rate, runtime->channels, runtime->format); dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.host_dma_id = dma_id; p_params.stream = substream->stream; m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream); if (m_cfg) skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params); return 0; }
static int skl_get_time_info(struct snd_pcm_substream *substream, struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { struct hdac_ext_stream *sstream = get_hdac_ext_stream(substream); struct hdac_stream *hstr = hdac_stream(sstream); u64 nsec; if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) { snd_pcm_gettime(substream->runtime, system_ts); nsec = timecounter_read(&hstr->tc); nsec = div_u64(nsec, 3); /* can be optimized */ if (audio_tstamp_config->report_delay) nsec = skl_adjust_codec_delay(substream, nsec); *audio_ts = ns_to_timespec(nsec); audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */ audio_tstamp_report->accuracy = 42; /* 24MHzWallClk == 42ns resolution */ } else { audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; } return 0; }
static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct hdac_stream *hstream = hdac_stream(stream); struct hdac_bus *bus = hstream->bus; return hbus_to_ebus(bus); }
static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_ext_stream *stream; int start; unsigned long cookie; struct hdac_stream *hstr; stream = get_hdac_ext_stream(substream); hstr = hdac_stream(stream); if (!hstr->prepared) return -EPIPE; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: start = 1; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: start = 0; break; default: return -EINVAL; } spin_lock_irqsave(&bus->reg_lock, cookie); if (start) { snd_hdac_stream_start(hdac_stream(stream), true); snd_hdac_stream_timecounter_init(hstr, 0); } else { snd_hdac_stream_stop(hdac_stream(stream)); } spin_unlock_irqrestore(&bus->reg_lock, cookie); return 0; }
static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); unsigned int format_val = 0; 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); if (dma_params) format_val = dma_params->format; 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); link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); if (!link) return -EINVAL; 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); link_dev->link_prepared = 1; return 0; }
static int skl_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream; struct snd_pcm_runtime *runtime = substream->runtime; struct skl_dma_params *dma_params; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); stream = snd_hdac_ext_stream_assign(ebus, substream, skl_get_host_stream_type(ebus)); if (stream == NULL) return -EBUSY; skl_set_pcm_constrains(ebus, runtime); /* * disable WALLCLOCK timestamps for capture streams * until we figure out how to handle digital inputs */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; } runtime->private_data = stream; dma_params = kzalloc(sizeof(*dma_params), GFP_KERNEL); if (!dma_params) return -ENOMEM; dma_params->stream_tag = hdac_stream(stream)->stream_tag; snd_soc_dai_set_dma_data(dai, substream, dma_params); dev_dbg(dai->dev, "stream tag set in dma params=%d\n", dma_params->stream_tag); skl_set_suspend_active(substream, dai, true); snd_pcm_set_sync(substream); return 0; }
static int skl_link_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); struct hdac_ext_link *link; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); link_dev->link_prepared = 0; link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); if (!link) return -EINVAL; snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_dev)->stream_tag); snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); return 0; }
static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_ext_stream *stream; struct snd_pcm_substream *s; bool start; int sbits = 0; unsigned long cookie; struct hdac_stream *hstr; stream = get_hdac_ext_stream(substream); hstr = hdac_stream(stream); dev_dbg(bus->dev, "In %s cmd=%d\n", __func__, cmd); if (!hstr->prepared) return -EPIPE; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: start = true; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: start = false; break; default: return -EINVAL; } snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; stream = get_hdac_ext_stream(s); sbits |= 1 << hdac_stream(stream)->index; snd_pcm_trigger_done(s, substream); } spin_lock_irqsave(&bus->reg_lock, cookie); /* first, set SYNC bits of corresponding streams */ snd_hdac_stream_sync_trigger(hstr, true, sbits, AZX_REG_SSYNC); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; stream = get_hdac_ext_stream(s); if (start) snd_hdac_stream_start(hdac_stream(stream), true); else snd_hdac_stream_stop(hdac_stream(stream)); } spin_unlock_irqrestore(&bus->reg_lock, cookie); snd_hdac_stream_sync(hstr, start, sbits); spin_lock_irqsave(&bus->reg_lock, cookie); /* reset SYNC bits */ snd_hdac_stream_sync_trigger(hstr, false, sbits, AZX_REG_SSYNC); if (start) snd_hdac_stream_timecounter_init(hstr, sbits); spin_unlock_irqrestore(&bus->reg_lock, cookie); return 0; }
static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct skl *skl = get_skl_ctx(dai->dev); struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_soc_dapm_widget *w; int ret; mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); if (!mconfig) return -EIO; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; else w = dai->capture_widget; switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: if (!w->ignore_suspend) { skl_pcm_prepare(substream, dai); /* * enable DMA Resume enable bit for the stream, set the * dpib & lpib position to resume before starting the * DMA */ snd_hdac_ext_stream_drsm_enable(ebus, true, hdac_stream(stream)->index); snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); } case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* * Start HOST DMA and Start FE Pipe.This is to make sure that * there are no underrun/overrun in the case when the FE * pipeline is started but there is a delay in starting the * DMA channel on the host. */ snd_hdac_ext_stream_decouple(ebus, stream, true); ret = skl_decoupled_trigger(substream, cmd); if (ret < 0) return ret; return skl_run_pipe(ctx, mconfig->pipe); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: /* * Stop FE Pipe first and stop DMA. This is to make sure that * there are no underrun/overrun in the case if there is a delay * between the two operations. */ ret = skl_stop_pipe(ctx, mconfig->pipe); if (ret < 0) return ret; ret = skl_decoupled_trigger(substream, cmd); if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) { /* save the dpib and lpib positions */ stream->dpib = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE + (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: return -EINVAL; } return 0; }