Merge branch 'asoc-5.2' into asoc-5.3

This commit is contained in:
Mark Brown 2019-05-30 16:47:43 +01:00
commit 79b3b7c4a3
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
14 changed files with 199 additions and 134 deletions

View File

@ -283,6 +283,11 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv,
codec_dai =
dai_props->codec_dai = &priv->dais[li->dais++];
ret = asoc_simple_parse_daifmt(dev, node, codec,
prefix, &dai_link->dai_fmt);
if (ret < 0)
goto dai_link_of_err;
simple_parse_mclk_fs(top, cpu, codec, dai_props, prefix);
ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu);
@ -293,11 +298,6 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv,
if (ret < 0)
goto dai_link_of_err;
ret = asoc_simple_parse_daifmt(dev, node, dai_link->codecs->of_node,
prefix, &dai_link->dai_fmt);
if (ret < 0)
goto dai_link_of_err;
ret = asoc_simple_parse_platform(plat, dai_link);
if (ret < 0)
goto dai_link_of_err;

View File

@ -228,7 +228,10 @@ static void soc_init_card_debugfs(struct snd_soc_card *card)
static void soc_cleanup_card_debugfs(struct snd_soc_card *card)
{
if (!card->debugfs_card_root)
return;
debugfs_remove_recursive(card->debugfs_card_root);
card->debugfs_card_root = NULL;
}
static void snd_soc_debugfs_init(void)
@ -1010,12 +1013,14 @@ static void soc_remove_link_components(struct snd_soc_card *card,
struct snd_soc_component *component;
struct snd_soc_rtdcom_list *rtdcom;
mutex_lock(&client_mutex);
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
if (component->driver->remove_order == order)
soc_remove_component(component);
}
mutex_unlock(&client_mutex);
}
static void soc_remove_dai_links(struct snd_soc_card *card)
@ -2035,8 +2040,10 @@ static void soc_check_tplg_fes(struct snd_soc_card *card)
static int soc_cleanup_card_resources(struct snd_soc_card *card)
{
/* free the ALSA card at first; this syncs with pending operations */
if (card->snd_card)
if (card->snd_card) {
snd_card_free(card->snd_card);
card->snd_card = NULL;
}
/* remove and free each DAI */
soc_remove_dai_links(card);

View File

@ -2194,7 +2194,10 @@ static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
if (!dapm->debugfs_dapm)
return;
debugfs_remove_recursive(dapm->debugfs_dapm);
dapm->debugfs_dapm = NULL;
}
#else

View File

@ -349,6 +349,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
struct sof_abi_hdr *data = cdata->data;
size_t size = data->size + sizeof(*data);
int ret, err;
if (be->max > sizeof(ucontrol->value.bytes.data)) {
@ -358,10 +359,10 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
if (data->size > be->max) {
if (size > be->max) {
dev_err_ratelimited(sdev->dev,
"error: size too big %d bytes max is %d\n",
data->size, be->max);
"error: size too big %zu bytes max is %d\n",
size, be->max);
return -EINVAL;
}
@ -375,7 +376,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
}
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, data->size);
memcpy(data, ucontrol->value.bytes.data, size);
/* notify DSP of byte control updates */
snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,

View File

@ -382,7 +382,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
if (IS_ERR(plat_data->pdev_mach)) {
ret = PTR_ERR(plat_data->pdev_mach);
goto comp_err;
goto fw_run_err;
}
dev_dbg(sdev->dev, "created machine %s\n",
@ -393,8 +393,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return 0;
comp_err:
snd_soc_unregister_component(sdev->dev);
#if !IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)
fw_run_err:
snd_sof_fw_unload(sdev);
fw_load_err:
@ -403,6 +402,21 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
snd_sof_free_debug(sdev);
dbg_err:
snd_sof_remove(sdev);
#else
/*
* when the probe_continue is handled in a work queue, the
* probe does not fail so we don't release resources here.
* They will be released with an explicit call to
* snd_sof_device_remove() when the PCI/ACPI device is removed
*/
fw_run_err:
fw_load_err:
ipc_err:
dbg_err:
#endif
return ret;
}
@ -484,7 +498,6 @@ int snd_sof_device_remove(struct device *dev)
snd_sof_ipc_free(sdev);
snd_sof_free_debug(sdev);
snd_sof_free_trace(sdev);
snd_sof_remove(sdev);
/*
* Unregister machine driver. This will unbind the snd_card which
@ -494,6 +507,14 @@ int snd_sof_device_remove(struct device *dev)
if (!IS_ERR_OR_NULL(pdata->pdev_mach))
platform_device_unregister(pdata->pdev_mach);
/*
* Unregistering the machine driver results in unloading the topology.
* Some widgets, ex: scheduler, attempt to power down the core they are
* scheduled on, when they are unloaded. Therefore, the DSP must be
* removed only after the topology has been unloaded.
*/
snd_sof_remove(sdev);
/* release firmware */
release_firmware(pdata->fw);
pdata->fw = NULL;

