mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-04 12:16:41 +00:00
soundwire: intel_ace2x: add DAI hw_params/prepare/hw_free callbacks
The code is fork-lifted from intel.c and is mostly similar *except* for the SHIM configuration which cannot be done here with the introduction of HDAudio Extended links. The ACE2.x SOF side also requires the hw_free and trigger callbacks to be implemented for HDaudio DMA management Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Rander Wang <rander.wang@intel.com> Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com> Link: https://lore.kernel.org/r/20230802061947.3788679-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
e66f91a2d1
commit
8c4c9a9ae5
@ -10,6 +10,7 @@
|
||||
#include <linux/soundwire/sdw_registers.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_intel.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/hda-mlink.h>
|
||||
#include "cadence_master.h"
|
||||
#include "bus.h"
|
||||
@ -191,10 +192,292 @@ static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw)
|
||||
return hdac_bus_eml_sdw_check_cmdsync_unlocked(sdw->link_res->hbus);
|
||||
}
|
||||
|
||||
/* DAI callbacks */
|
||||
static int intel_params_stream(struct sdw_intel *sdw,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai,
|
||||
struct snd_pcm_hw_params *hw_params,
|
||||
int link_id, int alh_stream_id)
|
||||
{
|
||||
struct sdw_intel_link_res *res = sdw->link_res;
|
||||
struct sdw_intel_stream_params_data params_data;
|
||||
|
||||
params_data.substream = substream;
|
||||
params_data.dai = dai;
|
||||
params_data.hw_params = hw_params;
|
||||
params_data.link_id = link_id;
|
||||
params_data.alh_stream_id = alh_stream_id;
|
||||
|
||||
if (res->ops && res->ops->params_stream && res->dev)
|
||||
return res->ops->params_stream(res->dev,
|
||||
¶ms_data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int intel_free_stream(struct sdw_intel *sdw,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai,
|
||||
int link_id)
|
||||
|
||||
{
|
||||
struct sdw_intel_link_res *res = sdw->link_res;
|
||||
struct sdw_intel_stream_free_data free_data;
|
||||
|
||||
free_data.substream = substream;
|
||||
free_data.dai = dai;
|
||||
free_data.link_id = link_id;
|
||||
|
||||
if (res->ops && res->ops->free_stream && res->dev)
|
||||
return res->ops->free_stream(res->dev,
|
||||
&free_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DAI operations
|
||||
*/
|
||||
static int intel_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
|
||||
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||
struct sdw_cdns_dai_runtime *dai_runtime;
|
||||
struct sdw_cdns_pdi *pdi;
|
||||
struct sdw_stream_config sconfig;
|
||||
struct sdw_port_config *pconfig;
|
||||
int ch, dir;
|
||||
int ret;
|
||||
|
||||
dai_runtime = cdns->dai_runtime_array[dai->id];
|
||||
if (!dai_runtime)
|
||||
return -EIO;
|
||||
|
||||
ch = params_channels(params);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
dir = SDW_DATA_DIR_RX;
|
||||
else
|
||||
dir = SDW_DATA_DIR_TX;
|
||||
|
||||
pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);
|
||||
|
||||
if (!pdi) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* the SHIM will be configured in the callback functions */
|
||||
|
||||
sdw_cdns_config_stream(cdns, ch, dir, pdi);
|
||||
|
||||
/* store pdi and state, may be needed in prepare step */
|
||||
dai_runtime->paused = false;
|
||||
dai_runtime->suspended = false;
|
||||
dai_runtime->pdi = pdi;
|
||||
|
||||
/* Inform DSP about PDI stream number */
|
||||
ret = intel_params_stream(sdw, substream, dai, params,
|
||||
sdw->instance,
|
||||
pdi->intel_alh_id);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
sconfig.direction = dir;
|
||||
sconfig.ch_count = ch;
|
||||
sconfig.frame_rate = params_rate(params);
|
||||
sconfig.type = dai_runtime->stream_type;
|
||||
|
||||
sconfig.bps = snd_pcm_format_width(params_format(params));
|
||||
|
||||
/* Port configuration */
|
||||
pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
|
||||
if (!pconfig) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
pconfig->num = pdi->num;
|
||||
pconfig->ch_mask = (1 << ch) - 1;
|
||||
|
||||
ret = sdw_stream_add_master(&cdns->bus, &sconfig,
|
||||
pconfig, 1, dai_runtime->stream);
|
||||
if (ret)
|
||||
dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
|
||||
|
||||
kfree(pconfig);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intel_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
|
||||
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||
struct sdw_cdns_dai_runtime *dai_runtime;
|
||||
int ch, dir;
|
||||
int ret = 0;
|
||||
|
||||
dai_runtime = cdns->dai_runtime_array[dai->id];
|
||||
if (!dai_runtime) {
|
||||
dev_err(dai->dev, "failed to get dai runtime in %s\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dai_runtime->suspended) {
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_pcm_hw_params *hw_params;
|
||||
|
||||
hw_params = &rtd->dpcm[substream->stream].hw_params;
|
||||
|
||||
dai_runtime->suspended = false;
|
||||
|
||||
/*
|
||||
* .prepare() is called after system resume, where we
|
||||
* need to reinitialize the SHIM/ALH/Cadence IP.
|
||||
* .prepare() is also called to deal with underflows,
|
||||
* but in those cases we cannot touch ALH/SHIM
|
||||
* registers
|
||||
*/
|
||||
|
||||
/* configure stream */
|
||||
ch = params_channels(hw_params);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
dir = SDW_DATA_DIR_RX;
|
||||
else
|
||||
dir = SDW_DATA_DIR_TX;
|
||||
|
||||
/* the SHIM will be configured in the callback functions */
|
||||
|
||||
sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi);
|
||||
|
||||
/* Inform DSP about PDI stream number */
|
||||
ret = intel_params_stream(sdw, substream, dai,
|
||||
hw_params,
|
||||
sdw->instance,
|
||||
dai_runtime->pdi->intel_alh_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
|
||||
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||
struct sdw_cdns_dai_runtime *dai_runtime;
|
||||
int ret;
|
||||
|
||||
dai_runtime = cdns->dai_runtime_array[dai->id];
|
||||
if (!dai_runtime)
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* The sdw stream state will transition to RELEASED when stream->
|
||||
* master_list is empty. So the stream state will transition to
|
||||
* DEPREPARED for the first cpu-dai and to RELEASED for the last
|
||||
* cpu-dai.
|
||||
*/
|
||||
ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "remove master from stream %s failed: %d\n",
|
||||
dai_runtime->stream->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = intel_free_stream(sdw, substream, dai, sdw->instance);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "intel_free_stream: failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dai_runtime->pdi = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
|
||||
void *stream, int direction)
|
||||
{
|
||||
return cdns_set_sdw_stream(dai, stream, direction);
|
||||
}
|
||||
|
||||
static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
|
||||
int direction)
|
||||
{
|
||||
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
|
||||
struct sdw_cdns_dai_runtime *dai_runtime;
|
||||
|
||||
dai_runtime = cdns->dai_runtime_array[dai->id];
|
||||
if (!dai_runtime)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return dai_runtime->stream;
|
||||
}
|
||||
|
||||
static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
|
||||
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||
struct sdw_intel_link_res *res = sdw->link_res;
|
||||
struct sdw_cdns_dai_runtime *dai_runtime;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* The .trigger callback is used to program HDaudio DMA and send required IPC to audio
|
||||
* firmware.
|
||||
*/
|
||||
if (res->ops && res->ops->trigger) {
|
||||
ret = res->ops->trigger(substream, cmd, dai);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dai_runtime = cdns->dai_runtime_array[dai->id];
|
||||
if (!dai_runtime) {
|
||||
dev_err(dai->dev, "failed to get dai runtime in %s\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
|
||||
/*
|
||||
* The .prepare callback is used to deal with xruns and resume operations.
|
||||
* In the case of xruns, the DMAs and SHIM registers cannot be touched,
|
||||
* but for resume operations the DMAs and SHIM registers need to be initialized.
|
||||
* the .trigger callback is used to track the suspend case only.
|
||||
*/
|
||||
|
||||
dai_runtime->suspended = true;
|
||||
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
dai_runtime->paused = true;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
dai_runtime->paused = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
|
||||
.hw_params = intel_hw_params,
|
||||
.prepare = intel_prepare,
|
||||
.hw_free = intel_hw_free,
|
||||
.trigger = intel_trigger,
|
||||
.set_stream = intel_pcm_set_sdw_stream,
|
||||
.get_stream = intel_get_sdw_stream,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver dai_component = {
|
||||
|
Loading…
Reference in New Issue
Block a user