summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2018-11-15 13:13:24 -0500
committerMark Brown <broonie@kernel.org>2018-12-14 07:43:59 -0500
commit22930c79ac5c243c95e46508f0989e153836adc7 (patch)
tree548c1311d371c1b31a7fbfe46b3bdd85a181713c /sound
parentf2e6c6aa0cb5f39e48627850680b87ec688070f8 (diff)
ASoC: qdsp6: q6asm-dai: Add support to compress offload
This patch adds MP3 playback support in q6asm dais, adding other codec support should be pretty trivial. Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Vinod Koul <vkoul@kernel.org> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/qcom/Kconfig1
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c372
2 files changed, 372 insertions, 1 deletions
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 1b9fc0665792..804ae0d93058 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -66,6 +66,7 @@ config SND_SOC_QDSP6_ASM
66 tristate 66 tristate
67 67
68config SND_SOC_QDSP6_ASM_DAI 68config SND_SOC_QDSP6_ASM_DAI
69 select SND_SOC_COMPRESS
69 tristate 70 tristate
70 71
71config SND_SOC_QDSP6 72config SND_SOC_QDSP6
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 86115de5c1b2..5b986b74dd36 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -10,6 +10,8 @@
10#include <sound/soc.h> 10#include <sound/soc.h>
11#include <sound/soc-dapm.h> 11#include <sound/soc-dapm.h>
12#include <sound/pcm.h> 12#include <sound/pcm.h>
13#include <linux/spinlock.h>
14#include <sound/compress_driver.h>
13#include <asm/dma.h> 15#include <asm/dma.h>
14#include <linux/dma-mapping.h> 16#include <linux/dma-mapping.h>
15#include <linux/of_device.h> 17#include <linux/of_device.h>
@@ -30,6 +32,15 @@
30#define CAPTURE_MIN_PERIOD_SIZE 320 32#define CAPTURE_MIN_PERIOD_SIZE 320
31#define SID_MASK_DEFAULT 0xF 33#define SID_MASK_DEFAULT 0xF
32 34
35/* Default values used if user space does not set */
36#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
37#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
38#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
39#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)
40#define Q6ASM_DAI_TX_RX 0
41#define Q6ASM_DAI_TX 1
42#define Q6ASM_DAI_RX 2
43
33enum stream_state { 44enum stream_state {
34 Q6ASM_STREAM_IDLE = 0, 45 Q6ASM_STREAM_IDLE = 0,
35 Q6ASM_STREAM_STOPPED, 46 Q6ASM_STREAM_STOPPED,
@@ -38,11 +49,18 @@ enum stream_state {
38 49
39struct q6asm_dai_rtd { 50struct q6asm_dai_rtd {
40 struct snd_pcm_substream *substream; 51 struct snd_pcm_substream *substream;
52 struct snd_compr_stream *cstream;
53 struct snd_compr_params codec_param;
54 struct snd_dma_buffer dma_buffer;
55 spinlock_t lock;
41 phys_addr_t phys; 56 phys_addr_t phys;
42 unsigned int pcm_size; 57 unsigned int pcm_size;
43 unsigned int pcm_count; 58 unsigned int pcm_count;
44 unsigned int pcm_irq_pos; /* IRQ position */ 59 unsigned int pcm_irq_pos; /* IRQ position */
45 unsigned int periods; 60 unsigned int periods;
61 unsigned int bytes_sent;
62 unsigned int bytes_received;
63 unsigned int copied_total;
46 uint16_t bits_per_sample; 64 uint16_t bits_per_sample;
47 uint16_t source; /* Encoding source bit mask */ 65 uint16_t source; /* Encoding source bit mask */
48 struct audio_client *audio_client; 66 struct audio_client *audio_client;
@@ -137,6 +155,21 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
137 .mask = 0, 155 .mask = 0,
138}; 156};
139 157
158static const struct snd_compr_codec_caps q6asm_compr_caps = {
159 .num_descriptors = 1,
160 .descriptor[0].max_ch = 2,
161 .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050,
162 24000, 32000, 44100, 48000, 88200,
163 96000, 176400, 192000 },
164 .descriptor[0].num_sample_rates = 13,
165 .descriptor[0].bit_rate[0] = 320,
166 .descriptor[0].bit_rate[1] = 128,
167 .descriptor[0].num_bitrates = 2,
168 .descriptor[0].profiles = 0,
169 .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
170 .descriptor[0].formats = 0,
171};
172
140static void event_handler(uint32_t opcode, uint32_t token, 173static void event_handler(uint32_t opcode, uint32_t token,
141 uint32_t *payload, void *priv) 174 uint32_t *payload, void *priv)
142{ 175{
@@ -460,6 +493,306 @@ static struct snd_pcm_ops q6asm_dai_ops = {
460 .mmap = q6asm_dai_mmap, 493 .mmap = q6asm_dai_mmap,
461}; 494};
462 495
496static void compress_event_handler(uint32_t opcode, uint32_t token,
497 uint32_t *payload, void *priv)
498{
499 struct q6asm_dai_rtd *prtd = priv;
500 struct snd_compr_stream *substream = prtd->cstream;
501 unsigned long flags;
502 uint64_t avail;
503
504 switch (opcode) {
505 case ASM_CLIENT_EVENT_CMD_RUN_DONE:
506 spin_lock_irqsave(&prtd->lock, flags);
507 if (!prtd->bytes_sent) {
508 q6asm_write_async(prtd->audio_client, prtd->pcm_count,
509 0, 0, NO_TIMESTAMP);
510 prtd->bytes_sent += prtd->pcm_count;
511 }
512
513 spin_unlock_irqrestore(&prtd->lock, flags);
514 break;
515
516 case ASM_CLIENT_EVENT_CMD_EOS_DONE:
517 prtd->state = Q6ASM_STREAM_STOPPED;
518 break;
519
520 case ASM_CLIENT_EVENT_DATA_WRITE_DONE:
521 spin_lock_irqsave(&prtd->lock, flags);
522
523 prtd->copied_total += prtd->pcm_count;
524 snd_compr_fragment_elapsed(substream);
525
526 if (prtd->state != Q6ASM_STREAM_RUNNING) {
527 spin_unlock_irqrestore(&prtd->lock, flags);
528 break;
529 }
530
531 avail = prtd->bytes_received - prtd->bytes_sent;
532
533 if (avail >= prtd->pcm_count) {
534 q6asm_write_async(prtd->audio_client,
535 prtd->pcm_count, 0, 0, NO_TIMESTAMP);
536 prtd->bytes_sent += prtd->pcm_count;
537 }
538
539 spin_unlock_irqrestore(&prtd->lock, flags);
540 break;
541
542 default:
543 break;
544 }
545}
546
547static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
548{
549 struct snd_soc_pcm_runtime *rtd = stream->private_data;
550 struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
551 struct snd_compr_runtime *runtime = stream->runtime;
552 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
553 struct q6asm_dai_data *pdata;
554 struct device *dev = c->dev;
555 struct q6asm_dai_rtd *prtd;
556 int stream_id, size, ret;
557
558 stream_id = cpu_dai->driver->id;
559 pdata = snd_soc_component_get_drvdata(c);
560 if (!pdata) {
561 dev_err(dev, "Drv data not found ..\n");
562 return -EINVAL;
563 }
564
565 prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
566 if (!prtd)
567 return -ENOMEM;
568
569 prtd->cstream = stream;
570 prtd->audio_client = q6asm_audio_client_alloc(dev,
571 (q6asm_cb)compress_event_handler,
572 prtd, stream_id, LEGACY_PCM_MODE);
573 if (!prtd->audio_client) {
574 dev_err(dev, "Could not allocate memory\n");
575 kfree(prtd);
576 return -ENOMEM;
577 }
578
579 size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *
580 COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
581 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size,
582 &prtd->dma_buffer);
583 if (ret) {
584 dev_err(dev, "Cannot allocate buffer(s)\n");
585 return ret;
586 }
587
588 if (pdata->sid < 0)
589 prtd->phys = prtd->dma_buffer.addr;
590 else
591 prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32);
592
593 snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer);
594 spin_lock_init(&prtd->lock);
595 runtime->private_data = prtd;
596
597 return 0;
598}
599
600static int q6asm_dai_compr_free(struct snd_compr_stream *stream)
601{
602 struct snd_compr_runtime *runtime = stream->runtime;
603 struct q6asm_dai_rtd *prtd = runtime->private_data;
604 struct snd_soc_pcm_runtime *rtd = stream->private_data;
605
606 if (prtd->audio_client) {
607 if (prtd->state)
608 q6asm_cmd(prtd->audio_client, CMD_CLOSE);
609
610 snd_dma_free_pages(&prtd->dma_buffer);
611 q6asm_unmap_memory_regions(stream->direction,
612 prtd->audio_client);
613 q6asm_audio_client_free(prtd->audio_client);
614 prtd->audio_client = NULL;
615 }
616 q6routing_stream_close(rtd->dai_link->id, stream->direction);
617 kfree(prtd);
618
619 return 0;
620}
621
622static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream,
623 struct snd_compr_params *params)
624{
625 struct snd_compr_runtime *runtime = stream->runtime;
626 struct q6asm_dai_rtd *prtd = runtime->private_data;
627 struct snd_soc_pcm_runtime *rtd = stream->private_data;
628 struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
629 int dir = stream->direction;
630 struct q6asm_dai_data *pdata;
631 struct device *dev = c->dev;
632 int ret;
633
634 memcpy(&prtd->codec_param, params, sizeof(*params));
635
636 pdata = snd_soc_component_get_drvdata(c);
637 if (!pdata)
638 return -EINVAL;
639
640 if (!prtd || !prtd->audio_client) {
641 dev_err(dev, "private data null or audio client freed\n");
642 return -EINVAL;
643 }
644
645 prtd->periods = runtime->fragments;
646 prtd->pcm_count = runtime->fragment_size;
647 prtd->pcm_size = runtime->fragments * runtime->fragment_size;
648 prtd->bits_per_sample = 16;
649 if (dir == SND_COMPRESS_PLAYBACK) {
650 ret = q6asm_open_write(prtd->audio_client, params->codec.id,
651 prtd->bits_per_sample);
652
653 if (ret < 0) {
654 dev_err(dev, "q6asm_open_write failed\n");
655 q6asm_audio_client_free(prtd->audio_client);
656 prtd->audio_client = NULL;
657 return ret;
658 }
659 }
660
661 prtd->session_id = q6asm_get_session_id(prtd->audio_client);
662 ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE,
663 prtd->session_id, dir);
664 if (ret) {
665 dev_err(dev, "Stream reg failed ret:%d\n", ret);
666 return ret;
667 }
668
669 ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys,
670 (prtd->pcm_size / prtd->periods),
671 prtd->periods);
672
673 if (ret < 0) {
674 dev_err(dev, "Buffer Mapping failed ret:%d\n", ret);
675 return -ENOMEM;
676 }
677
678 prtd->state = Q6ASM_STREAM_RUNNING;
679
680 return 0;
681}
682
683static int q6asm_dai_compr_trigger(struct snd_compr_stream *stream, int cmd)
684{
685 struct snd_compr_runtime *runtime = stream->runtime;
686 struct q6asm_dai_rtd *prtd = runtime->private_data;
687 int ret = 0;
688
689 switch (cmd) {
690 case SNDRV_PCM_TRIGGER_START:
691 case SNDRV_PCM_TRIGGER_RESUME:
692 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
693 ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
694 break;
695 case SNDRV_PCM_TRIGGER_STOP:
696 prtd->state = Q6ASM_STREAM_STOPPED;
697 ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
698 break;
699 case SNDRV_PCM_TRIGGER_SUSPEND:
700 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
701 ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
702 break;
703 default:
704 ret = -EINVAL;
705 break;
706 }
707
708 return ret;
709}
710
711static int q6asm_dai_compr_pointer(struct snd_compr_stream *stream,
712 struct snd_compr_tstamp *tstamp)
713{
714 struct snd_compr_runtime *runtime = stream->runtime;
715 struct q6asm_dai_rtd *prtd = runtime->private_data;
716 unsigned long flags;
717
718 spin_lock_irqsave(&prtd->lock, flags);
719
720 tstamp->copied_total = prtd->copied_total;
721 tstamp->byte_offset = prtd->copied_total % prtd->pcm_size;
722
723 spin_unlock_irqrestore(&prtd->lock, flags);
724
725 return 0;
726}
727
728static int q6asm_dai_compr_ack(struct snd_compr_stream *stream,
729 size_t count)
730{
731 struct snd_compr_runtime *runtime = stream->runtime;
732 struct q6asm_dai_rtd *prtd = runtime->private_data;
733 unsigned long flags;
734
735 spin_lock_irqsave(&prtd->lock, flags);
736 prtd->bytes_received += count;
737 spin_unlock_irqrestore(&prtd->lock, flags);
738
739 return count;
740}
741
742static int q6asm_dai_compr_mmap(struct snd_compr_stream *stream,
743 struct vm_area_struct *vma)
744{
745 struct snd_compr_runtime *runtime = stream->runtime;
746 struct q6asm_dai_rtd *prtd = runtime->private_data;
747 struct snd_soc_pcm_runtime *rtd = stream->private_data;
748 struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
749 struct device *dev = c->dev;
750
751 return dma_mmap_coherent(dev, vma,
752 prtd->dma_buffer.area, prtd->dma_buffer.addr,
753 prtd->dma_buffer.bytes);
754}
755
756static int q6asm_dai_compr_get_caps(struct snd_compr_stream *stream,
757 struct snd_compr_caps *caps)
758{
759 caps->direction = SND_COMPRESS_PLAYBACK;
760 caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;
761 caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;
762 caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
763 caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
764 caps->num_codecs = 1;
765 caps->codecs[0] = SND_AUDIOCODEC_MP3;
766
767 return 0;
768}
769
770static int q6asm_dai_compr_get_codec_caps(struct snd_compr_stream *stream,
771 struct snd_compr_codec_caps *codec)
772{
773 switch (codec->codec) {
774 case SND_AUDIOCODEC_MP3:
775 *codec = q6asm_compr_caps;
776 break;
777 default:
778 break;
779 }
780
781 return 0;
782}
783
784static struct snd_compr_ops q6asm_dai_compr_ops = {
785 .open = q6asm_dai_compr_open,
786 .free = q6asm_dai_compr_free,
787 .set_params = q6asm_dai_compr_set_params,
788 .pointer = q6asm_dai_compr_pointer,
789 .trigger = q6asm_dai_compr_trigger,
790 .get_caps = q6asm_dai_compr_get_caps,
791 .get_codec_caps = q6asm_dai_compr_get_codec_caps,
792 .mmap = q6asm_dai_compr_mmap,
793 .ack = q6asm_dai_compr_ack,
794};
795
463static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) 796static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd)
464{ 797{
465 struct snd_pcm_substream *psubstream, *csubstream; 798 struct snd_pcm_substream *psubstream, *csubstream;
@@ -515,7 +848,7 @@ static const struct snd_soc_component_driver q6asm_fe_dai_component = {
515 .ops = &q6asm_dai_ops, 848 .ops = &q6asm_dai_ops,
516 .pcm_new = q6asm_dai_pcm_new, 849 .pcm_new = q6asm_dai_pcm_new,
517 .pcm_free = q6asm_dai_pcm_free, 850 .pcm_free = q6asm_dai_pcm_free,
518 851 .compr_ops = &q6asm_dai_compr_ops,
519}; 852};
520 853
521static struct snd_soc_dai_driver q6asm_fe_dais[] = { 854static struct snd_soc_dai_driver q6asm_fe_dais[] = {
@@ -529,6 +862,41 @@ static struct snd_soc_dai_driver q6asm_fe_dais[] = {
529 Q6ASM_FEDAI_DRIVER(8), 862 Q6ASM_FEDAI_DRIVER(8),
530}; 863};
531 864
865static int of_q6asm_parse_dai_data(struct device *dev,
866 struct q6asm_dai_data *pdata)
867{
868 static struct snd_soc_dai_driver *dai_drv;
869 struct snd_soc_pcm_stream empty_stream;
870 struct device_node *node;
871 int ret, id, dir;
872
873 memset(&empty_stream, 0, sizeof(empty_stream));
874
875 for_each_child_of_node(dev->of_node, node) {
876 ret = of_property_read_u32(node, "reg", &id);
877 if (ret || id > MAX_SESSIONS || id < 0) {
878 dev_err(dev, "valid dai id not found:%d\n", ret);
879 continue;
880 }
881
882 dai_drv = &q6asm_fe_dais[id];
883
884 ret = of_property_read_u32(node, "direction", &dir);
885 if (ret)
886 continue;
887
888 if (dir == Q6ASM_DAI_RX)
889 dai_drv->capture = empty_stream;
890 else if (dir == Q6ASM_DAI_TX)
891 dai_drv->playback = empty_stream;
892
893 if (of_property_read_bool(node, "is-compress-dai"))
894 dai_drv->compress_new = snd_soc_new_compress;
895 }
896
897 return 0;
898}
899
532static int q6asm_dai_probe(struct platform_device *pdev) 900static int q6asm_dai_probe(struct platform_device *pdev)
533{ 901{
534 struct device *dev = &pdev->dev; 902 struct device *dev = &pdev->dev;
@@ -549,6 +917,8 @@ static int q6asm_dai_probe(struct platform_device *pdev)
549 917
550 dev_set_drvdata(dev, pdata); 918 dev_set_drvdata(dev, pdata);
551 919
920 of_q6asm_parse_dai_data(dev, pdata);
921
552 return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, 922 return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component,
553 q6asm_fe_dais, 923 q6asm_fe_dais,
554 ARRAY_SIZE(q6asm_fe_dais)); 924 ARRAY_SIZE(q6asm_fe_dais));