View File

@ -283,6 +283,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
SHIM_IMRX, SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
spin_lock_irq(&sdev->ipc_lock);
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@ -294,6 +296,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
snd_sof_ipc_reply(sdev, ipcx);
bdw_dsp_done(sdev);
spin_unlock_irq(&sdev->ipc_lock);
}
ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD);
@ -485,7 +489,6 @@ static void bdw_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
unsigned long flags;
int ret = 0;
/*
@ -501,8 +504,6 @@ static void bdw_get_reply(struct snd_sof_dev *sdev)
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
spin_lock_irqsave(&sdev->ipc_lock, flags);
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
@ -521,8 +522,6 @@ static void bdw_get_reply(struct snd_sof_dev *sdev)
}
msg->reply_error = ret;
spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static void bdw_host_done(struct snd_sof_dev *sdev)

View File

@ -329,6 +329,9 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
SHIM_IMRX,
SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
spin_lock_irq(&sdev->ipc_lock);
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@ -340,6 +343,8 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
snd_sof_ipc_reply(sdev, ipcx);
byt_dsp_done(sdev);
spin_unlock_irq(&sdev->ipc_lock);
}
/* new message from DSP */
@ -383,7 +388,6 @@ static void byt_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
unsigned long flags;
int ret = 0;
/*
@ -399,8 +403,6 @@ static void byt_get_reply(struct snd_sof_dev *sdev)
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
spin_lock_irqsave(&sdev->ipc_lock, flags);
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
@ -419,8 +421,6 @@ static void byt_get_reply(struct snd_sof_dev *sdev)
}
msg->reply_error = ret;
spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static void byt_host_done(struct snd_sof_dev *sdev)

View File

@ -64,6 +64,8 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
CNL_DSP_REG_HIPCCTL,
CNL_DSP_REG_HIPCCTL_DONE, 0);
spin_lock_irq(&sdev->ipc_lock);
/* handle immediate reply from DSP core */
hda_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, msg);
@ -75,6 +77,8 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
cnl_ipc_dsp_done(sdev);
spin_unlock_irq(&sdev->ipc_lock);
ret = IRQ_HANDLED;
}

View File

@ -161,21 +161,105 @@ int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
return 0;
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/*
* While performing reset, controller may not come back properly and causing
* issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset
* (init chip) and then again set CGCTL.MISCBDCGE to 1
*/
int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset)
{
struct hdac_bus *bus = sof_to_bus(sdev);
int ret;
struct hdac_stream *stream;
int sd_offset, ret = 0;
if (bus->chip_init)
return 0;
hda_dsp_ctrl_misc_clock_gating(sdev, false);
ret = snd_hdac_bus_init_chip(bus, full_reset);
if (full_reset) {
/* clear WAKESTS */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
SOF_HDA_WAKESTS_INT_MASK,
SOF_HDA_WAKESTS_INT_MASK);
/* reset HDA controller */
ret = hda_dsp_ctrl_link_reset(sdev, true);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to reset HDA controller\n");
return ret;
}
usleep_range(500, 1000);
/* exit HDA controller reset */
ret = hda_dsp_ctrl_link_reset(sdev, false);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to exit HDA controller reset\n");
return ret;
}
usleep_range(1000, 1200);
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* check to see if controller is ready */
if (!snd_hdac_chip_readb(bus, GCTL)) {
dev_dbg(bus->dev, "controller not ready!\n");
return -EBUSY;
}
/* Accept unsolicited responses */
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
/* detect codecs */
if (!bus->codec_mask) {
bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
}
#endif
/* clear stream status */
list_for_each_entry(stream, &bus->stream_list, list) {
sd_offset = SOF_STREAM_SD_OFFSET(stream);
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset +
SOF_HDA_ADSP_REG_CL_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK,
SOF_HDA_CL_DMA_SD_INT_MASK);
}
/* clear WAKESTS */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
SOF_HDA_WAKESTS_INT_MASK,
SOF_HDA_WAKESTS_INT_MASK);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* clear rirb status */
snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
#endif
/* clear interrupt status register */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* initialize the codec command I/O */
snd_hdac_bus_init_cmd_io(bus);
#endif
/* enable CIE and GIE interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* program the position buffer */
if (bus->use_posbuf && bus->posbuf.addr) {
snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
snd_hdac_chip_writel(bus, DPUBASE,
upper_32_bits(bus->posbuf.addr));
}
#endif
bus->chip_init = true;
hda_dsp_ctrl_misc_clock_gating(sdev, true);
return ret;
}
#endif

