diff options
author | Vinod Koul <vinod.koul@intel.com> | 2014-10-30 06:50:59 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-10-31 08:52:43 -0400 |
commit | 7adab122a57c5ade8efc2e4de67c72b084c31cda (patch) | |
tree | c3bbf9309e27bb56df7f449f7b615ab63019b837 | |
parent | fdcc4a039f0263f4674e363ebed14783b2f0543d (diff) |
ASoC: Intel: sst - add compressed ops handling
This patch add low level IPC handling for compressed stream operations
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/intel/sst/sst_drv_interface.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c index 4187057fb933..5f75ef3cdd22 100644 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ b/sound/soc/intel/sst/sst_drv_interface.c | |||
@@ -172,6 +172,273 @@ static int sst_open_pcm_stream(struct device *dev, | |||
172 | return retval; | 172 | return retval; |
173 | } | 173 | } |
174 | 174 | ||
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 | |||
175 | /* | 442 | /* |
176 | * sst_close_pcm_stream - Close PCM interface | 443 | * sst_close_pcm_stream - Close PCM interface |
177 | * | 444 | * |
@@ -372,10 +639,28 @@ static struct sst_ops pcm_ops = { | |||
372 | .power = sst_power_control, | 639 | .power = sst_power_control, |
373 | }; | 640 | }; |
374 | 641 | ||
642 | static struct compress_sst_ops compr_ops = { | ||
643 | .open = sst_cdev_open, | ||
644 | .close = sst_cdev_close, | ||
645 | .stream_pause = sst_cdev_stream_pause, | ||
646 | .stream_pause_release = sst_cdev_stream_pause_release, | ||
647 | .stream_start = sst_cdev_stream_start, | ||
648 | .stream_drop = sst_cdev_stream_drop, | ||
649 | .stream_drain = sst_cdev_stream_drain, | ||
650 | .stream_partial_drain = sst_cdev_stream_partial_drain, | ||
651 | .tstamp = sst_cdev_tstamp, | ||
652 | .ack = sst_cdev_ack, | ||
653 | .get_caps = sst_cdev_caps, | ||
654 | .get_codec_caps = sst_cdev_codec_caps, | ||
655 | .set_metadata = sst_cdev_set_metadata, | ||
656 | .power = sst_power_control, | ||
657 | }; | ||
658 | |||
375 | static struct sst_device sst_dsp_device = { | 659 | static struct sst_device sst_dsp_device = { |
376 | .name = "Intel(R) SST LPE", | 660 | .name = "Intel(R) SST LPE", |
377 | .dev = NULL, | 661 | .dev = NULL, |
378 | .ops = &pcm_ops, | 662 | .ops = &pcm_ops, |
663 | .compr_ops = &compr_ops, | ||
379 | }; | 664 | }; |
380 | 665 | ||
381 | /* | 666 | /* |