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 c85844d4845..5d230cee3fa 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) static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; + struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; int dir_mask; pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", @@ -235,8 +234,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; + struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; @@ -338,7 +336,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - int id = rtd->dai->cpu_dai->id; + int id = dai->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; @@ -368,7 +366,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, * function. It should not be used for other purposes * as it is common to all substreams. */ - snd_soc_dai_set_dma_data(rtd->dai->cpu_dai, substream, dma_params); + snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params); channels = params_channels(params); @@ -605,8 +603,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, static int atmel_ssc_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; + struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct atmel_pcm_dma_params *dma_params; int dir; @@ -690,6 +687,32 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ +static int atmel_ssc_probe(struct snd_soc_dai *dai) +{ + struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + int ret = 0; + + snd_soc_dai_set_drvdata(dai, ssc_p); + + /* + * Request SSC device + */ + ssc_p->ssc = ssc_request(dai->id); + if (IS_ERR(ssc_p->ssc)) { + printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id); + ret = PTR_ERR(ssc_p->ssc); + } + + return ret; +} + +static int atmel_ssc_remove(struct snd_soc_dai *dai) +{ + struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai); + + ssc_free(ssc_p->ssc); + return 0; +} #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) @@ -705,9 +728,11 @@ static struct snd_soc_dai_ops atmel_ssc_dai_ops = { .set_clkdiv = atmel_ssc_set_dai_clkdiv, }; -struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { - { .name = "atmel-ssc0", - .id = 0, +static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { + { + .name = "atmel-ssc-dai.0", + .probe = atmel_ssc_probe, + .remove = atmel_ssc_remove, .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { @@ -721,11 +746,12 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { .rates = ATMEL_SSC_RATES, .formats = ATMEL_SSC_FORMATS,}, .ops = &atmel_ssc_dai_ops, - .private_data = &ssc_info[0], }, #if NUM_SSC_DEVICES == 3 - { .name = "atmel-ssc1", - .id = 1, + { + .name = "atmel-ssc-dai.1", + .probe = atmel_ssc_probe, + .remove = atmel_ssc_remove, .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { @@ -739,10 +765,11 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { .rates = ATMEL_SSC_RATES, .formats = ATMEL_SSC_FORMATS,}, .ops = &atmel_ssc_dai_ops, - .private_data = &ssc_info[1], }, - { .name = "atmel-ssc2", - .id = 2, + { + .name = "atmel-ssc-dai.2", + .probe = atmel_ssc_probe, + .remove = atmel_ssc_remove, .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { @@ -756,23 +783,94 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = { .rates = ATMEL_SSC_RATES, .formats = ATMEL_SSC_FORMATS,}, .ops = &atmel_ssc_dai_ops, - .private_data = &ssc_info[2], }, #endif }; -EXPORT_SYMBOL_GPL(atmel_ssc_dai); -static int __init atmel_ssc_modinit(void) +static __devinit int asoc_ssc_probe(struct platform_device *pdev) +{ + BUG_ON(pdev->id < 0); + BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai)); + return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]); +} + +static int __devexit asoc_ssc_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&pdev->dev); + return 0; +} + +static struct platform_driver asoc_ssc_driver = { + .driver = { + .name = "atmel-ssc-dai", + .owner = THIS_MODULE, + }, + + .probe = asoc_ssc_probe, + .remove = __devexit_p(asoc_ssc_remove), +}; + +/** + * atmel_ssc_set_audio - Allocate the specified SSC for audio use. + */ +int atmel_ssc_set_audio(int ssc_id) +{ + struct ssc_device *ssc; + static struct platform_device *dma_pdev; + struct platform_device *ssc_pdev; + int ret; + + if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai)) + return -EINVAL; + + /* Allocate a dummy device for DMA if we don't have one already */ + if (!dma_pdev) { + dma_pdev = platform_device_alloc("atmel-pcm-audio", -1); + if (!dma_pdev) + return -ENOMEM; + + ret = platform_device_add(dma_pdev); + if (ret < 0) { + platform_device_put(dma_pdev); + dma_pdev = NULL; + return ret; + } + } + + ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id); + if (!ssc_pdev) { + ssc_free(ssc); + return -ENOMEM; + } + + /* If we can grab the SSC briefly to parent the DAI device off it */ + ssc = ssc_request(ssc_id); + if (IS_ERR(ssc)) + pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n", + PTR_ERR(ssc)); + else + ssc_pdev->dev.parent = &(ssc->pdev->dev); + ssc_free(ssc); + + ret = platform_device_add(ssc_pdev); + if (ret < 0) + platform_device_put(ssc_pdev); + + return ret; +} +EXPORT_SYMBOL_GPL(atmel_ssc_set_audio); + +static int __init snd_atmel_ssc_init(void) { - return snd_soc_register_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai)); + return platform_driver_register(&asoc_ssc_driver); } -module_init(atmel_ssc_modinit); +module_init(snd_atmel_ssc_init); -static void __exit atmel_ssc_modexit(void) +static void __exit snd_atmel_ssc_exit(void) { - snd_soc_unregister_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai)); + platform_driver_unregister(&asoc_ssc_driver); } -module_exit(atmel_ssc_modexit); +module_exit(snd_atmel_ssc_exit); /* Module information */ MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); |