View File

@ -72,7 +72,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
struct sof_ipc_cmd_hdr *hdr;
unsigned long flags;
int ret = 0;
/*
@ -84,7 +83,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
return;
}
spin_lock_irqsave(&sdev->ipc_lock, flags);
hdr = msg->msg_data;
if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) {
@ -123,7 +121,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
out:
msg->reply_error = ret;
spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static bool hda_dsp_ipc_is_sof(uint32_t msg)
@ -172,6 +169,18 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
HDA_DSP_REG_HIPCCTL,
HDA_DSP_REG_HIPCCTL_DONE, 0);
/*
* Make sure the interrupt thread cannot be preempted between
* waking up the sender and re-enabling the interrupt. Also
* protect against a theoretical race with sof_ipc_tx_message():
* if the DSP is fast enough to receive an IPC message, reply to
* it, and the host interrupt processing calls this function on
* a different core from the one, where the sending is taking
* place, the message might not yet be marked as expecting a
* reply.
*/
spin_lock_irq(&sdev->ipc_lock);
/* handle immediate reply from DSP core - ignore ROM messages */
if (hda_dsp_ipc_is_sof(msg)) {
hda_dsp_ipc_get_reply(sdev);
@ -187,6 +196,8 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
/* set the done bit */
hda_dsp_ipc_dsp_done(sdev);
spin_unlock_irq(&sdev->ipc_lock);
ret = IRQ_HANDLED;
}

View File

