Commit | Line | Data |
---|---|---|
d927fdae | 1 | /* |
a4b12990 | 2 | * sst_mfld_platform.c - Intel MID Platform driver |
d927fdae | 3 | * |
61b165ca | 4 | * Copyright (C) 2010-2014 Intel Corp |
d927fdae VK |
5 | * Author: Vinod Koul <vinod.koul@intel.com> |
6 | * Author: Harsha Priya <priya.harsha@intel.com> | |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; version 2 of the License. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
d927fdae | 18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
d927fdae VK |
19 | */ |
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
21 | ||
22 | #include <linux/slab.h> | |
23 | #include <linux/io.h> | |
da155d5b | 24 | #include <linux/module.h> |
d927fdae VK |
25 | #include <sound/core.h> |
26 | #include <sound/pcm.h> | |
27 | #include <sound/pcm_params.h> | |
28 | #include <sound/soc.h> | |
c514a911 | 29 | #include <sound/compress_driver.h> |
61b165ca | 30 | #include <asm/platform_sst_audio.h> |
a4b12990 | 31 | #include "sst-mfld-platform.h" |
61b165ca | 32 | #include "sst-atom-controls.h" |
d927fdae | 33 | |
4b68b4e1 | 34 | struct sst_device *sst; |
03c33042 | 35 | static DEFINE_MUTEX(sst_lock); |
4b68b4e1 | 36 | extern struct snd_compr_ops sst_platform_compr_ops; |
03c33042 VK |
37 | |
38 | int sst_register_dsp(struct sst_device *dev) | |
39 | { | |
656a22a1 TI |
40 | if (WARN_ON(!dev)) |
41 | return -EINVAL; | |
03c33042 VK |
42 | if (!try_module_get(dev->dev->driver->owner)) |
43 | return -ENODEV; | |
44 | mutex_lock(&sst_lock); | |
45 | if (sst) { | |
46 | pr_err("we already have a device %s\n", sst->name); | |
47 | module_put(dev->dev->driver->owner); | |
48 | mutex_unlock(&sst_lock); | |
49 | return -EEXIST; | |
50 | } | |
51 | pr_debug("registering device %s\n", dev->name); | |
52 | sst = dev; | |
53 | mutex_unlock(&sst_lock); | |
54 | return 0; | |
55 | } | |
56 | EXPORT_SYMBOL_GPL(sst_register_dsp); | |
57 | ||
58 | int sst_unregister_dsp(struct sst_device *dev) | |
59 | { | |
656a22a1 TI |
60 | if (WARN_ON(!dev)) |
61 | return -EINVAL; | |
03c33042 VK |
62 | if (dev != sst) |
63 | return -EINVAL; | |
64 | ||
65 | mutex_lock(&sst_lock); | |
66 | ||
67 | if (!sst) { | |
68 | mutex_unlock(&sst_lock); | |
69 | return -EIO; | |
70 | } | |
71 | ||
72 | module_put(sst->dev->driver->owner); | |
73 | pr_debug("unreg %s\n", sst->name); | |
74 | sst = NULL; | |
75 | mutex_unlock(&sst_lock); | |
76 | return 0; | |
77 | } | |
78 | EXPORT_SYMBOL_GPL(sst_unregister_dsp); | |
79 | ||
d927fdae VK |
80 | static struct snd_pcm_hardware sst_platform_pcm_hw = { |
81 | .info = (SNDRV_PCM_INFO_INTERLEAVED | | |
82 | SNDRV_PCM_INFO_DOUBLE | | |
83 | SNDRV_PCM_INFO_PAUSE | | |
84 | SNDRV_PCM_INFO_RESUME | | |
85 | SNDRV_PCM_INFO_MMAP| | |
86 | SNDRV_PCM_INFO_MMAP_VALID | | |
87 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
88 | SNDRV_PCM_INFO_SYNC_START), | |
d927fdae VK |
89 | .buffer_bytes_max = SST_MAX_BUFFER, |
90 | .period_bytes_min = SST_MIN_PERIOD_BYTES, | |
91 | .period_bytes_max = SST_MAX_PERIOD_BYTES, | |
92 | .periods_min = SST_MIN_PERIODS, | |
93 | .periods_max = SST_MAX_PERIODS, | |
94 | .fifo_size = SST_FIFO_SIZE, | |
95 | }; | |
96 | ||
61b165ca VK |
97 | static struct sst_dev_stream_map dpcm_strm_map[] = { |
98 | {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */ | |
99 | {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0}, | |
100 | {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0}, | |
101 | {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0}, | |
102 | }; | |
103 | ||
d927fdae | 104 | /* MFLD - MSIC */ |
d1b73287 | 105 | static struct snd_soc_dai_driver sst_platform_dai[] = { |
d927fdae | 106 | { |
5106f5a1 | 107 | .name = "Headset-cpu-dai", |
d927fdae VK |
108 | .id = 0, |
109 | .playback = { | |
110 | .channels_min = SST_STEREO, | |
111 | .channels_max = SST_STEREO, | |
112 | .rates = SNDRV_PCM_RATE_48000, | |
113 | .formats = SNDRV_PCM_FMTBIT_S24_LE, | |
114 | }, | |
a7bffdf7 HP |
115 | .capture = { |
116 | .channels_min = 1, | |
117 | .channels_max = 5, | |
118 | .rates = SNDRV_PCM_RATE_48000, | |
119 | .formats = SNDRV_PCM_FMTBIT_S24_LE, | |
d927fdae VK |
120 | }, |
121 | }, | |
c514a911 VK |
122 | { |
123 | .name = "Compress-cpu-dai", | |
124 | .compress_dai = 1, | |
125 | .playback = { | |
126 | .channels_min = SST_STEREO, | |
127 | .channels_max = SST_STEREO, | |
128 | .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, | |
129 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
130 | }, | |
131 | }, | |
d927fdae | 132 | }; |
5c68536f | 133 | |
d927fdae | 134 | /* helper functions */ |
4496ffab | 135 | void sst_set_stream_status(struct sst_runtime_stream *stream, |
5c68536f HP |
136 | int state) |
137 | { | |
d89b0a13 LG |
138 | unsigned long flags; |
139 | spin_lock_irqsave(&stream->status_lock, flags); | |
5c68536f | 140 | stream->stream_status = state; |
d89b0a13 | 141 | spin_unlock_irqrestore(&stream->status_lock, flags); |
5c68536f HP |
142 | } |
143 | ||
144 | static inline int sst_get_stream_status(struct sst_runtime_stream *stream) | |
145 | { | |
146 | int state; | |
d89b0a13 | 147 | unsigned long flags; |
5c68536f | 148 | |
d89b0a13 | 149 | spin_lock_irqsave(&stream->status_lock, flags); |
5c68536f | 150 | state = stream->stream_status; |
d89b0a13 | 151 | spin_unlock_irqrestore(&stream->status_lock, flags); |
5c68536f HP |
152 | return state; |
153 | } | |
154 | ||
a870cdce VK |
155 | static void sst_fill_alloc_params(struct snd_pcm_substream *substream, |
156 | struct snd_sst_alloc_params_ext *alloc_param) | |
157 | { | |
158 | unsigned int channels; | |
159 | snd_pcm_uframes_t period_size; | |
160 | ssize_t periodbytes; | |
161 | ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream); | |
162 | u32 buffer_addr = virt_to_phys(substream->dma_buffer.area); | |
163 | ||
164 | channels = substream->runtime->channels; | |
165 | period_size = substream->runtime->period_size; | |
166 | periodbytes = samples_to_bytes(substream->runtime, period_size); | |
167 | alloc_param->ring_buf_info[0].addr = buffer_addr; | |
168 | alloc_param->ring_buf_info[0].size = buffer_bytes; | |
169 | alloc_param->sg_count = 1; | |
170 | alloc_param->reserved = 0; | |
171 | alloc_param->frag_size = periodbytes * channels; | |
172 | ||
173 | } | |
5c68536f | 174 | static void sst_fill_pcm_params(struct snd_pcm_substream *substream, |
a870cdce | 175 | struct snd_sst_stream_params *param) |
5c68536f | 176 | { |
a870cdce VK |
177 | param->uc.pcm_params.num_chan = (u8) substream->runtime->channels; |
178 | param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; | |
179 | param->uc.pcm_params.sfreq = substream->runtime->rate; | |
5c68536f | 180 | |
a870cdce VK |
181 | /* PCM stream via ALSA interface */ |
182 | param->uc.pcm_params.use_offload_path = 0; | |
183 | param->uc.pcm_params.reserved2 = 0; | |
184 | memset(param->uc.pcm_params.channel_map, 0, sizeof(u8)); | |
185 | ||
186 | } | |
61b165ca VK |
187 | |
188 | static int sst_get_stream_mapping(int dev, int sdev, int dir, | |
189 | struct sst_dev_stream_map *map, int size) | |
190 | { | |
191 | int i; | |
192 | ||
193 | if (map == NULL) | |
194 | return -EINVAL; | |
195 | ||
196 | ||
197 | /* index 0 is not used in stream map */ | |
198 | for (i = 1; i < size; i++) { | |
199 | if ((map[i].dev_num == dev) && (map[i].direction == dir)) | |
200 | return i; | |
201 | } | |
202 | return 0; | |
203 | } | |
204 | ||
a870cdce | 205 | int sst_fill_stream_params(void *substream, |
61b165ca | 206 | const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress) |
a870cdce | 207 | { |
61b165ca VK |
208 | int map_size; |
209 | int index; | |
210 | struct sst_dev_stream_map *map; | |
a870cdce VK |
211 | struct snd_pcm_substream *pstream = NULL; |
212 | struct snd_compr_stream *cstream = NULL; | |
213 | ||
61b165ca VK |
214 | map = ctx->pdata->pdev_strm_map; |
215 | map_size = ctx->pdata->strm_map_size; | |
216 | ||
a870cdce VK |
217 | if (is_compress == true) |
218 | cstream = (struct snd_compr_stream *)substream; | |
219 | else | |
220 | pstream = (struct snd_pcm_substream *)substream; | |
221 | ||
222 | str_params->stream_type = SST_STREAM_TYPE_MUSIC; | |
223 | ||
224 | /* For pcm streams */ | |
61b165ca VK |
225 | if (pstream) { |
226 | index = sst_get_stream_mapping(pstream->pcm->device, | |
227 | pstream->number, pstream->stream, | |
228 | map, map_size); | |
229 | if (index <= 0) | |
230 | return -EINVAL; | |
231 | ||
232 | str_params->stream_id = index; | |
233 | str_params->device_type = map[index].device_id; | |
234 | str_params->task = map[index].task_id; | |
235 | ||
a870cdce | 236 | str_params->ops = (u8)pstream->stream; |
61b165ca VK |
237 | } |
238 | ||
239 | if (cstream) { | |
240 | index = sst_get_stream_mapping(cstream->device->device, | |
241 | 0, cstream->direction, | |
242 | map, map_size); | |
243 | if (index <= 0) | |
244 | return -EINVAL; | |
245 | str_params->stream_id = index; | |
246 | str_params->device_type = map[index].device_id; | |
247 | str_params->task = map[index].task_id; | |
a870cdce | 248 | |
61b165ca VK |
249 | str_params->ops = (u8)cstream->direction; |
250 | } | |
a870cdce | 251 | return 0; |
5c68536f HP |
252 | } |
253 | ||
a870cdce VK |
254 | static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, |
255 | struct snd_soc_platform *platform) | |
d927fdae VK |
256 | { |
257 | struct sst_runtime_stream *stream = | |
258 | substream->runtime->private_data; | |
a870cdce VK |
259 | struct snd_sst_stream_params param = {{{0,},},}; |
260 | struct snd_sst_params str_params = {0}; | |
261 | struct snd_sst_alloc_params_ext alloc_params = {0}; | |
262 | int ret_val = 0; | |
61b165ca | 263 | struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); |
d927fdae VK |
264 | |
265 | /* set codec params and inform SST driver the same */ | |
5c68536f | 266 | sst_fill_pcm_params(substream, ¶m); |
a870cdce | 267 | sst_fill_alloc_params(substream, &alloc_params); |
d927fdae | 268 | substream->runtime->dma_area = substream->dma_buffer.area; |
d927fdae | 269 | str_params.sparams = param; |
a870cdce VK |
270 | str_params.aparams = alloc_params; |
271 | str_params.codec = SST_CODEC_TYPE_PCM; | |
272 | ||
273 | /* fill the device type and stream id to pass to SST driver */ | |
61b165ca | 274 | ret_val = sst_fill_stream_params(substream, ctx, &str_params, false); |
d927fdae VK |
275 | if (ret_val < 0) |
276 | return ret_val; | |
277 | ||
a870cdce VK |
278 | stream->stream_info.str_id = str_params.stream_id; |
279 | ||
280 | ret_val = stream->ops->open(&str_params); | |
281 | if (ret_val <= 0) | |
282 | return ret_val; | |
283 | ||
284 | ||
d927fdae VK |
285 | return ret_val; |
286 | } | |
287 | ||
9daa5bd3 | 288 | static void sst_period_elapsed(void *arg) |
d927fdae | 289 | { |
9daa5bd3 | 290 | struct snd_pcm_substream *substream = arg; |
d927fdae | 291 | struct sst_runtime_stream *stream; |
5c68536f | 292 | int status; |
d927fdae VK |
293 | |
294 | if (!substream || !substream->runtime) | |
295 | return; | |
296 | stream = substream->runtime->private_data; | |
297 | if (!stream) | |
298 | return; | |
5c68536f HP |
299 | status = sst_get_stream_status(stream); |
300 | if (status != SST_PLATFORM_RUNNING) | |
d927fdae | 301 | return; |
d927fdae | 302 | snd_pcm_period_elapsed(substream); |
d927fdae VK |
303 | } |
304 | ||
305 | static int sst_platform_init_stream(struct snd_pcm_substream *substream) | |
306 | { | |
307 | struct sst_runtime_stream *stream = | |
308 | substream->runtime->private_data; | |
309 | int ret_val; | |
310 | ||
311 | pr_debug("setting buffer ptr param\n"); | |
5c68536f | 312 | sst_set_stream_status(stream, SST_PLATFORM_INIT); |
d927fdae | 313 | stream->stream_info.period_elapsed = sst_period_elapsed; |
9daa5bd3 | 314 | stream->stream_info.arg = substream; |
d927fdae VK |
315 | stream->stream_info.buffer_ptr = 0; |
316 | stream->stream_info.sfreq = substream->runtime->rate; | |
03c33042 | 317 | ret_val = stream->ops->device_control( |
a211701e | 318 | SST_SND_STREAM_INIT, &stream->stream_info); |
d927fdae VK |
319 | if (ret_val) |
320 | pr_err("control_set ret error %d\n", ret_val); | |
321 | return ret_val; | |
322 | ||
323 | } | |
324 | /* end -- helper functions */ | |
325 | ||
6cc0f4e6 VK |
326 | static int sst_media_open(struct snd_pcm_substream *substream, |
327 | struct snd_soc_dai *dai) | |
d927fdae | 328 | { |
6cc0f4e6 | 329 | int ret_val = 0; |
22be504a | 330 | struct snd_pcm_runtime *runtime = substream->runtime; |
d927fdae | 331 | struct sst_runtime_stream *stream; |
22be504a | 332 | |
d927fdae VK |
333 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); |
334 | if (!stream) | |
335 | return -ENOMEM; | |
d927fdae | 336 | spin_lock_init(&stream->status_lock); |
03c33042 VK |
337 | |
338 | /* get the sst ops */ | |
339 | mutex_lock(&sst_lock); | |
6cc0f4e6 VK |
340 | if (!sst || |
341 | !try_module_get(sst->dev->driver->owner)) { | |
03c33042 | 342 | pr_err("no device available to run\n"); |
6cc0f4e6 VK |
343 | ret_val = -ENODEV; |
344 | goto out_ops; | |
d927fdae | 345 | } |
03c33042 VK |
346 | stream->ops = sst->ops; |
347 | mutex_unlock(&sst_lock); | |
348 | ||
349 | stream->stream_info.str_id = 0; | |
6cc0f4e6 | 350 | |
9daa5bd3 | 351 | stream->stream_info.arg = substream; |
03c33042 | 352 | /* allocate memory for SST API set */ |
d927fdae | 353 | runtime->private_data = stream; |
283e42e0 | 354 | |
6cc0f4e6 VK |
355 | /* Make sure, that the period size is always even */ |
356 | snd_pcm_hw_constraint_step(substream->runtime, 0, | |
357 | SNDRV_PCM_HW_PARAM_PERIODS, 2); | |
358 | ||
359 | return snd_pcm_hw_constraint_integer(runtime, | |
360 | SNDRV_PCM_HW_PARAM_PERIODS); | |
361 | out_ops: | |
362 | kfree(stream); | |
363 | mutex_unlock(&sst_lock); | |
364 | return ret_val; | |
d927fdae VK |
365 | } |
366 | ||
6cc0f4e6 VK |
367 | static void sst_media_close(struct snd_pcm_substream *substream, |
368 | struct snd_soc_dai *dai) | |
d927fdae VK |
369 | { |
370 | struct sst_runtime_stream *stream; | |
371 | int ret_val = 0, str_id; | |
372 | ||
d927fdae VK |
373 | stream = substream->runtime->private_data; |
374 | str_id = stream->stream_info.str_id; | |
d927fdae | 375 | if (str_id) |
03c33042 VK |
376 | ret_val = stream->ops->close(str_id); |
377 | module_put(sst->dev->driver->owner); | |
d927fdae | 378 | kfree(stream); |
61b165ca VK |
379 | } |
380 | ||
381 | static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform, | |
382 | struct snd_pcm_substream *substream) | |
383 | { | |
384 | struct sst_data *sst = snd_soc_platform_get_drvdata(platform); | |
385 | struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; | |
386 | struct sst_runtime_stream *stream = | |
387 | substream->runtime->private_data; | |
388 | u32 str_id = stream->stream_info.str_id; | |
389 | unsigned int pipe_id; | |
390 | pipe_id = map[str_id].device_id; | |
391 | ||
392 | pr_debug("%s: got pipe_id = %#x for str_id = %d\n", | |
393 | __func__, pipe_id, str_id); | |
394 | return pipe_id; | |
d927fdae VK |
395 | } |
396 | ||
6cc0f4e6 VK |
397 | static int sst_media_prepare(struct snd_pcm_substream *substream, |
398 | struct snd_soc_dai *dai) | |
d927fdae VK |
399 | { |
400 | struct sst_runtime_stream *stream; | |
401 | int ret_val = 0, str_id; | |
402 | ||
d927fdae VK |
403 | stream = substream->runtime->private_data; |
404 | str_id = stream->stream_info.str_id; | |
405 | if (stream->stream_info.str_id) { | |
03c33042 VK |
406 | ret_val = stream->ops->device_control( |
407 | SST_SND_DROP, &str_id); | |
d927fdae VK |
408 | return ret_val; |
409 | } | |
410 | ||
a870cdce VK |
411 | ret_val = sst_platform_alloc_stream(substream, dai->platform); |
412 | if (ret_val <= 0) | |
d927fdae VK |
413 | return ret_val; |
414 | snprintf(substream->pcm->id, sizeof(substream->pcm->id), | |
415 | "%d", stream->stream_info.str_id); | |
416 | ||
417 | ret_val = sst_platform_init_stream(substream); | |
418 | if (ret_val) | |
419 | return ret_val; | |
420 | substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; | |
421 | return ret_val; | |
422 | } | |
423 | ||
6cc0f4e6 VK |
424 | static int sst_media_hw_params(struct snd_pcm_substream *substream, |
425 | struct snd_pcm_hw_params *params, | |
426 | struct snd_soc_dai *dai) | |
427 | { | |
428 | snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | |
429 | memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); | |
430 | return 0; | |
431 | } | |
432 | ||
433 | static int sst_media_hw_free(struct snd_pcm_substream *substream, | |
434 | struct snd_soc_dai *dai) | |
435 | { | |
436 | return snd_pcm_lib_free_pages(substream); | |
437 | } | |
438 | ||
439 | static struct snd_soc_dai_ops sst_media_dai_ops = { | |
440 | .startup = sst_media_open, | |
441 | .shutdown = sst_media_close, | |
442 | .prepare = sst_media_prepare, | |
443 | .hw_params = sst_media_hw_params, | |
444 | .hw_free = sst_media_hw_free, | |
445 | }; | |
446 | ||
447 | static int sst_platform_open(struct snd_pcm_substream *substream) | |
448 | { | |
449 | struct snd_pcm_runtime *runtime; | |
450 | ||
451 | if (substream->pcm->internal) | |
452 | return 0; | |
453 | ||
454 | runtime = substream->runtime; | |
455 | runtime->hw = sst_platform_pcm_hw; | |
456 | return 0; | |
457 | } | |
458 | ||
5106f5a1 | 459 | static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, |
d927fdae VK |
460 | int cmd) |
461 | { | |
462 | int ret_val = 0, str_id; | |
463 | struct sst_runtime_stream *stream; | |
a211701e | 464 | int str_cmd, status; |
d927fdae | 465 | |
5106f5a1 | 466 | pr_debug("sst_platform_pcm_trigger called\n"); |
d927fdae | 467 | stream = substream->runtime->private_data; |
d927fdae | 468 | str_id = stream->stream_info.str_id; |
d927fdae VK |
469 | switch (cmd) { |
470 | case SNDRV_PCM_TRIGGER_START: | |
471 | pr_debug("sst: Trigger Start\n"); | |
a211701e HP |
472 | str_cmd = SST_SND_START; |
473 | status = SST_PLATFORM_RUNNING; | |
9daa5bd3 | 474 | stream->stream_info.arg = substream; |
d927fdae VK |
475 | break; |
476 | case SNDRV_PCM_TRIGGER_STOP: | |
477 | pr_debug("sst: in stop\n"); | |
a211701e HP |
478 | str_cmd = SST_SND_DROP; |
479 | status = SST_PLATFORM_DROPPED; | |
d927fdae VK |
480 | break; |
481 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
482 | pr_debug("sst: in pause\n"); | |
a211701e HP |
483 | str_cmd = SST_SND_PAUSE; |
484 | status = SST_PLATFORM_PAUSED; | |
d927fdae VK |
485 | break; |
486 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
487 | pr_debug("sst: in pause release\n"); | |
a211701e HP |
488 | str_cmd = SST_SND_RESUME; |
489 | status = SST_PLATFORM_RUNNING; | |
d927fdae VK |
490 | break; |
491 | default: | |
a211701e | 492 | return -EINVAL; |
d927fdae | 493 | } |
03c33042 | 494 | ret_val = stream->ops->device_control(str_cmd, &str_id); |
a211701e HP |
495 | if (!ret_val) |
496 | sst_set_stream_status(stream, status); | |
497 | ||
d927fdae VK |
498 | return ret_val; |
499 | } | |
500 | ||
501 | ||
5106f5a1 | 502 | static snd_pcm_uframes_t sst_platform_pcm_pointer |
d927fdae VK |
503 | (struct snd_pcm_substream *substream) |
504 | { | |
505 | struct sst_runtime_stream *stream; | |
5c68536f | 506 | int ret_val, status; |
d927fdae VK |
507 | struct pcm_stream_info *str_info; |
508 | ||
d927fdae | 509 | stream = substream->runtime->private_data; |
5c68536f HP |
510 | status = sst_get_stream_status(stream); |
511 | if (status == SST_PLATFORM_INIT) | |
d927fdae | 512 | return 0; |
d927fdae | 513 | str_info = &stream->stream_info; |
03c33042 | 514 | ret_val = stream->ops->device_control( |
d927fdae VK |
515 | SST_SND_BUFFER_POINTER, str_info); |
516 | if (ret_val) { | |
517 | pr_err("sst: error code = %d\n", ret_val); | |
518 | return ret_val; | |
519 | } | |
2a635825 | 520 | substream->runtime->delay = str_info->pcm_delay; |
6cc0f4e6 | 521 | return str_info->buffer_ptr; |
9ab88434 | 522 | } |
523 | ||
d927fdae | 524 | static struct snd_pcm_ops sst_platform_ops = { |
5106f5a1 | 525 | .open = sst_platform_open, |
d927fdae | 526 | .ioctl = snd_pcm_lib_ioctl, |
5106f5a1 VK |
527 | .trigger = sst_platform_pcm_trigger, |
528 | .pointer = sst_platform_pcm_pointer, | |
d927fdae VK |
529 | }; |
530 | ||
531 | static void sst_pcm_free(struct snd_pcm *pcm) | |
532 | { | |
533 | pr_debug("sst_pcm_free called\n"); | |
534 | snd_pcm_lib_preallocate_free_for_all(pcm); | |
535 | } | |
536 | ||
8faa8c1a | 537 | static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) |
d927fdae | 538 | { |
6cc0f4e6 | 539 | struct snd_soc_dai *dai = rtd->cpu_dai; |
552d1ef6 | 540 | struct snd_pcm *pcm = rtd->pcm; |
d927fdae VK |
541 | int retval = 0; |
542 | ||
6cc0f4e6 VK |
543 | if (dai->driver->playback.channels_min || |
544 | dai->driver->capture.channels_min) { | |
d927fdae VK |
545 | retval = snd_pcm_lib_preallocate_pages_for_all(pcm, |
546 | SNDRV_DMA_TYPE_CONTINUOUS, | |
6cc0f4e6 | 547 | snd_dma_continuous_data(GFP_DMA), |
d927fdae VK |
548 | SST_MIN_BUFFER, SST_MAX_BUFFER); |
549 | if (retval) { | |
550 | pr_err("dma buffer allocationf fail\n"); | |
551 | return retval; | |
552 | } | |
553 | } | |
d927fdae VK |
554 | return retval; |
555 | } | |
c514a911 | 556 | |
8faa8c1a | 557 | static struct snd_soc_platform_driver sst_soc_platform_drv = { |
d927fdae | 558 | .ops = &sst_platform_ops, |
c514a911 | 559 | .compr_ops = &sst_platform_compr_ops, |
d927fdae VK |
560 | .pcm_new = sst_pcm_new, |
561 | .pcm_free = sst_pcm_free, | |
562 | }; | |
563 | ||
2b4c78df VK |
564 | static const struct snd_soc_component_driver sst_component = { |
565 | .name = "sst", | |
566 | }; | |
567 | ||
568 | ||
d927fdae VK |
569 | static int sst_platform_probe(struct platform_device *pdev) |
570 | { | |
61b165ca | 571 | struct sst_data *drv; |
d927fdae | 572 | int ret; |
2741d43a | 573 | struct sst_platform_data *pdata; |
61b165ca VK |
574 | |
575 | drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); | |
19a23a5d | 576 | if (drv == NULL) { |
61b165ca VK |
577 | pr_err("kzalloc failed\n"); |
578 | return -ENOMEM; | |
579 | } | |
580 | ||
2741d43a SP |
581 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); |
582 | if (pdata == NULL) { | |
583 | pr_err("kzalloc failed for pdata\n"); | |
584 | return -ENOMEM; | |
585 | } | |
586 | ||
61b165ca VK |
587 | pdata->pdev_strm_map = dpcm_strm_map; |
588 | pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map); | |
589 | drv->pdata = pdata; | |
590 | mutex_init(&drv->lock); | |
591 | dev_set_drvdata(&pdev->dev, drv); | |
d927fdae | 592 | |
d927fdae VK |
593 | ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); |
594 | if (ret) { | |
595 | pr_err("registering soc platform failed\n"); | |
596 | return ret; | |
597 | } | |
5c68536f | 598 | |
b1c36861 | 599 | ret = snd_soc_register_component(&pdev->dev, &sst_component, |
d927fdae VK |
600 | sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); |
601 | if (ret) { | |
602 | pr_err("registering cpu dais failed\n"); | |
603 | snd_soc_unregister_platform(&pdev->dev); | |
604 | } | |
605 | return ret; | |
606 | } | |
607 | ||
608 | static int sst_platform_remove(struct platform_device *pdev) | |
609 | { | |
610 | ||
b1c36861 | 611 | snd_soc_unregister_component(&pdev->dev); |
d927fdae | 612 | snd_soc_unregister_platform(&pdev->dev); |
25985edc | 613 | pr_debug("sst_platform_remove success\n"); |
d927fdae VK |
614 | return 0; |
615 | } | |
616 | ||
617 | static struct platform_driver sst_platform_driver = { | |
618 | .driver = { | |
a4b12990 | 619 | .name = "sst-mfld-platform", |
d927fdae VK |
620 | .owner = THIS_MODULE, |
621 | }, | |
622 | .probe = sst_platform_probe, | |
623 | .remove = sst_platform_remove, | |
624 | }; | |
625 | ||
29515d62 | 626 | module_platform_driver(sst_platform_driver); |
d927fdae VK |
627 | |
628 | MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); | |
629 | MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); | |
630 | MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); | |
631 | MODULE_LICENSE("GPL v2"); | |
a4b12990 | 632 | MODULE_ALIAS("platform:sst-mfld-platform"); |