From patchwork Thu May 8 13:07:26 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Nikula X-Patchwork-Id: 4136181 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 935C69F23C for ; Thu, 8 May 2014 13:11:49 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 8A7AD2011B for ; Thu, 8 May 2014 13:11:48 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 2B34820173 for ; Thu, 8 May 2014 13:11:47 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 3DE6A2655D9; Thu, 8 May 2014 15:11:44 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,NO_DNS_FOR_FROM, UNPARSEABLE_RELAY autolearn=no version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id BDEFA26166B; Thu, 8 May 2014 15:08:44 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 90922265555; Thu, 8 May 2014 15:08:43 +0200 (CEST) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by alsa0.perex.cz (Postfix) with ESMTP id EC0722615EC for ; Thu, 8 May 2014 15:08:32 +0200 (CEST) Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP; 08 May 2014 06:03:14 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos; i="4.97,1010,1389772800"; d="scan'208"; a="528507835" Received: from mylly.fi.intel.com (HELO mylly.fi.intel.com.) ([10.237.72.53]) by fmsmga001.fm.intel.com with ESMTP; 08 May 2014 06:08:10 -0700 From: Jarkko Nikula To: alsa-devel@alsa-project.org Date: Thu, 8 May 2014 16:07:26 +0300 Message-Id: <1399554447-16297-7-git-send-email-jarkko.nikula@linux.intel.com> X-Mailer: git-send-email 2.0.0.rc0 In-Reply-To: <1399554447-16297-1-git-send-email-jarkko.nikula@linux.intel.com> References: <1399554447-16297-1-git-send-email-jarkko.nikula@linux.intel.com> Cc: Liam Girdwood , Mark Brown , Jarkko Nikula , Liam Girdwood Subject: [alsa-devel] [PATCH 6/7] ASoC: Intel: Add Baytrail suspend/resume support X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP From: Liam Girdwood Add suspend and resume support to Baytrail SST DSP. This is implemented by unloading firmware modules and putting DSP into reset prior suspend and restarting DSP again in normal boot state after resume. Context restore for running streams is implemented by scheduling a work from sst_byt_pcm_trigger() that will allocate a stream with existing parameters and start it from last known buffer position before suspend. [Jarkko: Squashed together 5 WIP patches from Liam and 1 from me] Signed-off-by: Liam Girdwood Signed-off-by: Jarkko Nikula --- sound/soc/intel/sst-baytrail-ipc.c | 69 ++++++++++++++++++++++++ sound/soc/intel/sst-baytrail-ipc.h | 4 ++ sound/soc/intel/sst-baytrail-pcm.c | 107 ++++++++++++++++++++++++++++++++++++- 3 files changed, 179 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index 2487b6e2dfec..7c1ec003d55d 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -173,6 +173,7 @@ struct sst_byt { /* boot */ wait_queue_head_t boot_wait; bool boot_complete; + struct sst_fw *fw; /* IPC messaging */ struct list_head tx_list; @@ -797,6 +798,73 @@ static struct sst_dsp_device byt_dev = { .ops = &sst_byt_ops, }; +int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + + dev_dbg(byt->dev, "dsp reset\n"); + sst_dsp_reset(byt->dsp); + sst_byt_drop_all(byt); + dev_dbg(byt->dev, "dsp in reset\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_noirq); + +int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + + dev_dbg(byt->dev, "free all blocks and unload fw\n"); + sst_fw_unload(byt->fw); + + return 0; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late); + +int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + int ret; + + dev_dbg(byt->dev, "reload dsp fw\n"); + + sst_dsp_reset(byt->dsp); + + ret = sst_fw_reload(byt->fw); + if (ret < 0) { + dev_err(dev, "error: failed to reload firmware\n"); + return ret; + } + + /* wait for DSP boot completion */ + byt->boot_complete = false; + sst_dsp_boot(byt->dsp); + dev_dbg(byt->dev, "dsp booting...\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_boot); + +int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + int err; + + dev_dbg(byt->dev, "wait for dsp reboot\n"); + + err = wait_event_timeout(byt->boot_wait, byt->boot_complete, + msecs_to_jiffies(IPC_BOOT_MSECS)); + if (err == 0) { + dev_err(byt->dev, "ipc: error DSP boot timeout\n"); + return -EIO; + } + + dev_dbg(byt->dev, "dsp rebooted\n"); + return 0; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); + int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_byt *byt; @@ -863,6 +931,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) } pdata->dsp = byt; + byt->fw = byt_sst_fw; return 0; diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h index b643d9892f60..06a4d202689b 100644 --- a/sound/soc/intel/sst-baytrail-ipc.h +++ b/sound/soc/intel/sst-baytrail-ipc.h @@ -66,5 +66,9 @@ int sst_byt_get_dsp_position(struct sst_byt *byt, int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata); void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata); struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt); +int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata); +int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata); +int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata); +int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata); #endif diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index e2c2540ffff4..00a2118d20f5 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -48,6 +48,8 @@ struct sst_byt_pcm_data { /* latest DSP DMA hw pointer */ u32 hw_ptr; + + struct work_struct work; }; /* private data for the driver */ @@ -133,6 +135,38 @@ static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream) return 0; } +static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_byt *byt = pdata->byt; + int ret; + + /* commit stream using existing stream params */ + ret = sst_byt_stream_commit(byt, pcm_data->stream); + if (ret < 0) { + dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret); + return ret; + } + + sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr); + + dev_dbg(rtd->dev, "stream context restored at offset %d\n", + pcm_data->hw_ptr); + + return 0; +} + +static void sst_byt_pcm_work(struct work_struct *work) +{ + struct sst_byt_pcm_data *pcm_data = + container_of(work, struct sst_byt_pcm_data, work); + + sst_byt_pcm_restore_stream_context(pcm_data->substream); +} + static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -148,6 +182,8 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) sst_byt_stream_start(byt, pcm_data->stream, 0); break; case SNDRV_PCM_TRIGGER_RESUME: + schedule_work(&pcm_data->work); + break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: sst_byt_stream_resume(byt, pcm_data->stream); break; @@ -344,8 +380,10 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform) priv_data->byt = plat_data->dsp; snd_soc_platform_set_drvdata(platform, priv_data); - for (i = 0; i < ARRAY_SIZE(byt_dais); i++) + for (i = 0; i < ARRAY_SIZE(byt_dais); i++) { mutex_init(&priv_data->pcm[i].mutex); + INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work); + } return 0; } @@ -367,6 +405,72 @@ static const struct snd_soc_component_driver byt_dai_component = { .name = "byt-dai", }; +#ifdef CONFIG_PM +static int sst_byt_pcm_dev_suspend_noirq(struct device *dev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(dev); + int ret; + + dev_dbg(dev, "suspending noirq\n"); + + /* at this point all streams will be stopped and context saved */ + ret = sst_byt_dsp_suspend_noirq(dev, sst_pdata); + if (ret < 0) { + dev_err(dev, "failed to suspend %d\n", ret); + return ret; + } + + return ret; +} + +static int sst_byt_pcm_dev_suspend_late(struct device *dev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(dev); + int ret; + + dev_dbg(dev, "suspending late\n"); + + ret = sst_byt_dsp_suspend_late(dev, sst_pdata); + if (ret < 0) { + dev_err(dev, "failed to suspend %d\n", ret); + return ret; + } + + return ret; +} + +static int sst_byt_pcm_dev_resume_early(struct device *dev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(dev); + + dev_dbg(dev, "resume early\n"); + + /* load fw and boot DSP */ + return sst_byt_dsp_boot(dev, sst_pdata); +} + +static int sst_byt_pcm_dev_resume(struct device *dev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(dev); + + dev_dbg(dev, "resume\n"); + + /* wait for FW to finish booting */ + return sst_byt_dsp_wait_for_ready(dev, sst_pdata); +} + +static const struct dev_pm_ops sst_byt_pm_ops = { + .suspend_noirq = sst_byt_pcm_dev_suspend_noirq, + .suspend_late = sst_byt_pcm_dev_suspend_late, + .resume_early = sst_byt_pcm_dev_resume_early, + .resume = sst_byt_pcm_dev_resume, +}; + +#define SST_BYT_PM_OPS (&sst_byt_pm_ops) +#else +#define SST_BYT_PM_OPS NULL +#endif + static int sst_byt_pcm_dev_probe(struct platform_device *pdev) { struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); @@ -409,6 +513,7 @@ static struct platform_driver sst_byt_pcm_driver = { .driver = { .name = "baytrail-pcm-audio", .owner = THIS_MODULE, + .pm = SST_BYT_PM_OPS, }, .probe = sst_byt_pcm_dev_probe,