@ -264,9 +264,12 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
return tplg_filename;
}
#endif
static int hda_init_caps(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_ext_link *hlink;
struct snd_soc_acpi_mach_params *mach_params;
struct snd_soc_acpi_mach *hda_mach;
@ -274,8 +277,9 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
struct snd_soc_acpi_mach *mach;
const char *tplg_filename;
int codec_num = 0;
int ret = 0;
int i;
#endif
int ret = 0;
device_disable_async_suspend(bus->dev);
@ -283,6 +287,14 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
if (bus->ppcap)
dev_dbg(sdev->dev, "PP capability, will probe DSP later.\n");
ret = hda_dsp_ctrl_init_chip(sdev, true);
if (ret < 0) {
dev_err(bus->dev, "error: init chip failed with ret: %d\n",
ret);
return ret;
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
if (bus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(bus);
@ -293,12 +305,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
return ret;
}
ret = hda_dsp_ctrl_init_chip(sdev, true);
if (ret < 0) {
dev_err(bus->dev, "error: init chip failed with ret: %d\n", ret);
goto out;
}
/* codec detection */
if (!bus->codec_mask) {
dev_info(bus->dev, "no hda codecs found!\n");
@ -339,8 +345,10 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
/* use local variable for readability */
tplg_filename = pdata->tplg_filename;
tplg_filename = fixup_tplg_name(sdev, tplg_filename);
if (!tplg_filename)
goto out;
if (!tplg_filename) {
hda_codec_i915_exit(sdev);
return ret;
}
pdata->tplg_filename = tplg_filename;
}
}
@ -364,34 +372,9 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
*/
list_for_each_entry(hlink, &bus->hlink_list, list)
snd_hdac_ext_bus_link_put(bus, hlink);
return 0;
out:
hda_codec_i915_exit(sdev);
return ret;
}
#else
static int hda_init_caps(struct snd_sof_dev *sdev)
{
/*
* set CGCTL.MISCBDCGE to 0 during reset and set back to 1
* when reset finished.
* TODO: maybe no need for init_caps?
*/
hda_dsp_ctrl_misc_clock_gating(sdev, 0);
/* clear WAKESTS */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
SOF_HDA_WAKESTS_INT_MASK,
SOF_HDA_WAKESTS_INT_MASK);
return 0;
}
#endif
return 0;
}
static const struct sof_intel_dsp_desc
*get_chip_info(struct snd_sof_pdata *pdata)
@ -409,9 +392,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct sof_intel_hda_dev *hdev;
struct hdac_bus *bus;
struct hdac_stream *stream;
const struct sof_intel_dsp_desc *chip;
int sd_offset, ret = 0;
int ret = 0;
/*
* detect DSP by checking class/subclass/prog-id information
@ -562,49 +544,6 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
goto free_ipc_irq;
/* reset HDA controller */
ret = hda_dsp_ctrl_link_reset(sdev, true);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to reset HDA controller\n");
goto free_ipc_irq;
}
/* exit HDA controller reset */
ret = hda_dsp_ctrl_link_reset(sdev, false);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to exit HDA controller reset\n");
goto free_ipc_irq;
}
/* clear stream status */
list_for_each_entry(stream, &bus->stream_list, list) {
sd_offset = SOF_STREAM_SD_OFFSET(stream);
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset +
SOF_HDA_ADSP_REG_CL_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK,
SOF_HDA_CL_DMA_SD_INT_MASK);
}
/* clear WAKESTS */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
SOF_HDA_WAKESTS_INT_MASK,
SOF_HDA_WAKESTS_INT_MASK);
/* clear interrupt status register */
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM);
/* enable CIE and GIE interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN);
/* re-enable CGCTL.MISCBDCGE after reset */
hda_dsp_ctrl_misc_clock_gating(sdev, true);
device_disable_async_suspend(&pci->dev);
/* enable DSP features */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_GPROCEN, SOF_HDA_PPCTL_GPROCEN);

View File

@ -308,19 +308,8 @@ EXPORT_SYMBOL(sof_ipc_tx_message);
int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
{
struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
unsigned long flags;
/*
* Protect against a theoretical race with sof_ipc_tx_message(): if the
* DSP is fast enough to receive an IPC message, reply to it, and the
* host interrupt processing calls this function on a different core
* from the one, where the sending is taking place, the message might
* not yet be marked as expecting a reply.
*/
spin_lock_irqsave(&sdev->ipc_lock, flags);
if (msg->ipc_complete) {
spin_unlock_irqrestore(&sdev->ipc_lock, flags);
dev_err(sdev->dev, "error: no reply expected, received 0x%x",
msg_id);
return -EINVAL;
@ -330,8 +319,6 @@ int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
msg->ipc_complete = true;
wake_up(&msg->waitq);
spin_unlock_irqrestore(&sdev->ipc_lock, flags);
return 0;
}
EXPORT_SYMBOL(snd_sof_ipc_reply);

View File

@ -211,8 +211,8 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream,
/* save pcm hw_params */
memcpy(&spcm->params[substream->stream], params, sizeof(*params));
INIT_WORK(&spcm->stream[substream->stream].period_elapsed_work,
sof_pcm_period_elapsed_work);
/* clear hw_params_upon_resume flag */
spcm->hw_params_upon_resume[substream->stream] = 0;
return ret;
}
@ -428,8 +428,8 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
dev_dbg(sdev->dev, "pcm: open stream %d dir %d\n", spcm->pcm.pcm_id,
substream->stream);
/* clear hw_params_upon_resume flag */
spcm->hw_params_upon_resume[substream->stream] = 0;
INIT_WORK(&spcm->stream[substream->stream].period_elapsed_work,
sof_pcm_period_elapsed_work);
caps = &spcm->pcm.caps[substream->stream];

View File

@ -1329,6 +1329,15 @@ static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
gpiod_set_value_cansleep(scodec->gpio_pa,
!!SND_SOC_DAPM_EVENT_ON(event));
if (SND_SOC_DAPM_EVENT_ON(event)) {
/*
* Need a delay to wait for DAC to push the data. 700ms seems
* to be the best compromise not to feel this delay while
* playing a sound.
*/
msleep(700);
}
return 0;
}