diff options
Diffstat (limited to 'sound/soc/atmel/atmel_ssc_dai.c')
-rw-r--r-- | sound/soc/atmel/atmel_ssc_dai.c | 148 |
1 files changed, 123 insertions, 25 deletions
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index c85844d4845b..5d230cee3fa7 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c | |||
@@ -205,8 +205,7 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) | |||
205 | static int atmel_ssc_startup(struct snd_pcm_substream *substream, | 205 | static int atmel_ssc_startup(struct snd_pcm_substream *substream, |
206 | struct snd_soc_dai *dai) | 206 | struct snd_soc_dai *dai) |
207 | { | 207 | { |
208 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | 208 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; |
209 | struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; | ||
210 | int dir_mask; | 209 | int dir_mask; |
211 | 210 | ||
212 | pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", | 211 | pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", |
@@ -235,8 +234,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, | |||
235 | static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, | 234 | static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, |
236 | struct snd_soc_dai *dai) | 235 | struct snd_soc_dai *dai) |
237 | { | 236 | { |
238 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | 237 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; |
239 | struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; | ||
240 | struct atmel_pcm_dma_params *dma_params; | 238 | struct atmel_pcm_dma_params *dma_params; |
241 | int dir, dir_mask; | 239 | int dir, dir_mask; |
242 | 240 | ||
@@ -338,7 +336,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | |||
338 | struct snd_soc_dai *dai) | 336 | struct snd_soc_dai *dai) |
339 | { | 337 | { |
340 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | 338 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); |
341 | int id = rtd->dai->cpu_dai->id; | 339 | int id = dai->id; |
342 | struct atmel_ssc_info *ssc_p = &ssc_info[id]; | 340 | struct atmel_ssc_info *ssc_p = &ssc_info[id]; |
343 | struct atmel_pcm_dma_params *dma_params; | 341 | struct atmel_pcm_dma_params *dma_params; |
344 | int dir, channels, bits; | 342 | int dir, channels, bits; |
@@ -368,7 +366,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | |||
368 | * function. It should not be used for other purposes | 366 | * function. It should not be used for other purposes |
369 | * as it is common to all substreams. | 367 | * as it is common to all substreams. |
370 | */ | 368 | */ |
371 | snd_soc_dai_set_dma_data(rtd->dai->cpu_dai, substream, dma_params); | 369 | snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params); |
372 | 370 | ||
373 | channels = params_channels(params); | 371 | channels = params_channels(params); |
374 | 372 | ||
@@ -605,8 +603,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | |||
605 | static int atmel_ssc_prepare(struct snd_pcm_substream *substream, | 603 | static int atmel_ssc_prepare(struct snd_pcm_substream *substream, |
606 | struct snd_soc_dai *dai) | 604 | struct snd_soc_dai *dai) |
607 | { | 605 | { |
608 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | 606 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; |
609 | struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; | ||
610 | struct atmel_pcm_dma_params *dma_params; | 607 | struct atmel_pcm_dma_params *dma_params; |
611 | int dir; | 608 | int dir; |
612 | 609 | ||
@@ -690,6 +687,32 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) | |||
690 | # define atmel_ssc_resume NULL | 687 | # define atmel_ssc_resume NULL |
691 | #endif /* CONFIG_PM */ | 688 | #endif /* CONFIG_PM */ |
692 | 689 | ||
690 | static int atmel_ssc_probe(struct snd_soc_dai *dai) | ||
691 | { | ||
692 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; | ||
693 | int ret = 0; | ||
694 | |||
695 | snd_soc_dai_set_drvdata(dai, ssc_p); | ||
696 | |||
697 | /* | ||
698 | * Request SSC device | ||
699 | */ | ||
700 | ssc_p->ssc = ssc_request(dai->id); | ||
701 | if (IS_ERR(ssc_p->ssc)) { | ||
702 | printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id); | ||
703 | ret = PTR_ERR(ssc_p->ssc); | ||
704 | } | ||
705 | |||
706 | return ret; | ||
707 | } | ||
708 | |||
709 | static int atmel_ssc_remove(struct snd_soc_dai *dai) | ||
710 | { | ||
711 | struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai); | ||
712 | |||
713 | ssc_free(ssc_p->ssc); | ||
714 | return 0; | ||
715 | } | ||
693 | 716 | ||
694 | #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) | 717 | #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) |
695 | 718 | ||
@@ -705,9 +728,11 @@ static struct snd_soc_dai_ops atmel_ssc_dai_ops = { | |||
705 | .set_clkdiv = atmel_ssc_set_dai_clkdiv, | 728 | .set_clkdiv = atmel_ssc_set_dai_clkdiv, |
706 | }; | 729 | }; |
707 | 730 | ||
708 | struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { | 731 | static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { |
709 | { .name = "atmel-ssc0", | 732 | { |
710 | .id = 0, | 733 | .name = "atmel-ssc-dai.0", |
734 | .probe = atmel_ssc_probe, | ||
735 | .remove = atmel_ssc_remove, | ||
711 | .suspend = atmel_ssc_suspend, | 736 | .suspend = atmel_ssc_suspend, |
712 | .resume = atmel_ssc_resume, | 737 | .resume = atmel_ssc_resume, |
713 | .playback = { | 738 | .playback = { |
@@ -721,11 +746,12 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { | |||
721 | .rates = ATMEL_SSC_RATES, | 746 | .rates = ATMEL_SSC_RATES, |
722 | .formats = ATMEL_SSC_FORMATS,}, | 747 | .formats = ATMEL_SSC_FORMATS,}, |
723 | .ops = &atmel_ssc_dai_ops, | 748 | .ops = &atmel_ssc_dai_ops, |
724 | .private_data = &ssc_info[0], | ||
725 | }, | 749 | }, |
726 | #if NUM_SSC_DEVICES == 3 | 750 | #if NUM_SSC_DEVICES == 3 |
727 | { .name = "atmel-ssc1", | 751 | { |
728 | .id = 1, | 752 | .name = "atmel-ssc-dai.1", |
753 | .probe = atmel_ssc_probe, | ||
754 | .remove = atmel_ssc_remove, | ||
729 | .suspend = atmel_ssc_suspend, | 755 | .suspend = atmel_ssc_suspend, |
730 | .resume = atmel_ssc_resume, | 756 | .resume = atmel_ssc_resume, |
731 | .playback = { | 757 | .playback = { |
@@ -739,10 +765,11 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { | |||
739 | .rates = ATMEL_SSC_RATES, | 765 | .rates = ATMEL_SSC_RATES, |
740 | .formats = ATMEL_SSC_FORMATS,}, | 766 | .formats = ATMEL_SSC_FORMATS,}, |
741 | .ops = &atmel_ssc_dai_ops, | 767 | .ops = &atmel_ssc_dai_ops, |
742 | .private_data = &ssc_info[1], | ||
743 | }, | 768 | }, |
744 | { .name = "atmel-ssc2", | 769 | { |
745 | .id = 2, | 770 | .name = "atmel-ssc-dai.2", |
771 | .probe = atmel_ssc_probe, | ||
772 | .remove = atmel_ssc_remove, | ||
746 | .suspend = atmel_ssc_suspend, | 773 | .suspend = atmel_ssc_suspend, |
747 | .resume = atmel_ssc_resume, | 774 | .resume = atmel_ssc_resume, |
748 | .playback = { | 775 | .playback = { |
@@ -756,23 +783,94 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { | |||
756 | .rates = ATMEL_SSC_RATES, | 783 | .rates = ATMEL_SSC_RATES, |
757 | .formats = ATMEL_SSC_FORMATS,}, | 784 | .formats = ATMEL_SSC_FORMATS,}, |
758 | .ops = &atmel_ssc_dai_ops, | 785 | .ops = &atmel_ssc_dai_ops, |
759 | .private_data = &ssc_info[2], | ||
760 | }, | 786 | }, |
761 | #endif | 787 | #endif |
762 | }; | 788 | }; |
763 | EXPORT_SYMBOL_GPL(atmel_ssc_dai); | ||
764 | 789 | ||
765 | static int __init atmel_ssc_modinit(void) | 790 | static __devinit int asoc_ssc_probe(struct platform_device *pdev) |
791 | { | ||
792 | BUG_ON(pdev->id < 0); | ||
793 | BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai)); | ||
794 | return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]); | ||
795 | } | ||
796 | |||
797 | static int __devexit asoc_ssc_remove(struct platform_device *pdev) | ||
798 | { | ||
799 | snd_soc_unregister_dai(&pdev->dev); | ||
800 | return 0; | ||
801 | } | ||
802 | |||
803 | static struct platform_driver asoc_ssc_driver = { | ||
804 | .driver = { | ||
805 | .name = "atmel-ssc-dai", | ||
806 | .owner = THIS_MODULE, | ||
807 | }, | ||
808 | |||
809 | .probe = asoc_ssc_probe, | ||
810 | .remove = __devexit_p(asoc_ssc_remove), | ||
811 | }; | ||
812 | |||
813 | /** | ||
814 | * atmel_ssc_set_audio - Allocate the specified SSC for audio use. | ||
815 | */ | ||
816 | int atmel_ssc_set_audio(int ssc_id) | ||
817 | { | ||
818 | struct ssc_device *ssc; | ||
819 | static struct platform_device *dma_pdev; | ||
820 | struct platform_device *ssc_pdev; | ||
821 | int ret; | ||
822 | |||
823 | if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai)) | ||
824 | return -EINVAL; | ||
825 | |||
826 | /* Allocate a dummy device for DMA if we don't have one already */ | ||
827 | if (!dma_pdev) { | ||
828 | dma_pdev = platform_device_alloc("atmel-pcm-audio", -1); | ||
829 | if (!dma_pdev) | ||
830 | return -ENOMEM; | ||
831 | |||
832 | ret = platform_device_add(dma_pdev); | ||
833 | if (ret < 0) { | ||
834 | platform_device_put(dma_pdev); | ||
835 | dma_pdev = NULL; | ||
836 | return ret; | ||
837 | } | ||
838 | } | ||
839 | |||
840 | ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id); | ||
841 | if (!ssc_pdev) { | ||
842 | ssc_free(ssc); | ||
843 | return -ENOMEM; | ||
844 | } | ||
845 | |||
846 | /* If we can grab the SSC briefly to parent the DAI device off it */ | ||
847 | ssc = ssc_request(ssc_id); | ||
848 | if (IS_ERR(ssc)) | ||
849 | pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n", | ||
850 | PTR_ERR(ssc)); | ||
851 | else | ||
852 | ssc_pdev->dev.parent = &(ssc->pdev->dev); | ||
853 | ssc_free(ssc); | ||
854 | |||
855 | ret = platform_device_add(ssc_pdev); | ||
856 | if (ret < 0) | ||
857 | platform_device_put(ssc_pdev); | ||
858 | |||
859 | return ret; | ||
860 | } | ||
861 | EXPORT_SYMBOL_GPL(atmel_ssc_set_audio); | ||
862 | |||
863 | static int __init snd_atmel_ssc_init(void) | ||
766 | { | 864 | { |
767 | return snd_soc_register_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai)); | 865 | return platform_driver_register(&asoc_ssc_driver); |
768 | } | 866 | } |
769 | module_init(atmel_ssc_modinit); | 867 | module_init(snd_atmel_ssc_init); |
770 | 868 | ||
771 | static void __exit atmel_ssc_modexit(void) | 869 | static void __exit snd_atmel_ssc_exit(void) |
772 | { | 870 | { |
773 | snd_soc_unregister_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai)); | 871 | platform_driver_unregister(&asoc_ssc_driver); |
774 | } | 872 | } |
775 | module_exit(atmel_ssc_modexit); | 873 | module_exit(snd_atmel_ssc_exit); |
776 | 874 | ||
777 | /* Module information */ | 875 | /* Module information */ |
778 | MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); | 876 | MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); |