Commit | Line | Data |
---|---|---|
cc547054 VK |
1 | /* |
2 | * sst_drv_interface.c - Intel SST Driver for audio engine | |
3 | * | |
4 | * Copyright (C) 2008-14 Intel Corp | |
5 | * Authors: Vinod Koul <vinod.koul@intel.com> | |
6 | * Harsha Priya <priya.harsha@intel.com> | |
7 | * Dharageswari R <dharageswari.r@intel.com) | |
8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; version 2 of the License. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
20 | */ | |
21 | #include <linux/delay.h> | |
22 | #include <linux/pci.h> | |
23 | #include <linux/fs.h> | |
24 | #include <linux/firmware.h> | |
25 | #include <linux/pm_runtime.h> | |
26 | #include <linux/pm_qos.h> | |
27 | #include <linux/math64.h> | |
28 | #include <sound/core.h> | |
29 | #include <sound/pcm.h> | |
30 | #include <sound/soc.h> | |
31 | #include <sound/compress_driver.h> | |
32 | #include <asm/platform_sst_audio.h> | |
33 | #include "../sst-mfld-platform.h" | |
34 | #include "sst.h" | |
35 | #include "../sst-dsp.h" | |
36 | ||
37 | ||
38 | ||
39 | #define NUM_CODEC 2 | |
40 | #define MIN_FRAGMENT 2 | |
41 | #define MAX_FRAGMENT 4 | |
42 | #define MIN_FRAGMENT_SIZE (50 * 1024) | |
43 | #define MAX_FRAGMENT_SIZE (1024 * 1024) | |
44 | #define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1) | |
45 | ||
46 | int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id) | |
47 | { | |
48 | struct stream_info *stream; | |
49 | int ret = 0; | |
50 | ||
51 | stream = get_stream_info(ctx, str_id); | |
52 | if (stream) { | |
53 | /* str_id is valid, so stream is alloacted */ | |
54 | ret = sst_free_stream(ctx, str_id); | |
55 | if (ret) | |
56 | sst_clean_stream(&ctx->streams[str_id]); | |
57 | return ret; | |
790b4075 VK |
58 | } else { |
59 | dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id); | |
cc547054 VK |
60 | } |
61 | return ret; | |
62 | } | |
63 | ||
64 | int sst_get_stream_allocated(struct intel_sst_drv *ctx, | |
65 | struct snd_sst_params *str_param, | |
66 | struct snd_sst_lib_download **lib_dnld) | |
67 | { | |
68 | int retval; | |
69 | ||
70 | retval = ctx->ops->alloc_stream(ctx, str_param); | |
71 | if (retval > 0) | |
72 | dev_dbg(ctx->dev, "Stream allocated %d\n", retval); | |
73 | return retval; | |
74 | ||
75 | } | |
76 | ||
77 | /* | |
78 | * sst_get_sfreq - this function returns the frequency of the stream | |
79 | * | |
80 | * @str_param : stream params | |
81 | */ | |
82 | int sst_get_sfreq(struct snd_sst_params *str_param) | |
83 | { | |
84 | switch (str_param->codec) { | |
85 | case SST_CODEC_TYPE_PCM: | |
86 | return str_param->sparams.uc.pcm_params.sfreq; | |
87 | case SST_CODEC_TYPE_AAC: | |
88 | return str_param->sparams.uc.aac_params.externalsr; | |
89 | case SST_CODEC_TYPE_MP3: | |
90 | return 0; | |
91 | default: | |
92 | return -EINVAL; | |
93 | } | |
94 | } | |
95 | ||
96 | /* | |
dee2ce69 | 97 | * sst_get_num_channel - get number of channels for the stream |
cc547054 VK |
98 | * |
99 | * @str_param : stream params | |
100 | */ | |
101 | int sst_get_num_channel(struct snd_sst_params *str_param) | |
102 | { | |
103 | switch (str_param->codec) { | |
104 | case SST_CODEC_TYPE_PCM: | |
105 | return str_param->sparams.uc.pcm_params.num_chan; | |
106 | case SST_CODEC_TYPE_MP3: | |
107 | return str_param->sparams.uc.mp3_params.num_chan; | |
108 | case SST_CODEC_TYPE_AAC: | |
109 | return str_param->sparams.uc.aac_params.num_chan; | |
110 | default: | |
111 | return -EINVAL; | |
112 | } | |
113 | } | |
114 | ||
115 | /* | |
116 | * sst_get_stream - this function prepares for stream allocation | |
117 | * | |
118 | * @str_param : stream param | |
119 | */ | |
120 | int sst_get_stream(struct intel_sst_drv *ctx, | |
121 | struct snd_sst_params *str_param) | |
122 | { | |
123 | int retval; | |
124 | struct stream_info *str_info; | |
125 | ||
126 | /* stream is not allocated, we are allocating */ | |
127 | retval = ctx->ops->alloc_stream(ctx, str_param); | |
128 | if (retval <= 0) { | |
129 | return -EIO; | |
130 | } | |
131 | /* store sampling freq */ | |
132 | str_info = &ctx->streams[retval]; | |
133 | str_info->sfreq = sst_get_sfreq(str_param); | |
134 | ||
135 | return retval; | |
136 | } | |
137 | ||
138 | static int sst_power_control(struct device *dev, bool state) | |
139 | { | |
140 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
141 | ||
142 | dev_dbg(ctx->dev, "state:%d", state); | |
143 | if (state == true) | |
144 | return pm_runtime_get_sync(dev); | |
145 | else | |
146 | return sst_pm_runtime_put(ctx); | |
147 | } | |
148 | ||
149 | /* | |
150 | * sst_open_pcm_stream - Open PCM interface | |
151 | * | |
152 | * @str_param: parameters of pcm stream | |
153 | * | |
154 | * This function is called by MID sound card driver to open | |
155 | * a new pcm interface | |
156 | */ | |
157 | static int sst_open_pcm_stream(struct device *dev, | |
158 | struct snd_sst_params *str_param) | |
159 | { | |
160 | int retval; | |
161 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
162 | ||
163 | if (!str_param) | |
164 | return -EINVAL; | |
165 | ||
cc547054 | 166 | retval = sst_get_stream(ctx, str_param); |
1a6db0bd | 167 | if (retval > 0) |
cc547054 | 168 | ctx->stream_cnt++; |
1a6db0bd | 169 | else |
cc547054 | 170 | dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval); |
cc547054 VK |
171 | |
172 | return retval; | |
173 | } | |
174 | ||
7adab122 VK |
175 | static int sst_cdev_open(struct device *dev, |
176 | struct snd_sst_params *str_params, struct sst_compress_cb *cb) | |
177 | { | |
178 | int str_id, retval; | |
179 | struct stream_info *stream; | |
180 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
181 | ||
182 | retval = pm_runtime_get_sync(ctx->dev); | |
183 | if (retval < 0) | |
184 | return retval; | |
185 | ||
186 | str_id = sst_get_stream(ctx, str_params); | |
187 | if (str_id > 0) { | |
188 | dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id); | |
189 | stream = &ctx->streams[str_id]; | |
190 | stream->compr_cb = cb->compr_cb; | |
191 | stream->compr_cb_param = cb->param; | |
192 | stream->drain_notify = cb->drain_notify; | |
193 | stream->drain_cb_param = cb->drain_cb_param; | |
194 | } else { | |
195 | dev_err(dev, "stream encountered error during alloc %d\n", str_id); | |
196 | str_id = -EINVAL; | |
197 | sst_pm_runtime_put(ctx); | |
198 | } | |
199 | return str_id; | |
200 | } | |
201 | ||
202 | static int sst_cdev_close(struct device *dev, unsigned int str_id) | |
203 | { | |
204 | int retval; | |
205 | struct stream_info *stream; | |
206 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
207 | ||
208 | stream = get_stream_info(ctx, str_id); | |
209 | if (!stream) { | |
210 | dev_err(dev, "stream info is NULL for str %d!!!\n", str_id); | |
211 | return -EINVAL; | |
212 | } | |
213 | ||
214 | if (stream->status == STREAM_RESET) { | |
215 | dev_dbg(dev, "stream in reset state...\n"); | |
216 | stream->status = STREAM_UN_INIT; | |
217 | ||
218 | retval = 0; | |
219 | goto put; | |
220 | } | |
221 | ||
222 | retval = sst_free_stream(ctx, str_id); | |
223 | put: | |
224 | stream->compr_cb_param = NULL; | |
225 | stream->compr_cb = NULL; | |
226 | ||
227 | if (retval) | |
228 | dev_err(dev, "free stream returned err %d\n", retval); | |
229 | ||
230 | dev_dbg(dev, "End\n"); | |
231 | return retval; | |
232 | ||
233 | } | |
234 | ||
235 | static int sst_cdev_ack(struct device *dev, unsigned int str_id, | |
236 | unsigned long bytes) | |
237 | { | |
238 | struct stream_info *stream; | |
239 | struct snd_sst_tstamp fw_tstamp = {0,}; | |
240 | int offset; | |
241 | void __iomem *addr; | |
242 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
243 | ||
244 | stream = get_stream_info(ctx, str_id); | |
245 | if (!stream) | |
246 | return -EINVAL; | |
247 | ||
248 | /* update bytes sent */ | |
249 | stream->cumm_bytes += bytes; | |
250 | dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); | |
251 | ||
252 | memcpy_fromio(&fw_tstamp, | |
253 | ((void *)(ctx->mailbox + ctx->tstamp) | |
254 | +(str_id * sizeof(fw_tstamp))), | |
255 | sizeof(fw_tstamp)); | |
256 | ||
257 | fw_tstamp.bytes_copied = stream->cumm_bytes; | |
258 | dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n", | |
259 | fw_tstamp.bytes_copied, bytes); | |
260 | ||
261 | addr = ((void *)(ctx->mailbox + ctx->tstamp)) + | |
262 | (str_id * sizeof(fw_tstamp)); | |
263 | offset = offsetof(struct snd_sst_tstamp, bytes_copied); | |
264 | sst_shim_write(addr, offset, fw_tstamp.bytes_copied); | |
265 | return 0; | |
266 | } | |
267 | ||
268 | static int sst_cdev_set_metadata(struct device *dev, | |
269 | unsigned int str_id, struct snd_compr_metadata *metadata) | |
270 | { | |
271 | int retval = 0; | |
272 | struct stream_info *str_info; | |
273 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
274 | ||
275 | dev_dbg(dev, "set metadata for stream %d\n", str_id); | |
276 | ||
277 | str_info = get_stream_info(ctx, str_id); | |
278 | if (!str_info) | |
279 | return -EINVAL; | |
280 | ||
281 | dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id); | |
282 | retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD, | |
283 | IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id, | |
284 | sizeof(*metadata), metadata, NULL, | |
285 | true, true, true, false); | |
286 | ||
287 | return retval; | |
288 | } | |
289 | ||
290 | static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id) | |
291 | { | |
292 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
293 | ||
294 | return sst_pause_stream(ctx, str_id); | |
295 | } | |
296 | ||
297 | static int sst_cdev_stream_pause_release(struct device *dev, | |
298 | unsigned int str_id) | |
299 | { | |
300 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
301 | ||
302 | return sst_resume_stream(ctx, str_id); | |
303 | } | |
304 | ||
305 | static int sst_cdev_stream_start(struct device *dev, unsigned int str_id) | |
306 | { | |
307 | struct stream_info *str_info; | |
308 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
309 | ||
310 | str_info = get_stream_info(ctx, str_id); | |
311 | if (!str_info) | |
312 | return -EINVAL; | |
313 | str_info->prev = str_info->status; | |
314 | str_info->status = STREAM_RUNNING; | |
315 | return sst_start_stream(ctx, str_id); | |
316 | } | |
317 | ||
318 | static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id) | |
319 | { | |
320 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
321 | ||
322 | return sst_drop_stream(ctx, str_id); | |
323 | } | |
324 | ||
325 | static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id) | |
326 | { | |
327 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
328 | ||
329 | return sst_drain_stream(ctx, str_id, false); | |
330 | } | |
331 | ||
332 | static int sst_cdev_stream_partial_drain(struct device *dev, | |
333 | unsigned int str_id) | |
334 | { | |
335 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
336 | ||
337 | return sst_drain_stream(ctx, str_id, true); | |
338 | } | |
339 | ||
340 | static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, | |
341 | struct snd_compr_tstamp *tstamp) | |
342 | { | |
343 | struct snd_sst_tstamp fw_tstamp = {0,}; | |
344 | struct stream_info *stream; | |
345 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
346 | ||
347 | memcpy_fromio(&fw_tstamp, | |
348 | ((void *)(ctx->mailbox + ctx->tstamp) | |
349 | +(str_id * sizeof(fw_tstamp))), | |
350 | sizeof(fw_tstamp)); | |
351 | ||
352 | stream = get_stream_info(ctx, str_id); | |
353 | if (!stream) | |
354 | return -EINVAL; | |
355 | dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter); | |
356 | ||
357 | tstamp->copied_total = fw_tstamp.ring_buffer_counter; | |
358 | tstamp->pcm_frames = fw_tstamp.frames_decoded; | |
359 | tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, | |
360 | (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24))); | |
361 | tstamp->sampling_rate = fw_tstamp.sampling_frequency; | |
362 | ||
363 | dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames); | |
364 | dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n", | |
365 | str_id, tstamp->copied_total, tstamp->pcm_frames); | |
366 | dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames); | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
371 | static int sst_cdev_caps(struct snd_compr_caps *caps) | |
372 | { | |
373 | caps->num_codecs = NUM_CODEC; | |
374 | caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ | |
375 | caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ | |
376 | caps->min_fragments = MIN_FRAGMENT; | |
377 | caps->max_fragments = MAX_FRAGMENT; | |
378 | caps->codecs[0] = SND_AUDIOCODEC_MP3; | |
379 | caps->codecs[1] = SND_AUDIOCODEC_AAC; | |
380 | return 0; | |
381 | } | |
382 | ||
383 | static struct snd_compr_codec_caps caps_mp3 = { | |
384 | .num_descriptors = 1, | |
385 | .descriptor[0].max_ch = 2, | |
386 | .descriptor[0].sample_rates[0] = 48000, | |
387 | .descriptor[0].sample_rates[1] = 44100, | |
388 | .descriptor[0].sample_rates[2] = 32000, | |
389 | .descriptor[0].sample_rates[3] = 16000, | |
390 | .descriptor[0].sample_rates[4] = 8000, | |
391 | .descriptor[0].num_sample_rates = 5, | |
392 | .descriptor[0].bit_rate[0] = 320, | |
393 | .descriptor[0].bit_rate[1] = 192, | |
394 | .descriptor[0].num_bitrates = 2, | |
395 | .descriptor[0].profiles = 0, | |
396 | .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, | |
397 | .descriptor[0].formats = 0, | |
398 | }; | |
399 | ||
400 | static struct snd_compr_codec_caps caps_aac = { | |
401 | .num_descriptors = 2, | |
402 | .descriptor[1].max_ch = 2, | |
403 | .descriptor[0].sample_rates[0] = 48000, | |
404 | .descriptor[0].sample_rates[1] = 44100, | |
405 | .descriptor[0].sample_rates[2] = 32000, | |
406 | .descriptor[0].sample_rates[3] = 16000, | |
407 | .descriptor[0].sample_rates[4] = 8000, | |
408 | .descriptor[0].num_sample_rates = 5, | |
409 | .descriptor[1].bit_rate[0] = 320, | |
410 | .descriptor[1].bit_rate[1] = 192, | |
411 | .descriptor[1].num_bitrates = 2, | |
412 | .descriptor[1].profiles = 0, | |
413 | .descriptor[1].modes = 0, | |
414 | .descriptor[1].formats = | |
415 | (SND_AUDIOSTREAMFORMAT_MP4ADTS | | |
416 | SND_AUDIOSTREAMFORMAT_RAW), | |
417 | }; | |
418 | ||
419 | static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec) | |
420 | { | |
421 | if (codec->codec == SND_AUDIOCODEC_MP3) | |
422 | *codec = caps_mp3; | |
423 | else if (codec->codec == SND_AUDIOCODEC_AAC) | |
424 | *codec = caps_aac; | |
425 | else | |
426 | return -EINVAL; | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
431 | void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id) | |
432 | { | |
433 | struct stream_info *stream; | |
434 | ||
435 | dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n", | |
436 | str_id); | |
437 | stream = &ctx->streams[str_id]; | |
438 | if (stream->compr_cb) | |
439 | stream->compr_cb(stream->compr_cb_param); | |
440 | } | |
441 | ||
cc547054 VK |
442 | /* |
443 | * sst_close_pcm_stream - Close PCM interface | |
444 | * | |
445 | * @str_id: stream id to be closed | |
446 | * | |
447 | * This function is called by MID sound card driver to close | |
448 | * an existing pcm interface | |
449 | */ | |
450 | static int sst_close_pcm_stream(struct device *dev, unsigned int str_id) | |
451 | { | |
452 | struct stream_info *stream; | |
453 | int retval = 0; | |
454 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
455 | ||
456 | stream = get_stream_info(ctx, str_id); | |
457 | if (!stream) { | |
458 | dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id); | |
459 | return -EINVAL; | |
460 | } | |
461 | ||
462 | if (stream->status == STREAM_RESET) { | |
463 | /* silently fail here as we have cleaned the stream earlier */ | |
464 | dev_dbg(ctx->dev, "stream in reset state...\n"); | |
465 | ||
466 | retval = 0; | |
467 | goto put; | |
468 | } | |
469 | ||
470 | retval = free_stream_context(ctx, str_id); | |
471 | put: | |
472 | stream->pcm_substream = NULL; | |
473 | stream->status = STREAM_UN_INIT; | |
474 | stream->period_elapsed = NULL; | |
475 | ctx->stream_cnt--; | |
476 | ||
1a6db0bd SP |
477 | if (retval) |
478 | dev_err(ctx->dev, "free stream returned err %d\n", retval); | |
cc547054 VK |
479 | |
480 | dev_dbg(ctx->dev, "Exit\n"); | |
481 | return 0; | |
482 | } | |
483 | ||
484 | static inline int sst_calc_tstamp(struct intel_sst_drv *ctx, | |
485 | struct pcm_stream_info *info, | |
486 | struct snd_pcm_substream *substream, | |
487 | struct snd_sst_tstamp *fw_tstamp) | |
488 | { | |
489 | size_t delay_bytes, delay_frames; | |
490 | size_t buffer_sz; | |
491 | u32 pointer_bytes, pointer_samples; | |
492 | ||
493 | dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n", | |
494 | fw_tstamp->ring_buffer_counter); | |
495 | dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n", | |
496 | fw_tstamp->hardware_counter); | |
497 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
498 | delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter - | |
499 | fw_tstamp->hardware_counter); | |
500 | else | |
501 | delay_bytes = (size_t) (fw_tstamp->hardware_counter - | |
502 | fw_tstamp->ring_buffer_counter); | |
503 | delay_frames = bytes_to_frames(substream->runtime, delay_bytes); | |
504 | buffer_sz = snd_pcm_lib_buffer_bytes(substream); | |
505 | div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes); | |
506 | pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes); | |
507 | ||
508 | dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes); | |
509 | ||
510 | info->buffer_ptr = pointer_samples / substream->runtime->channels; | |
511 | ||
512 | info->pcm_delay = delay_frames / substream->runtime->channels; | |
513 | dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n", | |
514 | info->buffer_ptr, info->pcm_delay); | |
515 | return 0; | |
516 | } | |
517 | ||
518 | static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) | |
519 | { | |
520 | struct stream_info *stream; | |
521 | struct snd_pcm_substream *substream; | |
522 | struct snd_sst_tstamp fw_tstamp; | |
523 | unsigned int str_id; | |
524 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
525 | ||
526 | str_id = info->str_id; | |
527 | stream = get_stream_info(ctx, str_id); | |
528 | if (!stream) | |
529 | return -EINVAL; | |
530 | ||
531 | if (!stream->pcm_substream) | |
532 | return -EINVAL; | |
533 | substream = stream->pcm_substream; | |
534 | ||
535 | memcpy_fromio(&fw_tstamp, | |
536 | ((void *)(ctx->mailbox + ctx->tstamp) | |
537 | + (str_id * sizeof(fw_tstamp))), | |
538 | sizeof(fw_tstamp)); | |
539 | return sst_calc_tstamp(ctx, info, substream, &fw_tstamp); | |
540 | } | |
541 | ||
542 | static int sst_stream_start(struct device *dev, int str_id) | |
543 | { | |
544 | struct stream_info *str_info; | |
545 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
546 | ||
547 | if (ctx->sst_state != SST_FW_RUNNING) | |
548 | return 0; | |
549 | str_info = get_stream_info(ctx, str_id); | |
550 | if (!str_info) | |
551 | return -EINVAL; | |
552 | str_info->prev = str_info->status; | |
553 | str_info->status = STREAM_RUNNING; | |
554 | sst_start_stream(ctx, str_id); | |
555 | ||
556 | return 0; | |
557 | } | |
558 | ||
559 | static int sst_stream_drop(struct device *dev, int str_id) | |
560 | { | |
561 | struct stream_info *str_info; | |
562 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
563 | ||
564 | if (ctx->sst_state != SST_FW_RUNNING) | |
565 | return 0; | |
566 | ||
567 | str_info = get_stream_info(ctx, str_id); | |
568 | if (!str_info) | |
569 | return -EINVAL; | |
570 | str_info->prev = STREAM_UN_INIT; | |
571 | str_info->status = STREAM_INIT; | |
572 | return sst_drop_stream(ctx, str_id); | |
573 | } | |
574 | ||
e0b87d47 VK |
575 | static int sst_stream_pause(struct device *dev, int str_id) |
576 | { | |
577 | struct stream_info *str_info; | |
578 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
579 | ||
580 | if (ctx->sst_state != SST_FW_RUNNING) | |
581 | return 0; | |
582 | ||
583 | str_info = get_stream_info(ctx, str_id); | |
584 | if (!str_info) | |
585 | return -EINVAL; | |
586 | ||
587 | return sst_pause_stream(ctx, str_id); | |
588 | } | |
589 | ||
590 | static int sst_stream_resume(struct device *dev, int str_id) | |
591 | { | |
592 | struct stream_info *str_info; | |
593 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
594 | ||
595 | if (ctx->sst_state != SST_FW_RUNNING) | |
596 | return 0; | |
597 | ||
598 | str_info = get_stream_info(ctx, str_id); | |
599 | if (!str_info) | |
600 | return -EINVAL; | |
601 | return sst_resume_stream(ctx, str_id); | |
602 | } | |
603 | ||
cc547054 VK |
604 | static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info) |
605 | { | |
606 | int str_id = 0; | |
607 | struct stream_info *stream; | |
608 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
609 | ||
610 | str_id = str_info->str_id; | |
611 | ||
612 | if (ctx->sst_state != SST_FW_RUNNING) | |
613 | return 0; | |
614 | ||
615 | stream = get_stream_info(ctx, str_id); | |
616 | if (!stream) | |
617 | return -EINVAL; | |
618 | ||
619 | dev_dbg(ctx->dev, "setting the period ptrs\n"); | |
620 | stream->pcm_substream = str_info->arg; | |
621 | stream->period_elapsed = str_info->period_elapsed; | |
622 | stream->sfreq = str_info->sfreq; | |
623 | stream->prev = stream->status; | |
624 | stream->status = STREAM_INIT; | |
625 | dev_dbg(ctx->dev, | |
626 | "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n", | |
627 | stream->pcm_substream, stream->period_elapsed, | |
628 | stream->sfreq, stream->status); | |
629 | ||
630 | return 0; | |
631 | } | |
632 | ||
633 | /* | |
634 | * sst_set_byte_stream - Set generic params | |
635 | * | |
636 | * @cmd: control cmd to be set | |
637 | * @arg: command argument | |
638 | * | |
639 | * This function is called by MID sound card driver to configure | |
640 | * SST runtime params. | |
641 | */ | |
642 | static int sst_send_byte_stream(struct device *dev, | |
643 | struct snd_sst_bytes_v2 *bytes) | |
644 | { | |
645 | int ret_val = 0; | |
646 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
647 | ||
648 | if (NULL == bytes) | |
649 | return -EINVAL; | |
650 | ret_val = pm_runtime_get_sync(ctx->dev); | |
651 | if (ret_val < 0) | |
652 | return ret_val; | |
653 | ||
654 | ret_val = sst_send_byte_stream_mrfld(ctx, bytes); | |
655 | sst_pm_runtime_put(ctx); | |
656 | ||
657 | return ret_val; | |
658 | } | |
659 | ||
660 | static struct sst_ops pcm_ops = { | |
661 | .open = sst_open_pcm_stream, | |
662 | .stream_init = sst_stream_init, | |
663 | .stream_start = sst_stream_start, | |
664 | .stream_drop = sst_stream_drop, | |
e0b87d47 VK |
665 | .stream_pause = sst_stream_pause, |
666 | .stream_pause_release = sst_stream_resume, | |
cc547054 VK |
667 | .stream_read_tstamp = sst_read_timestamp, |
668 | .send_byte_stream = sst_send_byte_stream, | |
669 | .close = sst_close_pcm_stream, | |
670 | .power = sst_power_control, | |
671 | }; | |
672 | ||
7adab122 VK |
673 | static struct compress_sst_ops compr_ops = { |
674 | .open = sst_cdev_open, | |
675 | .close = sst_cdev_close, | |
676 | .stream_pause = sst_cdev_stream_pause, | |
677 | .stream_pause_release = sst_cdev_stream_pause_release, | |
678 | .stream_start = sst_cdev_stream_start, | |
679 | .stream_drop = sst_cdev_stream_drop, | |
680 | .stream_drain = sst_cdev_stream_drain, | |
681 | .stream_partial_drain = sst_cdev_stream_partial_drain, | |
682 | .tstamp = sst_cdev_tstamp, | |
683 | .ack = sst_cdev_ack, | |
684 | .get_caps = sst_cdev_caps, | |
685 | .get_codec_caps = sst_cdev_codec_caps, | |
686 | .set_metadata = sst_cdev_set_metadata, | |
687 | .power = sst_power_control, | |
688 | }; | |
689 | ||
cc547054 VK |
690 | static struct sst_device sst_dsp_device = { |
691 | .name = "Intel(R) SST LPE", | |
692 | .dev = NULL, | |
693 | .ops = &pcm_ops, | |
7adab122 | 694 | .compr_ops = &compr_ops, |
cc547054 VK |
695 | }; |
696 | ||
697 | /* | |
698 | * sst_register - function to register DSP | |
699 | * | |
700 | * This functions registers DSP with the platform driver | |
701 | */ | |
702 | int sst_register(struct device *dev) | |
703 | { | |
704 | int ret_val; | |
705 | ||
706 | sst_dsp_device.dev = dev; | |
707 | ret_val = sst_register_dsp(&sst_dsp_device); | |
708 | if (ret_val) | |
709 | dev_err(dev, "Unable to register DSP with platform driver\n"); | |
710 | ||
711 | return ret_val; | |
712 | } | |
713 | ||
714 | int sst_unregister(struct device *dev) | |
715 | { | |
716 | return sst_unregister_dsp(&sst_dsp_device); | |
717 | } |