diff options
Diffstat (limited to 'sound/soc/intel/sst-haswell-pcm.c')
-rw-r--r-- | sound/soc/intel/sst-haswell-pcm.c | 419 |
1 files changed, 346 insertions, 73 deletions
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 4df867cbb92a..0180b386c421 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/dma-mapping.h> | 18 | #include <linux/dma-mapping.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/pm_runtime.h> | ||
21 | #include <asm/page.h> | 22 | #include <asm/page.h> |
22 | #include <asm/pgtable.h> | 23 | #include <asm/pgtable.h> |
23 | #include <sound/core.h> | 24 | #include <sound/core.h> |
@@ -73,6 +74,13 @@ static const u32 volume_map[] = { | |||
73 | #define HSW_PCM_PERIODS_MAX 64 | 74 | #define HSW_PCM_PERIODS_MAX 64 |
74 | #define HSW_PCM_PERIODS_MIN 2 | 75 | #define HSW_PCM_PERIODS_MIN 2 |
75 | 76 | ||
77 | #define HSW_PCM_DAI_ID_SYSTEM 0 | ||
78 | #define HSW_PCM_DAI_ID_OFFLOAD0 1 | ||
79 | #define HSW_PCM_DAI_ID_OFFLOAD1 2 | ||
80 | #define HSW_PCM_DAI_ID_LOOPBACK 3 | ||
81 | #define HSW_PCM_DAI_ID_CAPTURE 4 | ||
82 | |||
83 | |||
76 | static const struct snd_pcm_hardware hsw_pcm_hardware = { | 84 | static const struct snd_pcm_hardware hsw_pcm_hardware = { |
77 | .info = SNDRV_PCM_INFO_MMAP | | 85 | .info = SNDRV_PCM_INFO_MMAP | |
78 | SNDRV_PCM_INFO_MMAP_VALID | | 86 | SNDRV_PCM_INFO_MMAP_VALID | |
@@ -89,22 +97,39 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = { | |||
89 | .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE, | 97 | .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE, |
90 | }; | 98 | }; |
91 | 99 | ||
100 | struct hsw_pcm_module_map { | ||
101 | int dai_id; | ||
102 | enum sst_hsw_module_id mod_id; | ||
103 | }; | ||
104 | |||
92 | /* private data for each PCM DSP stream */ | 105 | /* private data for each PCM DSP stream */ |
93 | struct hsw_pcm_data { | 106 | struct hsw_pcm_data { |
94 | int dai_id; | 107 | int dai_id; |
95 | struct sst_hsw_stream *stream; | 108 | struct sst_hsw_stream *stream; |
109 | struct sst_module_runtime *runtime; | ||
110 | struct sst_module_runtime_context context; | ||
111 | struct snd_pcm *hsw_pcm; | ||
96 | u32 volume[2]; | 112 | u32 volume[2]; |
97 | struct snd_pcm_substream *substream; | 113 | struct snd_pcm_substream *substream; |
98 | struct snd_compr_stream *cstream; | 114 | struct snd_compr_stream *cstream; |
99 | unsigned int wpos; | 115 | unsigned int wpos; |
100 | struct mutex mutex; | 116 | struct mutex mutex; |
101 | bool allocated; | 117 | bool allocated; |
118 | int persistent_offset; | ||
119 | }; | ||
120 | |||
121 | enum hsw_pm_state { | ||
122 | HSW_PM_STATE_D3 = 0, | ||
123 | HSW_PM_STATE_D0 = 1, | ||
102 | }; | 124 | }; |
103 | 125 | ||
104 | /* private data for the driver */ | 126 | /* private data for the driver */ |
105 | struct hsw_priv_data { | 127 | struct hsw_priv_data { |
106 | /* runtime DSP */ | 128 | /* runtime DSP */ |
107 | struct sst_hsw *hsw; | 129 | struct sst_hsw *hsw; |
130 | struct device *dev; | ||
131 | enum hsw_pm_state pm_state; | ||
132 | struct snd_soc_card *soc_card; | ||
108 | 133 | ||
109 | /* page tables */ | 134 | /* page tables */ |
110 | struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; | 135 | struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; |
@@ -138,21 +163,25 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value) | |||
138 | static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, | 163 | static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, |
139 | struct snd_ctl_elem_value *ucontrol) | 164 | struct snd_ctl_elem_value *ucontrol) |
140 | { | 165 | { |
141 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | 166 | struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); |
142 | struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); | ||
143 | struct soc_mixer_control *mc = | 167 | struct soc_mixer_control *mc = |
144 | (struct soc_mixer_control *)kcontrol->private_value; | 168 | (struct soc_mixer_control *)kcontrol->private_value; |
169 | struct hsw_priv_data *pdata = | ||
170 | snd_soc_platform_get_drvdata(platform); | ||
145 | struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; | 171 | struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; |
146 | struct sst_hsw *hsw = pdata->hsw; | 172 | struct sst_hsw *hsw = pdata->hsw; |
147 | u32 volume; | 173 | u32 volume; |
148 | 174 | ||
149 | mutex_lock(&pcm_data->mutex); | 175 | mutex_lock(&pcm_data->mutex); |
176 | pm_runtime_get_sync(pdata->dev); | ||
150 | 177 | ||
151 | if (!pcm_data->stream) { | 178 | if (!pcm_data->stream) { |
152 | pcm_data->volume[0] = | 179 | pcm_data->volume[0] = |
153 | hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); | 180 | hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); |
154 | pcm_data->volume[1] = | 181 | pcm_data->volume[1] = |
155 | hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); | 182 | hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); |
183 | pm_runtime_mark_last_busy(pdata->dev); | ||
184 | pm_runtime_put_autosuspend(pdata->dev); | ||
156 | mutex_unlock(&pcm_data->mutex); | 185 | mutex_unlock(&pcm_data->mutex); |
157 | return 0; | 186 | return 0; |
158 | } | 187 | } |
@@ -160,7 +189,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, | |||
160 | if (ucontrol->value.integer.value[0] == | 189 | if (ucontrol->value.integer.value[0] == |
161 | ucontrol->value.integer.value[1]) { | 190 | ucontrol->value.integer.value[1]) { |
162 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); | 191 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); |
163 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume); | 192 | /* apply volume value to all channels */ |
193 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume); | ||
164 | } else { | 194 | } else { |
165 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); | 195 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); |
166 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume); | 196 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume); |
@@ -168,6 +198,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, | |||
168 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume); | 198 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume); |
169 | } | 199 | } |
170 | 200 | ||
201 | pm_runtime_mark_last_busy(pdata->dev); | ||
202 | pm_runtime_put_autosuspend(pdata->dev); | ||
171 | mutex_unlock(&pcm_data->mutex); | 203 | mutex_unlock(&pcm_data->mutex); |
172 | return 0; | 204 | return 0; |
173 | } | 205 | } |
@@ -175,21 +207,25 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, | |||
175 | static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, | 207 | static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, |
176 | struct snd_ctl_elem_value *ucontrol) | 208 | struct snd_ctl_elem_value *ucontrol) |
177 | { | 209 | { |
178 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | 210 | struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); |
179 | struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); | ||
180 | struct soc_mixer_control *mc = | 211 | struct soc_mixer_control *mc = |
181 | (struct soc_mixer_control *)kcontrol->private_value; | 212 | (struct soc_mixer_control *)kcontrol->private_value; |
213 | struct hsw_priv_data *pdata = | ||
214 | snd_soc_platform_get_drvdata(platform); | ||
182 | struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; | 215 | struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; |
183 | struct sst_hsw *hsw = pdata->hsw; | 216 | struct sst_hsw *hsw = pdata->hsw; |
184 | u32 volume; | 217 | u32 volume; |
185 | 218 | ||
186 | mutex_lock(&pcm_data->mutex); | 219 | mutex_lock(&pcm_data->mutex); |
220 | pm_runtime_get_sync(pdata->dev); | ||
187 | 221 | ||
188 | if (!pcm_data->stream) { | 222 | if (!pcm_data->stream) { |
189 | ucontrol->value.integer.value[0] = | 223 | ucontrol->value.integer.value[0] = |
190 | hsw_ipc_to_mixer(pcm_data->volume[0]); | 224 | hsw_ipc_to_mixer(pcm_data->volume[0]); |
191 | ucontrol->value.integer.value[1] = | 225 | ucontrol->value.integer.value[1] = |
192 | hsw_ipc_to_mixer(pcm_data->volume[1]); | 226 | hsw_ipc_to_mixer(pcm_data->volume[1]); |
227 | pm_runtime_mark_last_busy(pdata->dev); | ||
228 | pm_runtime_put_autosuspend(pdata->dev); | ||
193 | mutex_unlock(&pcm_data->mutex); | 229 | mutex_unlock(&pcm_data->mutex); |
194 | return 0; | 230 | return 0; |
195 | } | 231 | } |
@@ -198,6 +234,9 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, | |||
198 | ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); | 234 | ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); |
199 | sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume); | 235 | sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume); |
200 | ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); | 236 | ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); |
237 | |||
238 | pm_runtime_mark_last_busy(pdata->dev); | ||
239 | pm_runtime_put_autosuspend(pdata->dev); | ||
201 | mutex_unlock(&pcm_data->mutex); | 240 | mutex_unlock(&pcm_data->mutex); |
202 | 241 | ||
203 | return 0; | 242 | return 0; |
@@ -206,16 +245,18 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, | |||
206 | static int hsw_volume_put(struct snd_kcontrol *kcontrol, | 245 | static int hsw_volume_put(struct snd_kcontrol *kcontrol, |
207 | struct snd_ctl_elem_value *ucontrol) | 246 | struct snd_ctl_elem_value *ucontrol) |
208 | { | 247 | { |
209 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | 248 | struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); |
210 | struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); | 249 | struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); |
211 | struct sst_hsw *hsw = pdata->hsw; | 250 | struct sst_hsw *hsw = pdata->hsw; |
212 | u32 volume; | 251 | u32 volume; |
213 | 252 | ||
253 | pm_runtime_get_sync(pdata->dev); | ||
254 | |||
214 | if (ucontrol->value.integer.value[0] == | 255 | if (ucontrol->value.integer.value[0] == |
215 | ucontrol->value.integer.value[1]) { | 256 | ucontrol->value.integer.value[1]) { |
216 | 257 | ||
217 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); | 258 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); |
218 | sst_hsw_mixer_set_volume(hsw, 0, 2, volume); | 259 | sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume); |
219 | 260 | ||
220 | } else { | 261 | } else { |
221 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); | 262 | volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); |
@@ -225,23 +266,28 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol, | |||
225 | sst_hsw_mixer_set_volume(hsw, 0, 1, volume); | 266 | sst_hsw_mixer_set_volume(hsw, 0, 1, volume); |
226 | } | 267 | } |
227 | 268 | ||
269 | pm_runtime_mark_last_busy(pdata->dev); | ||
270 | pm_runtime_put_autosuspend(pdata->dev); | ||
228 | return 0; | 271 | return 0; |
229 | } | 272 | } |
230 | 273 | ||
231 | static int hsw_volume_get(struct snd_kcontrol *kcontrol, | 274 | static int hsw_volume_get(struct snd_kcontrol *kcontrol, |
232 | struct snd_ctl_elem_value *ucontrol) | 275 | struct snd_ctl_elem_value *ucontrol) |
233 | { | 276 | { |
234 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | 277 | struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); |
235 | struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); | 278 | struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); |
236 | struct sst_hsw *hsw = pdata->hsw; | 279 | struct sst_hsw *hsw = pdata->hsw; |
237 | unsigned int volume = 0; | 280 | unsigned int volume = 0; |
238 | 281 | ||
282 | pm_runtime_get_sync(pdata->dev); | ||
239 | sst_hsw_mixer_get_volume(hsw, 0, 0, &volume); | 283 | sst_hsw_mixer_get_volume(hsw, 0, 0, &volume); |
240 | ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); | 284 | ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); |
241 | 285 | ||
242 | sst_hsw_mixer_get_volume(hsw, 0, 1, &volume); | 286 | sst_hsw_mixer_get_volume(hsw, 0, 1, &volume); |
243 | ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); | 287 | ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); |
244 | 288 | ||
289 | pm_runtime_mark_last_busy(pdata->dev); | ||
290 | pm_runtime_put_autosuspend(pdata->dev); | ||
245 | return 0; | 291 | return 0; |
246 | } | 292 | } |
247 | 293 | ||
@@ -252,23 +298,19 @@ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1); | |||
252 | static const struct snd_kcontrol_new hsw_volume_controls[] = { | 298 | static const struct snd_kcontrol_new hsw_volume_controls[] = { |
253 | /* Global DSP volume */ | 299 | /* Global DSP volume */ |
254 | SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8, | 300 | SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8, |
255 | ARRAY_SIZE(volume_map) -1, 0, | 301 | ARRAY_SIZE(volume_map) - 1, 0, |
256 | hsw_volume_get, hsw_volume_put, hsw_vol_tlv), | 302 | hsw_volume_get, hsw_volume_put, hsw_vol_tlv), |
257 | /* Offload 0 volume */ | 303 | /* Offload 0 volume */ |
258 | SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8, | 304 | SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8, |
259 | ARRAY_SIZE(volume_map), 0, | 305 | ARRAY_SIZE(volume_map) - 1, 0, |
260 | hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), | 306 | hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), |
261 | /* Offload 1 volume */ | 307 | /* Offload 1 volume */ |
262 | SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8, | 308 | SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8, |
263 | ARRAY_SIZE(volume_map), 0, | 309 | ARRAY_SIZE(volume_map) - 1, 0, |
264 | hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), | ||
265 | /* Loopback volume */ | ||
266 | SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8, | ||
267 | ARRAY_SIZE(volume_map), 0, | ||
268 | hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), | 310 | hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), |
269 | /* Mic Capture volume */ | 311 | /* Mic Capture volume */ |
270 | SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, | 312 | SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8, |
271 | ARRAY_SIZE(volume_map), 0, | 313 | ARRAY_SIZE(volume_map) - 1, 0, |
272 | hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), | 314 | hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), |
273 | }; | 315 | }; |
274 | 316 | ||
@@ -354,8 +396,14 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, | |||
354 | /* DSP stream type depends on DAI ID */ | 396 | /* DSP stream type depends on DAI ID */ |
355 | switch (rtd->cpu_dai->id) { | 397 | switch (rtd->cpu_dai->id) { |
356 | case 0: | 398 | case 0: |
357 | stream_type = SST_HSW_STREAM_TYPE_SYSTEM; | 399 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
358 | module_id = SST_HSW_MODULE_PCM_SYSTEM; | 400 | stream_type = SST_HSW_STREAM_TYPE_SYSTEM; |
401 | module_id = SST_HSW_MODULE_PCM_SYSTEM; | ||
402 | } | ||
403 | else { | ||
404 | stream_type = SST_HSW_STREAM_TYPE_CAPTURE; | ||
405 | module_id = SST_HSW_MODULE_PCM_CAPTURE; | ||
406 | } | ||
359 | break; | 407 | break; |
360 | case 1: | 408 | case 1: |
361 | case 2: | 409 | case 2: |
@@ -368,10 +416,6 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, | |||
368 | path_id = SST_HSW_STREAM_PATH_SSP0_OUT; | 416 | path_id = SST_HSW_STREAM_PATH_SSP0_OUT; |
369 | module_id = SST_HSW_MODULE_PCM_REFERENCE; | 417 | module_id = SST_HSW_MODULE_PCM_REFERENCE; |
370 | break; | 418 | break; |
371 | case 4: | ||
372 | stream_type = SST_HSW_STREAM_TYPE_CAPTURE; | ||
373 | module_id = SST_HSW_MODULE_PCM_CAPTURE; | ||
374 | break; | ||
375 | default: | 419 | default: |
376 | dev_err(rtd->dev, "error: invalid DAI ID %d\n", | 420 | dev_err(rtd->dev, "error: invalid DAI ID %d\n", |
377 | rtd->cpu_dai->id); | 421 | rtd->cpu_dai->id); |
@@ -421,13 +465,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, | |||
421 | return ret; | 465 | return ret; |
422 | } | 466 | } |
423 | 467 | ||
424 | /* we only support stereo atm */ | ||
425 | channels = params_channels(params); | 468 | channels = params_channels(params); |
426 | if (channels != 2) { | ||
427 | dev_err(rtd->dev, "error: invalid channels %d\n", channels); | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | |||
431 | map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO); | 469 | map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO); |
432 | sst_hsw_stream_set_map_config(hsw, pcm_data->stream, | 470 | sst_hsw_stream_set_map_config(hsw, pcm_data->stream, |
433 | map, SST_HSW_CHANNEL_CONFIG_STEREO); | 471 | map, SST_HSW_CHANNEL_CONFIG_STEREO); |
@@ -478,35 +516,23 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, | |||
478 | return -EINVAL; | 516 | return -EINVAL; |
479 | } | 517 | } |
480 | 518 | ||
481 | /* we use hardcoded memory offsets atm, will be updated for new FW */ | 519 | sst_hsw_stream_set_module_info(hsw, pcm_data->stream, |
482 | if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) { | 520 | pcm_data->runtime); |
483 | sst_hsw_stream_set_module_info(hsw, pcm_data->stream, | ||
484 | SST_HSW_MODULE_PCM_CAPTURE, module_data->entry); | ||
485 | sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, | ||
486 | 0x449400, 0x4000); | ||
487 | sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, | ||
488 | 0x400000, 0); | ||
489 | } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */ | ||
490 | sst_hsw_stream_set_module_info(hsw, pcm_data->stream, | ||
491 | SST_HSW_MODULE_PCM_SYSTEM, module_data->entry); | ||
492 | |||
493 | sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, | ||
494 | module_data->offset, module_data->size); | ||
495 | sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, | ||
496 | 0x44d400, 0x3800); | ||
497 | |||
498 | sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, | ||
499 | module_data->offset, module_data->size); | ||
500 | sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, | ||
501 | 0x400000, 0); | ||
502 | } | ||
503 | 521 | ||
504 | ret = sst_hsw_stream_commit(hsw, pcm_data->stream); | 522 | ret = sst_hsw_stream_commit(hsw, pcm_data->stream); |
505 | if (ret < 0) { | 523 | if (ret < 0) { |
506 | dev_err(rtd->dev, "error: failed to commit stream %d\n", ret); | 524 | dev_err(rtd->dev, "error: failed to commit stream %d\n", ret); |
507 | return ret; | 525 | return ret; |
508 | } | 526 | } |
509 | pcm_data->allocated = true; | 527 | |
528 | if (!pcm_data->allocated) { | ||
529 | /* Set previous saved volume */ | ||
530 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, | ||
531 | 0, pcm_data->volume[0]); | ||
532 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, | ||
533 | 1, pcm_data->volume[1]); | ||
534 | pcm_data->allocated = true; | ||
535 | } | ||
510 | 536 | ||
511 | ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1); | 537 | ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1); |
512 | if (ret < 0) | 538 | if (ret < 0) |
@@ -558,7 +584,7 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data) | |||
558 | pos = frames_to_bytes(runtime, | 584 | pos = frames_to_bytes(runtime, |
559 | (runtime->control->appl_ptr % runtime->buffer_size)); | 585 | (runtime->control->appl_ptr % runtime->buffer_size)); |
560 | 586 | ||
561 | dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); | 587 | dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); |
562 | 588 | ||
563 | /* let alsa know we have play a period */ | 589 | /* let alsa know we have play a period */ |
564 | snd_pcm_period_elapsed(substream); | 590 | snd_pcm_period_elapsed(substream); |
@@ -580,7 +606,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream) | |||
580 | offset = bytes_to_frames(runtime, position); | 606 | offset = bytes_to_frames(runtime, position); |
581 | ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream); | 607 | ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream); |
582 | 608 | ||
583 | dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n", | 609 | dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n", |
584 | position, ppos); | 610 | position, ppos); |
585 | return offset; | 611 | return offset; |
586 | } | 612 | } |
@@ -596,6 +622,7 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) | |||
596 | pcm_data = &pdata->pcm[rtd->cpu_dai->id]; | 622 | pcm_data = &pdata->pcm[rtd->cpu_dai->id]; |
597 | 623 | ||
598 | mutex_lock(&pcm_data->mutex); | 624 | mutex_lock(&pcm_data->mutex); |
625 | pm_runtime_get_sync(pdata->dev); | ||
599 | 626 | ||
600 | snd_soc_pcm_set_drvdata(rtd, pcm_data); | 627 | snd_soc_pcm_set_drvdata(rtd, pcm_data); |
601 | pcm_data->substream = substream; | 628 | pcm_data->substream = substream; |
@@ -606,16 +633,12 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) | |||
606 | hsw_notify_pointer, pcm_data); | 633 | hsw_notify_pointer, pcm_data); |
607 | if (pcm_data->stream == NULL) { | 634 | if (pcm_data->stream == NULL) { |
608 | dev_err(rtd->dev, "error: failed to create stream\n"); | 635 | dev_err(rtd->dev, "error: failed to create stream\n"); |
636 | pm_runtime_mark_last_busy(pdata->dev); | ||
637 | pm_runtime_put_autosuspend(pdata->dev); | ||
609 | mutex_unlock(&pcm_data->mutex); | 638 | mutex_unlock(&pcm_data->mutex); |
610 | return -EINVAL; | 639 | return -EINVAL; |
611 | } | 640 | } |
612 | 641 | ||
613 | /* Set previous saved volume */ | ||
614 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, | ||
615 | 0, pcm_data->volume[0]); | ||
616 | sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, | ||
617 | 1, pcm_data->volume[1]); | ||
618 | |||
619 | mutex_unlock(&pcm_data->mutex); | 642 | mutex_unlock(&pcm_data->mutex); |
620 | return 0; | 643 | return 0; |
621 | } | 644 | } |
@@ -645,6 +668,8 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream) | |||
645 | pcm_data->stream = NULL; | 668 | pcm_data->stream = NULL; |
646 | 669 | ||
647 | out: | 670 | out: |
671 | pm_runtime_mark_last_busy(pdata->dev); | ||
672 | pm_runtime_put_autosuspend(pdata->dev); | ||
648 | mutex_unlock(&pcm_data->mutex); | 673 | mutex_unlock(&pcm_data->mutex); |
649 | return ret; | 674 | return ret; |
650 | } | 675 | } |
@@ -660,6 +685,56 @@ static struct snd_pcm_ops hsw_pcm_ops = { | |||
660 | .page = snd_pcm_sgbuf_ops_page, | 685 | .page = snd_pcm_sgbuf_ops_page, |
661 | }; | 686 | }; |
662 | 687 | ||
688 | /* static mappings between PCMs and modules - may be dynamic in future */ | ||
689 | static struct hsw_pcm_module_map mod_map[] = { | ||
690 | {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM}, | ||
691 | {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM}, | ||
692 | {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM}, | ||
693 | {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE}, | ||
694 | {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE}, | ||
695 | }; | ||
696 | |||
697 | static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) | ||
698 | { | ||
699 | struct sst_hsw *hsw = pdata->hsw; | ||
700 | struct hsw_pcm_data *pcm_data; | ||
701 | int i; | ||
702 | |||
703 | for (i = 0; i < ARRAY_SIZE(mod_map); i++) { | ||
704 | pcm_data = &pdata->pcm[i]; | ||
705 | |||
706 | /* create new runtime module, use same offset if recreated */ | ||
707 | pcm_data->runtime = sst_hsw_runtime_module_create(hsw, | ||
708 | mod_map[i].mod_id, pcm_data->persistent_offset); | ||
709 | if (pcm_data->runtime == NULL) | ||
710 | goto err; | ||
711 | pcm_data->persistent_offset = | ||
712 | pcm_data->runtime->persistent_offset; | ||
713 | } | ||
714 | |||
715 | return 0; | ||
716 | |||
717 | err: | ||
718 | for (--i; i >= 0; i--) { | ||
719 | pcm_data = &pdata->pcm[i]; | ||
720 | sst_hsw_runtime_module_free(pcm_data->runtime); | ||
721 | } | ||
722 | |||
723 | return -ENODEV; | ||
724 | } | ||
725 | |||
726 | static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) | ||
727 | { | ||
728 | struct hsw_pcm_data *pcm_data; | ||
729 | int i; | ||
730 | |||
731 | for (i = 0; i < ARRAY_SIZE(mod_map); i++) { | ||
732 | pcm_data = &pdata->pcm[i]; | ||
733 | |||
734 | sst_hsw_runtime_module_free(pcm_data->runtime); | ||
735 | } | ||
736 | } | ||
737 | |||
663 | static void hsw_pcm_free(struct snd_pcm *pcm) | 738 | static void hsw_pcm_free(struct snd_pcm *pcm) |
664 | { | 739 | { |
665 | snd_pcm_lib_preallocate_free_for_all(pcm); | 740 | snd_pcm_lib_preallocate_free_for_all(pcm); |
@@ -670,6 +745,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
670 | struct snd_pcm *pcm = rtd->pcm; | 745 | struct snd_pcm *pcm = rtd->pcm; |
671 | struct snd_soc_platform *platform = rtd->platform; | 746 | struct snd_soc_platform *platform = rtd->platform; |
672 | struct sst_pdata *pdata = dev_get_platdata(platform->dev); | 747 | struct sst_pdata *pdata = dev_get_platdata(platform->dev); |
748 | struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev); | ||
673 | struct device *dev = pdata->dma_dev; | 749 | struct device *dev = pdata->dma_dev; |
674 | int ret = 0; | 750 | int ret = 0; |
675 | 751 | ||
@@ -686,6 +762,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
686 | return ret; | 762 | return ret; |
687 | } | 763 | } |
688 | } | 764 | } |
765 | priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm; | ||
689 | 766 | ||
690 | return ret; | 767 | return ret; |
691 | } | 768 | } |
@@ -696,6 +773,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
696 | static struct snd_soc_dai_driver hsw_dais[] = { | 773 | static struct snd_soc_dai_driver hsw_dais[] = { |
697 | { | 774 | { |
698 | .name = "System Pin", | 775 | .name = "System Pin", |
776 | .id = HSW_PCM_DAI_ID_SYSTEM, | ||
699 | .playback = { | 777 | .playback = { |
700 | .stream_name = "System Playback", | 778 | .stream_name = "System Playback", |
701 | .channels_min = 2, | 779 | .channels_min = 2, |
@@ -703,10 +781,18 @@ static struct snd_soc_dai_driver hsw_dais[] = { | |||
703 | .rates = SNDRV_PCM_RATE_48000, | 781 | .rates = SNDRV_PCM_RATE_48000, |
704 | .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, | 782 | .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, |
705 | }, | 783 | }, |
784 | .capture = { | ||
785 | .stream_name = "Analog Capture", | ||
786 | .channels_min = 2, | ||
787 | .channels_max = 4, | ||
788 | .rates = SNDRV_PCM_RATE_48000, | ||
789 | .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, | ||
790 | }, | ||
706 | }, | 791 | }, |
707 | { | 792 | { |
708 | /* PCM */ | 793 | /* PCM */ |
709 | .name = "Offload0 Pin", | 794 | .name = "Offload0 Pin", |
795 | .id = HSW_PCM_DAI_ID_OFFLOAD0, | ||
710 | .playback = { | 796 | .playback = { |
711 | .stream_name = "Offload0 Playback", | 797 | .stream_name = "Offload0 Playback", |
712 | .channels_min = 2, | 798 | .channels_min = 2, |
@@ -718,6 +804,7 @@ static struct snd_soc_dai_driver hsw_dais[] = { | |||
718 | { | 804 | { |
719 | /* PCM */ | 805 | /* PCM */ |
720 | .name = "Offload1 Pin", | 806 | .name = "Offload1 Pin", |
807 | .id = HSW_PCM_DAI_ID_OFFLOAD1, | ||
721 | .playback = { | 808 | .playback = { |
722 | .stream_name = "Offload1 Playback", | 809 | .stream_name = "Offload1 Playback", |
723 | .channels_min = 2, | 810 | .channels_min = 2, |
@@ -728,6 +815,7 @@ static struct snd_soc_dai_driver hsw_dais[] = { | |||
728 | }, | 815 | }, |
729 | { | 816 | { |
730 | .name = "Loopback Pin", | 817 | .name = "Loopback Pin", |
818 | .id = HSW_PCM_DAI_ID_LOOPBACK, | ||
731 | .capture = { | 819 | .capture = { |
732 | .stream_name = "Loopback Capture", | 820 | .stream_name = "Loopback Capture", |
733 | .channels_min = 2, | 821 | .channels_min = 2, |
@@ -736,16 +824,6 @@ static struct snd_soc_dai_driver hsw_dais[] = { | |||
736 | .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, | 824 | .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, |
737 | }, | 825 | }, |
738 | }, | 826 | }, |
739 | { | ||
740 | .name = "Capture Pin", | ||
741 | .capture = { | ||
742 | .stream_name = "Analog Capture", | ||
743 | .channels_min = 2, | ||
744 | .channels_max = 2, | ||
745 | .rates = SNDRV_PCM_RATE_48000, | ||
746 | .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, | ||
747 | }, | ||
748 | }, | ||
749 | }; | 827 | }; |
750 | 828 | ||
751 | static const struct snd_soc_dapm_widget widgets[] = { | 829 | static const struct snd_soc_dapm_widget widgets[] = { |
@@ -776,9 +854,20 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) | |||
776 | { | 854 | { |
777 | struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform); | 855 | struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform); |
778 | struct sst_pdata *pdata = dev_get_platdata(platform->dev); | 856 | struct sst_pdata *pdata = dev_get_platdata(platform->dev); |
779 | struct device *dma_dev = pdata->dma_dev; | 857 | struct device *dma_dev, *dev; |
780 | int i, ret = 0; | 858 | int i, ret = 0; |
781 | 859 | ||
860 | if (!pdata) | ||
861 | return -ENODEV; | ||
862 | |||
863 | dev = platform->dev; | ||
864 | dma_dev = pdata->dma_dev; | ||
865 | |||
866 | priv_data->hsw = pdata->dsp; | ||
867 | priv_data->dev = platform->dev; | ||
868 | priv_data->pm_state = HSW_PM_STATE_D0; | ||
869 | priv_data->soc_card = platform->component.card; | ||
870 | |||
782 | /* allocate DSP buffer page tables */ | 871 | /* allocate DSP buffer page tables */ |
783 | for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { | 872 | for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { |
784 | 873 | ||
@@ -801,6 +890,16 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) | |||
801 | } | 890 | } |
802 | } | 891 | } |
803 | 892 | ||
893 | /* allocate runtime modules */ | ||
894 | hsw_pcm_create_modules(priv_data); | ||
895 | |||
896 | /* enable runtime PM with auto suspend */ | ||
897 | pm_runtime_set_autosuspend_delay(platform->dev, | ||
898 | SST_RUNTIME_SUSPEND_DELAY); | ||
899 | pm_runtime_use_autosuspend(platform->dev); | ||
900 | pm_runtime_enable(platform->dev); | ||
901 | pm_runtime_idle(platform->dev); | ||
902 | |||
804 | return 0; | 903 | return 0; |
805 | 904 | ||
806 | err: | 905 | err: |
@@ -819,6 +918,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform) | |||
819 | snd_soc_platform_get_drvdata(platform); | 918 | snd_soc_platform_get_drvdata(platform); |
820 | int i; | 919 | int i; |
821 | 920 | ||
921 | pm_runtime_disable(platform->dev); | ||
922 | hsw_pcm_free_modules(priv_data); | ||
923 | |||
822 | for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { | 924 | for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { |
823 | if (hsw_dais[i].playback.channels_min) | 925 | if (hsw_dais[i].playback.channels_min) |
824 | snd_dma_free_pages(&priv_data->dmab[i][0]); | 926 | snd_dma_free_pages(&priv_data->dmab[i][0]); |
@@ -896,10 +998,181 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev) | |||
896 | return 0; | 998 | return 0; |
897 | } | 999 | } |
898 | 1000 | ||
1001 | #ifdef CONFIG_PM_RUNTIME | ||
1002 | |||
1003 | static int hsw_pcm_runtime_idle(struct device *dev) | ||
1004 | { | ||
1005 | return 0; | ||
1006 | } | ||
1007 | |||
1008 | static int hsw_pcm_runtime_suspend(struct device *dev) | ||
1009 | { | ||
1010 | struct hsw_priv_data *pdata = dev_get_drvdata(dev); | ||
1011 | struct sst_hsw *hsw = pdata->hsw; | ||
1012 | |||
1013 | if (pdata->pm_state == HSW_PM_STATE_D3) | ||
1014 | return 0; | ||
1015 | |||
1016 | sst_hsw_dsp_runtime_suspend(hsw); | ||
1017 | sst_hsw_dsp_runtime_sleep(hsw); | ||
1018 | pdata->pm_state = HSW_PM_STATE_D3; | ||
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | static int hsw_pcm_runtime_resume(struct device *dev) | ||
1024 | { | ||
1025 | struct hsw_priv_data *pdata = dev_get_drvdata(dev); | ||
1026 | struct sst_hsw *hsw = pdata->hsw; | ||
1027 | int ret; | ||
1028 | |||
1029 | if (pdata->pm_state == HSW_PM_STATE_D0) | ||
1030 | return 0; | ||
1031 | |||
1032 | ret = sst_hsw_dsp_load(hsw); | ||
1033 | if (ret < 0) { | ||
1034 | dev_err(dev, "failed to reload %d\n", ret); | ||
1035 | return ret; | ||
1036 | } | ||
1037 | |||
1038 | ret = hsw_pcm_create_modules(pdata); | ||
1039 | if (ret < 0) { | ||
1040 | dev_err(dev, "failed to create modules %d\n", ret); | ||
1041 | return ret; | ||
1042 | } | ||
1043 | |||
1044 | ret = sst_hsw_dsp_runtime_resume(hsw); | ||
1045 | if (ret < 0) | ||
1046 | return ret; | ||
1047 | else if (ret == 1) /* no action required */ | ||
1048 | return 0; | ||
1049 | |||
1050 | pdata->pm_state = HSW_PM_STATE_D0; | ||
1051 | return ret; | ||
1052 | } | ||
1053 | |||
1054 | #else | ||
1055 | #define hsw_pcm_runtime_idle NULL | ||
1056 | #define hsw_pcm_runtime_suspend NULL | ||
1057 | #define hsw_pcm_runtime_resume NULL | ||
1058 | #endif | ||
1059 | |||
1060 | #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME) | ||
1061 | |||
1062 | static void hsw_pcm_complete(struct device *dev) | ||
1063 | { | ||
1064 | struct hsw_priv_data *pdata = dev_get_drvdata(dev); | ||
1065 | struct sst_hsw *hsw = pdata->hsw; | ||
1066 | struct hsw_pcm_data *pcm_data; | ||
1067 | int i, err; | ||
1068 | |||
1069 | if (pdata->pm_state == HSW_PM_STATE_D0) | ||
1070 | return; | ||
1071 | |||
1072 | err = sst_hsw_dsp_load(hsw); | ||
1073 | if (err < 0) { | ||
1074 | dev_err(dev, "failed to reload %d\n", err); | ||
1075 | return; | ||
1076 | } | ||
1077 | |||
1078 | err = hsw_pcm_create_modules(pdata); | ||
1079 | if (err < 0) { | ||
1080 | dev_err(dev, "failed to create modules %d\n", err); | ||
1081 | return; | ||
1082 | } | ||
1083 | |||
1084 | for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { | ||
1085 | pcm_data = &pdata->pcm[i]; | ||
1086 | |||
1087 | if (!pcm_data->substream) | ||
1088 | continue; | ||
1089 | |||
1090 | err = sst_module_runtime_restore(pcm_data->runtime, | ||
1091 | &pcm_data->context); | ||
1092 | if (err < 0) | ||
1093 | dev_err(dev, "failed to restore context for PCM %d\n", i); | ||
1094 | } | ||
1095 | |||
1096 | snd_soc_resume(pdata->soc_card->dev); | ||
1097 | |||
1098 | err = sst_hsw_dsp_runtime_resume(hsw); | ||
1099 | if (err < 0) | ||
1100 | return; | ||
1101 | else if (err == 1) /* no action required */ | ||
1102 | return; | ||
1103 | |||
1104 | pdata->pm_state = HSW_PM_STATE_D0; | ||
1105 | return; | ||
1106 | } | ||
1107 | |||
1108 | static int hsw_pcm_prepare(struct device *dev) | ||
1109 | { | ||
1110 | struct hsw_priv_data *pdata = dev_get_drvdata(dev); | ||
1111 | struct sst_hsw *hsw = pdata->hsw; | ||
1112 | struct hsw_pcm_data *pcm_data; | ||
1113 | int i, err; | ||
1114 | |||
1115 | if (pdata->pm_state == HSW_PM_STATE_D3) | ||
1116 | return 0; | ||
1117 | /* suspend all active streams */ | ||
1118 | for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { | ||
1119 | pcm_data = &pdata->pcm[i]; | ||
1120 | |||
1121 | if (!pcm_data->substream) | ||
1122 | continue; | ||
1123 | dev_dbg(dev, "suspending pcm %d\n", i); | ||
1124 | snd_pcm_suspend_all(pcm_data->hsw_pcm); | ||
1125 | |||
1126 | /* We need to wait until the DSP FW stops the streams */ | ||
1127 | msleep(2); | ||
1128 | } | ||
1129 | |||
1130 | snd_soc_suspend(pdata->soc_card->dev); | ||
1131 | snd_soc_poweroff(pdata->soc_card->dev); | ||
1132 | |||
1133 | /* enter D3 state and stall */ | ||
1134 | sst_hsw_dsp_runtime_suspend(hsw); | ||
1135 | |||
1136 | /* preserve persistent memory */ | ||
1137 | for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { | ||
1138 | pcm_data = &pdata->pcm[i]; | ||
1139 | |||
1140 | if (!pcm_data->substream) | ||
1141 | continue; | ||
1142 | |||
1143 | dev_dbg(dev, "saving context pcm %d\n", i); | ||
1144 | err = sst_module_runtime_save(pcm_data->runtime, | ||
1145 | &pcm_data->context); | ||
1146 | if (err < 0) | ||
1147 | dev_err(dev, "failed to save context for PCM %d\n", i); | ||
1148 | } | ||
1149 | |||
1150 | /* put the DSP to sleep */ | ||
1151 | sst_hsw_dsp_runtime_sleep(hsw); | ||
1152 | pdata->pm_state = HSW_PM_STATE_D3; | ||
1153 | |||
1154 | return 0; | ||
1155 | } | ||
1156 | |||
1157 | #else | ||
1158 | #define hsw_pcm_prepare NULL | ||
1159 | #define hsw_pcm_complete NULL | ||
1160 | #endif | ||
1161 | |||
1162 | static const struct dev_pm_ops hsw_pcm_pm = { | ||
1163 | .runtime_idle = hsw_pcm_runtime_idle, | ||
1164 | .runtime_suspend = hsw_pcm_runtime_suspend, | ||
1165 | .runtime_resume = hsw_pcm_runtime_resume, | ||
1166 | .prepare = hsw_pcm_prepare, | ||
1167 | .complete = hsw_pcm_complete, | ||
1168 | }; | ||
1169 | |||
899 | static struct platform_driver hsw_pcm_driver = { | 1170 | static struct platform_driver hsw_pcm_driver = { |
900 | .driver = { | 1171 | .driver = { |
901 | .name = "haswell-pcm-audio", | 1172 | .name = "haswell-pcm-audio", |
902 | .owner = THIS_MODULE, | 1173 | .owner = THIS_MODULE, |
1174 | .pm = &hsw_pcm_pm, | ||
1175 | |||
903 | }, | 1176 | }, |
904 | 1177 | ||
905 | .probe = hsw_pcm_dev_probe, | 1178 | .probe = hsw_pcm_dev_probe, |