Path: blob/master/sound/soc/intel/atom/sst-mfld-platform-pcm.c
26493 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* sst_mfld_platform.c - Intel MID Platform driver3*4* Copyright (C) 2010-2014 Intel Corp5* Author: Vinod Koul <[email protected]>6* Author: Harsha Priya <[email protected]>7* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~8*9* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~10*/11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt1213#include <linux/slab.h>14#include <linux/io.h>15#include <linux/module.h>16#include <sound/core.h>17#include <sound/pcm.h>18#include <sound/pcm_params.h>19#include <sound/soc.h>20#include <sound/compress_driver.h>21#include <asm/platform_sst_audio.h>22#include "sst-mfld-platform.h"23#include "sst-atom-controls.h"2425struct sst_device *sst;26static DEFINE_MUTEX(sst_lock);2728int sst_register_dsp(struct sst_device *dev)29{30if (WARN_ON(!dev))31return -EINVAL;32if (!try_module_get(dev->dev->driver->owner))33return -ENODEV;34mutex_lock(&sst_lock);35if (sst) {36dev_err(dev->dev, "we already have a device %s\n", sst->name);37module_put(dev->dev->driver->owner);38mutex_unlock(&sst_lock);39return -EEXIST;40}41dev_dbg(dev->dev, "registering device %s\n", dev->name);42sst = dev;43mutex_unlock(&sst_lock);44return 0;45}46EXPORT_SYMBOL_GPL(sst_register_dsp);4748int sst_unregister_dsp(struct sst_device *dev)49{50if (WARN_ON(!dev))51return -EINVAL;52if (dev != sst)53return -EINVAL;5455mutex_lock(&sst_lock);5657if (!sst) {58mutex_unlock(&sst_lock);59return -EIO;60}6162module_put(sst->dev->driver->owner);63dev_dbg(dev->dev, "unreg %s\n", sst->name);64sst = NULL;65mutex_unlock(&sst_lock);66return 0;67}68EXPORT_SYMBOL_GPL(sst_unregister_dsp);6970static const struct snd_pcm_hardware sst_platform_pcm_hw = {71.info = (SNDRV_PCM_INFO_INTERLEAVED |72SNDRV_PCM_INFO_DOUBLE |73SNDRV_PCM_INFO_PAUSE |74SNDRV_PCM_INFO_RESUME |75SNDRV_PCM_INFO_MMAP|76SNDRV_PCM_INFO_MMAP_VALID |77SNDRV_PCM_INFO_BLOCK_TRANSFER |78SNDRV_PCM_INFO_SYNC_START),79.buffer_bytes_max = SST_MAX_BUFFER,80.period_bytes_min = SST_MIN_PERIOD_BYTES,81.period_bytes_max = SST_MAX_PERIOD_BYTES,82.periods_min = SST_MIN_PERIODS,83.periods_max = SST_MAX_PERIODS,84.fifo_size = SST_FIFO_SIZE,85};8687static struct sst_dev_stream_map dpcm_strm_map[] = {88{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */89{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},90{MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},91{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},92{MERR_DPCM_DEEP_BUFFER, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA3_IN, SST_TASK_ID_MEDIA, 0},93};9495static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)96{9798return sst_send_pipe_gains(dai, stream, mute);99}100101/* helper functions */102void sst_set_stream_status(struct sst_runtime_stream *stream,103int state)104{105unsigned long flags;106spin_lock_irqsave(&stream->status_lock, flags);107stream->stream_status = state;108spin_unlock_irqrestore(&stream->status_lock, flags);109}110111static inline int sst_get_stream_status(struct sst_runtime_stream *stream)112{113int state;114unsigned long flags;115116spin_lock_irqsave(&stream->status_lock, flags);117state = stream->stream_status;118spin_unlock_irqrestore(&stream->status_lock, flags);119return state;120}121122static void sst_fill_alloc_params(struct snd_pcm_substream *substream,123struct snd_sst_alloc_params_ext *alloc_param)124{125unsigned int channels;126snd_pcm_uframes_t period_size;127ssize_t periodbytes;128ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);129u32 buffer_addr = substream->runtime->dma_addr;130131channels = substream->runtime->channels;132period_size = substream->runtime->period_size;133periodbytes = samples_to_bytes(substream->runtime, period_size);134alloc_param->ring_buf_info[0].addr = buffer_addr;135alloc_param->ring_buf_info[0].size = buffer_bytes;136alloc_param->sg_count = 1;137alloc_param->reserved = 0;138alloc_param->frag_size = periodbytes * channels;139140}141static void sst_fill_pcm_params(struct snd_pcm_substream *substream,142struct snd_sst_stream_params *param)143{144param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;145param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;146param->uc.pcm_params.sfreq = substream->runtime->rate;147148/* PCM stream via ALSA interface */149param->uc.pcm_params.use_offload_path = 0;150param->uc.pcm_params.reserved2 = 0;151memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));152153}154155static int sst_get_stream_mapping(int dev, int sdev, int dir,156struct sst_dev_stream_map *map, int size)157{158int i;159160if (map == NULL)161return -EINVAL;162163164/* index 0 is not used in stream map */165for (i = 1; i < size; i++) {166if ((map[i].dev_num == dev) && (map[i].direction == dir))167return i;168}169return 0;170}171172int sst_fill_stream_params(void *substream,173const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress)174{175int map_size;176int index;177struct sst_dev_stream_map *map;178struct snd_pcm_substream *pstream = NULL;179struct snd_compr_stream *cstream = NULL;180181map = ctx->pdata->pdev_strm_map;182map_size = ctx->pdata->strm_map_size;183184if (is_compress)185cstream = (struct snd_compr_stream *)substream;186else187pstream = (struct snd_pcm_substream *)substream;188189str_params->stream_type = SST_STREAM_TYPE_MUSIC;190191/* For pcm streams */192if (pstream) {193index = sst_get_stream_mapping(pstream->pcm->device,194pstream->number, pstream->stream,195map, map_size);196if (index <= 0)197return -EINVAL;198199str_params->stream_id = index;200str_params->device_type = map[index].device_id;201str_params->task = map[index].task_id;202203str_params->ops = (u8)pstream->stream;204}205206if (cstream) {207index = sst_get_stream_mapping(cstream->device->device,2080, cstream->direction,209map, map_size);210if (index <= 0)211return -EINVAL;212str_params->stream_id = index;213str_params->device_type = map[index].device_id;214str_params->task = map[index].task_id;215216str_params->ops = (u8)cstream->direction;217}218return 0;219}220221static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,222struct snd_soc_dai *dai)223{224struct sst_runtime_stream *stream =225substream->runtime->private_data;226struct snd_sst_stream_params param = {{{0,},},};227struct snd_sst_params str_params = {0};228struct snd_sst_alloc_params_ext alloc_params = {0};229int ret_val = 0;230struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);231232/* set codec params and inform SST driver the same */233sst_fill_pcm_params(substream, ¶m);234sst_fill_alloc_params(substream, &alloc_params);235str_params.sparams = param;236str_params.aparams = alloc_params;237str_params.codec = SST_CODEC_TYPE_PCM;238239/* fill the device type and stream id to pass to SST driver */240ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);241if (ret_val < 0)242return ret_val;243244stream->stream_info.str_id = str_params.stream_id;245246ret_val = stream->ops->open(sst->dev, &str_params);247if (ret_val <= 0)248return ret_val;249250251return ret_val;252}253254static void sst_period_elapsed(void *arg)255{256struct snd_pcm_substream *substream = arg;257struct sst_runtime_stream *stream;258int status;259260if (!substream || !substream->runtime)261return;262stream = substream->runtime->private_data;263if (!stream)264return;265status = sst_get_stream_status(stream);266if (status != SST_PLATFORM_RUNNING)267return;268snd_pcm_period_elapsed(substream);269}270271static int sst_platform_init_stream(struct snd_pcm_substream *substream)272{273struct sst_runtime_stream *stream =274substream->runtime->private_data;275struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);276int ret_val;277278dev_dbg(rtd->dev, "setting buffer ptr param\n");279sst_set_stream_status(stream, SST_PLATFORM_INIT);280stream->stream_info.period_elapsed = sst_period_elapsed;281stream->stream_info.arg = substream;282stream->stream_info.buffer_ptr = 0;283stream->stream_info.sfreq = substream->runtime->rate;284ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info);285if (ret_val)286dev_err(rtd->dev, "control_set ret error %d\n", ret_val);287return ret_val;288289}290291static int power_up_sst(struct sst_runtime_stream *stream)292{293return stream->ops->power(sst->dev, true);294}295296static void power_down_sst(struct sst_runtime_stream *stream)297{298stream->ops->power(sst->dev, false);299}300301static int sst_media_open(struct snd_pcm_substream *substream,302struct snd_soc_dai *dai)303{304int ret_val = 0;305struct snd_pcm_runtime *runtime = substream->runtime;306struct sst_runtime_stream *stream;307308stream = kzalloc(sizeof(*stream), GFP_KERNEL);309if (!stream)310return -ENOMEM;311spin_lock_init(&stream->status_lock);312313/* get the sst ops */314mutex_lock(&sst_lock);315if (!sst ||316!try_module_get(sst->dev->driver->owner)) {317dev_err(dai->dev, "no device available to run\n");318ret_val = -ENODEV;319goto out_ops;320}321stream->ops = sst->ops;322mutex_unlock(&sst_lock);323324stream->stream_info.str_id = 0;325326stream->stream_info.arg = substream;327/* allocate memory for SST API set */328runtime->private_data = stream;329330ret_val = power_up_sst(stream);331if (ret_val < 0)332goto out_power_up;333334/*335* Make sure the period to be multiple of 1ms to align the336* design of firmware. Apply same rule to buffer size to make337* sure alsa could always find a value for period size338* regardless the buffer size given by user space.339*/340snd_pcm_hw_constraint_step(substream->runtime, 0,341SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 48);342snd_pcm_hw_constraint_step(substream->runtime, 0,343SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 48);344345/* Make sure, that the period size is always even */346snd_pcm_hw_constraint_step(substream->runtime, 0,347SNDRV_PCM_HW_PARAM_PERIODS, 2);348349return snd_pcm_hw_constraint_integer(runtime,350SNDRV_PCM_HW_PARAM_PERIODS);351out_ops:352mutex_unlock(&sst_lock);353out_power_up:354kfree(stream);355return ret_val;356}357358static void sst_media_close(struct snd_pcm_substream *substream,359struct snd_soc_dai *dai)360{361struct sst_runtime_stream *stream;362int str_id;363364stream = substream->runtime->private_data;365power_down_sst(stream);366367str_id = stream->stream_info.str_id;368if (str_id)369stream->ops->close(sst->dev, str_id);370module_put(sst->dev->driver->owner);371kfree(stream);372}373374static int sst_media_prepare(struct snd_pcm_substream *substream,375struct snd_soc_dai *dai)376{377struct sst_runtime_stream *stream;378int ret_val, str_id;379380stream = substream->runtime->private_data;381str_id = stream->stream_info.str_id;382if (stream->stream_info.str_id) {383ret_val = stream->ops->stream_drop(sst->dev, str_id);384return ret_val;385}386387ret_val = sst_platform_alloc_stream(substream, dai);388if (ret_val <= 0)389return ret_val;390snprintf(substream->pcm->id, sizeof(substream->pcm->id),391"%d", stream->stream_info.str_id);392393ret_val = sst_platform_init_stream(substream);394if (ret_val)395return ret_val;396substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;397return 0;398}399400static int sst_enable_ssp(struct snd_pcm_substream *substream,401struct snd_soc_dai *dai)402{403int ret = 0;404405if (!snd_soc_dai_active(dai)) {406ret = sst_handle_vb_timer(dai, true);407sst_fill_ssp_defaults(dai);408}409return ret;410}411412static int sst_be_hw_params(struct snd_pcm_substream *substream,413struct snd_pcm_hw_params *params,414struct snd_soc_dai *dai)415{416int ret = 0;417418if (snd_soc_dai_active(dai) == 1)419ret = send_ssp_cmd(dai, dai->name, 1);420return ret;421}422423static int sst_set_format(struct snd_soc_dai *dai, unsigned int fmt)424{425int ret = 0;426427if (!snd_soc_dai_active(dai))428return 0;429430ret = sst_fill_ssp_config(dai, fmt);431if (ret < 0)432dev_err(dai->dev, "sst_set_format failed..\n");433434return ret;435}436437static int sst_platform_set_ssp_slot(struct snd_soc_dai *dai,438unsigned int tx_mask, unsigned int rx_mask,439int slots, int slot_width) {440int ret = 0;441442if (!snd_soc_dai_active(dai))443return ret;444445ret = sst_fill_ssp_slot(dai, tx_mask, rx_mask, slots, slot_width);446if (ret < 0)447dev_err(dai->dev, "sst_fill_ssp_slot failed..%d\n", ret);448449return ret;450}451452static void sst_disable_ssp(struct snd_pcm_substream *substream,453struct snd_soc_dai *dai)454{455if (!snd_soc_dai_active(dai)) {456send_ssp_cmd(dai, dai->name, 0);457sst_handle_vb_timer(dai, false);458}459}460461static const struct snd_soc_dai_ops sst_media_dai_ops = {462.startup = sst_media_open,463.shutdown = sst_media_close,464.prepare = sst_media_prepare,465.mute_stream = sst_media_digital_mute,466};467468static const struct snd_soc_dai_ops sst_compr_dai_ops = {469.compress_new = snd_soc_new_compress,470.mute_stream = sst_media_digital_mute,471};472473static const struct snd_soc_dai_ops sst_be_dai_ops = {474.startup = sst_enable_ssp,475.hw_params = sst_be_hw_params,476.set_fmt = sst_set_format,477.set_tdm_slot = sst_platform_set_ssp_slot,478.shutdown = sst_disable_ssp,479};480481static struct snd_soc_dai_driver sst_platform_dai[] = {482{483.name = "media-cpu-dai",484.ops = &sst_media_dai_ops,485.playback = {486.stream_name = "Headset Playback",487.channels_min = SST_STEREO,488.channels_max = SST_STEREO,489.rates = SNDRV_PCM_RATE_48000,490.formats = SNDRV_PCM_FMTBIT_S16_LE,491},492.capture = {493.stream_name = "Headset Capture",494.channels_min = 1,495.channels_max = 2,496.rates = SNDRV_PCM_RATE_48000,497.formats = SNDRV_PCM_FMTBIT_S16_LE,498},499},500{501.name = "deepbuffer-cpu-dai",502.ops = &sst_media_dai_ops,503.playback = {504.stream_name = "Deepbuffer Playback",505.channels_min = SST_STEREO,506.channels_max = SST_STEREO,507.rates = SNDRV_PCM_RATE_48000,508.formats = SNDRV_PCM_FMTBIT_S16_LE,509},510},511{512.name = "compress-cpu-dai",513.ops = &sst_compr_dai_ops,514.playback = {515.stream_name = "Compress Playback",516.channels_min = 1,517},518},519/* BE CPU Dais */520{521.name = "ssp0-port",522.ops = &sst_be_dai_ops,523.playback = {524.stream_name = "ssp0 Tx",525.channels_min = SST_STEREO,526.channels_max = SST_STEREO,527.rates = SNDRV_PCM_RATE_48000,528.formats = SNDRV_PCM_FMTBIT_S16_LE,529},530.capture = {531.stream_name = "ssp0 Rx",532.channels_min = SST_STEREO,533.channels_max = SST_STEREO,534.rates = SNDRV_PCM_RATE_48000,535.formats = SNDRV_PCM_FMTBIT_S16_LE,536},537},538{539.name = "ssp1-port",540.ops = &sst_be_dai_ops,541.playback = {542.stream_name = "ssp1 Tx",543.channels_min = SST_STEREO,544.channels_max = SST_STEREO,545.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,546.formats = SNDRV_PCM_FMTBIT_S16_LE,547},548.capture = {549.stream_name = "ssp1 Rx",550.channels_min = SST_STEREO,551.channels_max = SST_STEREO,552.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,553.formats = SNDRV_PCM_FMTBIT_S16_LE,554},555},556{557.name = "ssp2-port",558.ops = &sst_be_dai_ops,559.playback = {560.stream_name = "ssp2 Tx",561.channels_min = SST_STEREO,562.channels_max = SST_STEREO,563.rates = SNDRV_PCM_RATE_48000,564.formats = SNDRV_PCM_FMTBIT_S16_LE,565},566.capture = {567.stream_name = "ssp2 Rx",568.channels_min = SST_STEREO,569.channels_max = SST_STEREO,570.rates = SNDRV_PCM_RATE_48000,571.formats = SNDRV_PCM_FMTBIT_S16_LE,572},573},574};575576static int sst_soc_open(struct snd_soc_component *component,577struct snd_pcm_substream *substream)578{579struct snd_pcm_runtime *runtime;580581if (substream->pcm->internal)582return 0;583584runtime = substream->runtime;585runtime->hw = sst_platform_pcm_hw;586return 0;587}588589static int sst_soc_trigger(struct snd_soc_component *component,590struct snd_pcm_substream *substream, int cmd)591{592int ret_val = 0, str_id;593struct sst_runtime_stream *stream;594int status;595struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);596597dev_dbg(rtd->dev, "%s called\n", __func__);598if (substream->pcm->internal)599return 0;600stream = substream->runtime->private_data;601str_id = stream->stream_info.str_id;602switch (cmd) {603case SNDRV_PCM_TRIGGER_START:604dev_dbg(rtd->dev, "sst: Trigger Start\n");605status = SST_PLATFORM_RUNNING;606stream->stream_info.arg = substream;607ret_val = stream->ops->stream_start(sst->dev, str_id);608break;609case SNDRV_PCM_TRIGGER_STOP:610dev_dbg(rtd->dev, "sst: in stop\n");611status = SST_PLATFORM_DROPPED;612ret_val = stream->ops->stream_drop(sst->dev, str_id);613break;614case SNDRV_PCM_TRIGGER_PAUSE_PUSH:615case SNDRV_PCM_TRIGGER_SUSPEND:616dev_dbg(rtd->dev, "sst: in pause\n");617status = SST_PLATFORM_PAUSED;618ret_val = stream->ops->stream_pause(sst->dev, str_id);619break;620case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:621case SNDRV_PCM_TRIGGER_RESUME:622dev_dbg(rtd->dev, "sst: in pause release\n");623status = SST_PLATFORM_RUNNING;624ret_val = stream->ops->stream_pause_release(sst->dev, str_id);625break;626default:627return -EINVAL;628}629630if (!ret_val)631sst_set_stream_status(stream, status);632633return ret_val;634}635636637static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component,638struct snd_pcm_substream *substream)639{640struct sst_runtime_stream *stream;641int ret_val, status;642struct pcm_stream_info *str_info;643struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);644645stream = substream->runtime->private_data;646status = sst_get_stream_status(stream);647if (status == SST_PLATFORM_INIT)648return 0;649str_info = &stream->stream_info;650ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info);651if (ret_val) {652dev_err(rtd->dev, "sst: error code = %d\n", ret_val);653return ret_val;654}655return str_info->buffer_ptr;656}657658static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component,659struct snd_pcm_substream *substream)660{661struct sst_runtime_stream *stream = substream->runtime->private_data;662struct pcm_stream_info *str_info = &stream->stream_info;663664if (sst_get_stream_status(stream) == SST_PLATFORM_INIT)665return 0;666667return str_info->pcm_delay;668}669670static int sst_soc_pcm_new(struct snd_soc_component *component,671struct snd_soc_pcm_runtime *rtd)672{673struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);674struct snd_pcm *pcm = rtd->pcm;675676if (dai->driver->playback.channels_min ||677dai->driver->capture.channels_min) {678snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,679pcm->card->dev,680SST_MIN_BUFFER, SST_MAX_BUFFER);681}682return 0;683}684685static int sst_soc_probe(struct snd_soc_component *component)686{687struct sst_data *drv = dev_get_drvdata(component->dev);688689drv->soc_card = component->card;690return sst_dsp_init_v2_dpcm(component);691}692693static void sst_soc_remove(struct snd_soc_component *component)694{695struct sst_data *drv = dev_get_drvdata(component->dev);696697drv->soc_card = NULL;698}699700static const struct snd_soc_component_driver sst_soc_platform_drv = {701.name = DRV_NAME,702.probe = sst_soc_probe,703.remove = sst_soc_remove,704.open = sst_soc_open,705.trigger = sst_soc_trigger,706.pointer = sst_soc_pointer,707.delay = sst_soc_delay,708.compress_ops = &sst_platform_compress_ops,709.pcm_construct = sst_soc_pcm_new,710};711712static int sst_platform_probe(struct platform_device *pdev)713{714struct sst_data *drv;715int ret;716struct sst_platform_data *pdata;717718drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);719if (drv == NULL) {720return -ENOMEM;721}722723pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);724if (pdata == NULL) {725return -ENOMEM;726}727728pdata->pdev_strm_map = dpcm_strm_map;729pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);730drv->pdata = pdata;731drv->pdev = pdev;732mutex_init(&drv->lock);733dev_set_drvdata(&pdev->dev, drv);734735ret = devm_snd_soc_register_component(&pdev->dev, &sst_soc_platform_drv,736sst_platform_dai, ARRAY_SIZE(sst_platform_dai));737if (ret)738dev_err(&pdev->dev, "registering cpu dais failed\n");739740return ret;741}742743static void sst_platform_remove(struct platform_device *pdev)744{745dev_dbg(&pdev->dev, "sst_platform_remove success\n");746}747748#ifdef CONFIG_PM_SLEEP749750static int sst_soc_prepare(struct device *dev)751{752struct sst_data *drv = dev_get_drvdata(dev);753struct snd_soc_pcm_runtime *rtd;754755if (!drv->soc_card)756return 0;757758/* suspend all pcms first */759snd_soc_suspend(drv->soc_card->dev);760snd_soc_poweroff(drv->soc_card->dev);761762/* set the SSPs to idle */763for_each_card_rtds(drv->soc_card, rtd) {764struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);765766if (snd_soc_dai_active(dai)) {767send_ssp_cmd(dai, dai->name, 0);768sst_handle_vb_timer(dai, false);769}770}771772return 0;773}774775static void sst_soc_complete(struct device *dev)776{777struct sst_data *drv = dev_get_drvdata(dev);778struct snd_soc_pcm_runtime *rtd;779780if (!drv->soc_card)781return;782783/* restart SSPs */784for_each_card_rtds(drv->soc_card, rtd) {785struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);786787if (snd_soc_dai_active(dai)) {788sst_handle_vb_timer(dai, true);789send_ssp_cmd(dai, dai->name, 1);790}791}792snd_soc_resume(drv->soc_card->dev);793}794795#else796797#define sst_soc_prepare NULL798#define sst_soc_complete NULL799800#endif801802803static const struct dev_pm_ops sst_platform_pm = {804.prepare = sst_soc_prepare,805.complete = sst_soc_complete,806};807808static struct platform_driver sst_platform_driver = {809.driver = {810.name = "sst-mfld-platform",811.pm = &sst_platform_pm,812},813.probe = sst_platform_probe,814.remove = sst_platform_remove,815};816817module_platform_driver(sst_platform_driver);818819MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");820MODULE_AUTHOR("Vinod Koul <[email protected]>");821MODULE_AUTHOR("Harsha Priya <[email protected]>");822MODULE_LICENSE("GPL v2");823MODULE_ALIAS("platform:sst-atom-hifi2-platform");824MODULE_ALIAS("platform:sst-mfld-platform");825826827