From f0fba2ad1b6b53d5360125c41953b7afcd6deff0 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 17 Mar 2010 20:15:21 +0000 Subject: ASoC: multi-component - ASoC Multi-Component Support This patch extends the ASoC API to allow sound cards to have more than one CODEC and more than one platform DMA controller. This is achieved by dividing some current ASoC structures that contain both driver data and device data into structures that only either contain device data or driver data. i.e. struct snd_soc_codec ---> struct snd_soc_codec (device data) +-> struct snd_soc_codec_driver (driver data) struct snd_soc_platform ---> struct snd_soc_platform (device data) +-> struct snd_soc_platform_driver (driver data) struct snd_soc_dai ---> struct snd_soc_dai (device data) +-> struct snd_soc_dai_driver (driver data) struct snd_soc_device ---> deleted This now allows ASoC to be more tightly aligned with the Linux driver model and also means that every ASoC codec, platform and (platform) DAI is a kernel device. ASoC component private data is now stored as device private data. The ASoC sound card struct snd_soc_card has also been updated to store lists of it's components rather than a pointer to a codec and platform. The PCM runtime struct soc_pcm_runtime now has pointers to all its components. This patch adds DAPM support for ASoC multi-component and removes struct snd_soc_socdev from DAPM core. All DAPM calls are now made on a card, codec or runtime PCM level basis rather than using snd_soc_socdev. Other notable multi-component changes:- * Stream operations now de-reference less structures. * close_delayed work() now runs on a DAI basis rather than looping all DAIs in a card. * PM suspend()/resume() operations can now handle N CODECs and Platforms per sound card. * Added soc_bind_dai_link() to bind the component devices to the sound card. * Added soc_dai_link_probe() and soc_dai_link_remove() to probe and remove DAI link components. * sysfs entries can now be registered per component per card. * snd_soc_new_pcms() functionailty rolled into dai_link_probe(). * snd_soc_register_codec() now does all the codec list and mutex init. This patch changes the probe() and remove() of the CODEC drivers as follows:- o Make CODEC driver a platform driver o Moved all struct snd_soc_codec list, mutex, etc initialiasation to core. o Removed all static codec pointers (drivers now support > 1 codec dev) o snd_soc_register_pcms() now done by core. o snd_soc_register_dai() folded into snd_soc_register_codec(). CS4270 portions: Acked-by: Timur Tabi Some TLV320aic23 and Cirrus platform fixes. Signed-off-by: Ryan Mallon TI CODEC and OMAP fixes Signed-off-by: Peter Ujfalusi Signed-off-by: Janusz Krzysztofik Signed-off-by: Jarkko Nikula Samsung platform and misc fixes :- Signed-off-by: Chanwoo Choi Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Reviewed-by: Jassi Brar Signed-off-by: Seungwhan Youn MPC8610 and PPC fixes. Signed-off-by: Timur Tabi i.MX fixes and some core fixes. Signed-off-by: Sascha Hauer J4740 platform fixes:- Signed-off-by: Lars-Peter Clausen CC: Tony Lindgren CC: Nicolas Ferre CC: Kevin Hilman CC: Sascha Hauer CC: Atsushi Nemoto CC: Kuninori Morimoto CC: Daniel Gloeckner CC: Manuel Lauss CC: Mike Frysinger CC: Arnaud Patard CC: Wan ZongShun Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/txx9/txx9aclc-ac97.c | 55 ++++++--------- sound/soc/txx9/txx9aclc-generic.c | 24 ++----- sound/soc/txx9/txx9aclc.c | 141 +++++++++++++++++++++----------------- sound/soc/txx9/txx9aclc.h | 13 +--- 4 files changed, 109 insertions(+), 124 deletions(-) (limited to 'sound/soc/txx9') diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index 0ec20b68e8c..743d07b82c0 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -36,13 +36,11 @@ static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq); -/* REVISIT: How to find txx9aclc_soc_device from snd_ac97? */ -static struct txx9aclc_soc_device *txx9aclc_soc_dev; +/* REVISIT: How to find txx9aclc_drvdata from snd_ac97? */ +static struct txx9aclc_plat_drvdata *txx9aclc_drvdata; -static int txx9aclc_regready(struct txx9aclc_soc_device *dev) +static int txx9aclc_regready(struct txx9aclc_plat_drvdata *drvdata) { - struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); - return __raw_readl(drvdata->base + ACINTSTS) & ACINT_REGACCRDY; } @@ -50,8 +48,7 @@ static int txx9aclc_regready(struct txx9aclc_soc_device *dev) static unsigned short txx9aclc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { - struct txx9aclc_soc_device *dev = txx9aclc_soc_dev; - struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); + struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata; void __iomem *base = drvdata->base; u32 dat; @@ -61,15 +58,15 @@ static unsigned short txx9aclc_ac97_read(struct snd_ac97 *ac97, dat = (reg << ACREGACC_REG_SHIFT) | ACREGACC_READ; __raw_writel(dat, base + ACREGACC); __raw_writel(ACINT_REGACCRDY, base + ACINTEN); - if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) { + if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(txx9aclc_drvdata), HZ)) { __raw_writel(ACINT_REGACCRDY, base + ACINTDIS); - dev_err(dev->soc_dev.dev, "ac97 read timeout (reg %#x)\n", reg); + printk(KERN_ERR "ac97 read timeout (reg %#x)\n", reg); dat = 0xffff; goto done; } dat = __raw_readl(base + ACREGACC); if (((dat >> ACREGACC_REG_SHIFT) & 0xff) != reg) { - dev_err(dev->soc_dev.dev, "reg mismatch %x with %x\n", + printk(KERN_ERR "reg mismatch %x with %x\n", dat, reg); dat = 0xffff; goto done; @@ -84,16 +81,15 @@ done: static void txx9aclc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { - struct txx9aclc_soc_device *dev = txx9aclc_soc_dev; - struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); + struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata; void __iomem *base = drvdata->base; __raw_writel(((reg | (ac97->num << 7)) << ACREGACC_REG_SHIFT) | (val << ACREGACC_DAT_SHIFT), base + ACREGACC); __raw_writel(ACINT_REGACCRDY, base + ACINTEN); - if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) { - dev_err(dev->soc_dev.dev, + if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(txx9aclc_drvdata), HZ)) { + printk(KERN_ERR "ac97 write timeout (reg %#x)\n", reg); } __raw_writel(ACINT_REGACCRDY, base + ACINTDIS); @@ -101,8 +97,7 @@ static void txx9aclc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97) { - struct txx9aclc_soc_device *dev = txx9aclc_soc_dev; - struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); + struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata; void __iomem *base = drvdata->base; u32 ready = ACINT_CODECRDY(ac97->num) | ACINT_REGACCRDY; @@ -141,31 +136,23 @@ static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int txx9aclc_ac97_probe(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int txx9aclc_ac97_probe(struct snd_soc_dai *dai) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct txx9aclc_soc_device *dev = - container_of(socdev, struct txx9aclc_soc_device, soc_dev); - - dev->aclc_pdev = to_platform_device(dai->dev); - txx9aclc_soc_dev = dev; + txx9aclc_drvdata = snd_soc_dai_get_drvdata(dai); return 0; } -static void txx9aclc_ac97_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int txx9aclc_ac97_remove(struct snd_soc_dai *dai) { - struct platform_device *aclc_pdev = to_platform_device(dai->dev); - struct txx9aclc_plat_drvdata *drvdata = platform_get_drvdata(aclc_pdev); + struct txx9aclc_plat_drvdata *drvdata = snd_soc_dai_get_drvdata(dai); /* disable AC-link */ __raw_writel(ACCTL_ENLINK, drvdata->base + ACCTLDIS); - txx9aclc_soc_dev = NULL; + txx9aclc_drvdata = NULL; + return 0; } -struct snd_soc_dai txx9aclc_ac97_dai = { - .name = "txx9aclc_ac97", +static struct snd_soc_dai_driver txx9aclc_ac97_dai = { .ac97_control = 1, .probe = txx9aclc_ac97_probe, .remove = txx9aclc_ac97_remove, @@ -182,7 +169,6 @@ struct snd_soc_dai txx9aclc_ac97_dai = { .channels_max = 2, }, }; -EXPORT_SYMBOL_GPL(txx9aclc_ac97_dai); static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev) { @@ -219,13 +205,12 @@ static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev) if (err < 0) return err; - txx9aclc_ac97_dai.dev = &pdev->dev; - return snd_soc_register_dai(&txx9aclc_ac97_dai); + return snd_soc_register_dai(&pdev->dev, &txx9aclc_ac97_dai); } static int __devexit txx9aclc_ac97_dev_remove(struct platform_device *pdev) { - snd_soc_unregister_dai(&txx9aclc_ac97_dai); + snd_soc_unregister_dai(&pdev->dev); return 0; } diff --git a/sound/soc/txx9/txx9aclc-generic.c b/sound/soc/txx9/txx9aclc-generic.c index 95b17f731ae..6770e7166be 100644 --- a/sound/soc/txx9/txx9aclc-generic.c +++ b/sound/soc/txx9/txx9aclc-generic.c @@ -19,54 +19,44 @@ #include #include #include -#include "../codecs/ac97.h" #include "txx9aclc.h" static struct snd_soc_dai_link txx9aclc_generic_dai = { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai = &txx9aclc_ac97_dai, - .codec_dai = &ac97_dai, + .cpu_dai_name = "txx9aclc-ac97", + .codec_dai_name = "ac97-hifi", + .platform_name = "txx9aclc-pcm-audio", + .codec_name = "ac97-codec", }; static struct snd_soc_card txx9aclc_generic_card = { .name = "Generic TXx9 ACLC Audio", - .platform = &txx9aclc_soc_platform, .dai_link = &txx9aclc_generic_dai, .num_links = 1, }; -static struct txx9aclc_soc_device txx9aclc_generic_soc_device = { - .soc_dev = { - .card = &txx9aclc_generic_card, - .codec_dev = &soc_codec_dev_ac97, - }, -}; +static struct platform_device *soc_pdev; static int __init txx9aclc_generic_probe(struct platform_device *pdev) { - struct txx9aclc_soc_device *dev = &txx9aclc_generic_soc_device; - struct platform_device *soc_pdev; int ret; soc_pdev = platform_device_alloc("soc-audio", -1); if (!soc_pdev) return -ENOMEM; - platform_set_drvdata(soc_pdev, &dev->soc_dev); - dev->soc_dev.dev = &soc_pdev->dev; + platform_set_drvdata(soc_pdev, &txx9aclc_generic_card); ret = platform_device_add(soc_pdev); if (ret) { platform_device_put(soc_pdev); return ret; } - platform_set_drvdata(pdev, soc_pdev); + return 0; } static int __exit txx9aclc_generic_remove(struct platform_device *pdev) { - struct platform_device *soc_pdev = platform_get_drvdata(pdev); - platform_device_unregister(soc_pdev); return 0; } diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 0e3452303ea..f4aa4e03c88 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -22,6 +22,16 @@ #include #include "txx9aclc.h" +static struct txx9aclc_soc_device { + struct txx9aclc_dmadata dmadata[2]; +} txx9aclc_soc_device; + +/* REVISIT: How to find txx9aclc_drvdata from snd_ac97? */ +static struct txx9aclc_plat_drvdata *txx9aclc_drvdata; + +static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev, + struct txx9aclc_dmadata *dmadata); + static const struct snd_pcm_hardware txx9aclc_pcm_hardware = { /* * REVISIT: SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID @@ -46,7 +56,6 @@ static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_soc_device *socdev = rtd->socdev; struct snd_pcm_runtime *runtime = substream->runtime; struct txx9aclc_dmadata *dmadata = runtime->private_data; int ret; @@ -55,13 +64,13 @@ static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - dev_dbg(socdev->dev, + dev_dbg(rtd->platform->dev, "runtime->dma_area = %#lx dma_addr = %#lx dma_bytes = %zd " "runtime->min_align %ld\n", (unsigned long)runtime->dma_area, (unsigned long)runtime->dma_addr, runtime->dma_bytes, runtime->min_align); - dev_dbg(socdev->dev, + dev_dbg(rtd->platform->dev, "periods %d period_bytes %d stream %d\n", params_periods(params), params_period_bytes(params), substream->stream); @@ -152,11 +161,7 @@ static void txx9aclc_dma_tasklet(unsigned long data) spin_lock_irqsave(&dmadata->dma_lock, flags); if (dmadata->frag_count < 0) { - struct txx9aclc_soc_device *dev = - container_of(dmadata, struct txx9aclc_soc_device, - dmadata[substream->stream]); - struct txx9aclc_plat_drvdata *drvdata = - txx9aclc_get_plat_drvdata(dev); + struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata; void __iomem *base = drvdata->base; spin_unlock_irqrestore(&dmadata->dma_lock, flags); @@ -202,10 +207,7 @@ static void txx9aclc_dma_tasklet(unsigned long data) static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct txx9aclc_dmadata *dmadata = substream->runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct txx9aclc_soc_device *dev = - container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev); - struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); + struct txx9aclc_plat_drvdata *drvdata =txx9aclc_drvdata; void __iomem *base = drvdata->base; unsigned long flags; int ret = 0; @@ -244,9 +246,7 @@ txx9aclc_pcm_pointer(struct snd_pcm_substream *substream) static int txx9aclc_pcm_open(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct txx9aclc_soc_device *dev = - container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev); + struct txx9aclc_soc_device *dev = &txx9aclc_soc_device; struct txx9aclc_dmadata *dmadata = &dev->dmadata[substream->stream]; int ret; @@ -291,8 +291,38 @@ static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm) static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { + struct platform_device *pdev = to_platform_device(dai->platform->dev); + struct txx9aclc_soc_device *dev; + struct resource *r; + int i; + int ret; + + /* at this point onwards the AC97 component has probed and this will be valid */ + dev = snd_soc_dai_get_drvdata(dai); + + dev->dmadata[0].stream = SNDRV_PCM_STREAM_PLAYBACK; + dev->dmadata[1].stream = SNDRV_PCM_STREAM_CAPTURE; + for (i = 0; i < 2; i++) { + r = platform_get_resource(pdev, IORESOURCE_DMA, i); + if (!r) { + ret = -EBUSY; + goto exit; + } + dev->dmadata[i].dma_res = r; + ret = txx9aclc_dma_init(dev, &dev->dmadata[i]); + if (ret) + goto exit; + } return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, 64 * 1024, 4 * 1024 * 1024); + +exit: + for (i = 0; i < 2; i++) { + if (dev->dmadata[i].dma_chan) + dma_release_channel(dev->dmadata[i].dma_chan); + dev->dmadata[i].dma_chan = NULL; + } + return ret; } static bool filter(struct dma_chan *chan, void *param) @@ -314,7 +344,7 @@ static bool filter(struct dma_chan *chan, void *param) static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev, struct txx9aclc_dmadata *dmadata) { - struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); + struct txx9aclc_plat_drvdata *drvdata =txx9aclc_drvdata; struct txx9dmac_slave *ds = &dmadata->dma_slave; dma_cap_mask_t mask; @@ -334,7 +364,7 @@ static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev, dma_cap_set(DMA_SLAVE, mask); dmadata->dma_chan = dma_request_channel(mask, filter, dmadata); if (!dmadata->dma_chan) { - dev_err(dev->soc_dev.dev, + printk(KERN_ERR "DMA channel for %s is not available\n", dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); @@ -345,45 +375,16 @@ static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev, return 0; } -static int txx9aclc_pcm_probe(struct platform_device *pdev) +static int txx9aclc_pcm_probe(struct snd_soc_platform *platform) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct txx9aclc_soc_device *dev = - container_of(socdev, struct txx9aclc_soc_device, soc_dev); - struct resource *r; - int i; - int ret; - - dev->dmadata[0].stream = SNDRV_PCM_STREAM_PLAYBACK; - dev->dmadata[1].stream = SNDRV_PCM_STREAM_CAPTURE; - for (i = 0; i < 2; i++) { - r = platform_get_resource(dev->aclc_pdev, IORESOURCE_DMA, i); - if (!r) { - ret = -EBUSY; - goto exit; - } - dev->dmadata[i].dma_res = r; - ret = txx9aclc_dma_init(dev, &dev->dmadata[i]); - if (ret) - goto exit; - } + snd_soc_platform_set_drvdata(platform, &txx9aclc_soc_device); return 0; - -exit: - for (i = 0; i < 2; i++) { - if (dev->dmadata[i].dma_chan) - dma_release_channel(dev->dmadata[i].dma_chan); - dev->dmadata[i].dma_chan = NULL; - } - return ret; } -static int txx9aclc_pcm_remove(struct platform_device *pdev) +static int txx9aclc_pcm_remove(struct snd_soc_platform *platform) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct txx9aclc_soc_device *dev = - container_of(socdev, struct txx9aclc_soc_device, soc_dev); - struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); + struct txx9aclc_soc_device *dev = snd_soc_platform_get_drvdata(platform); + struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata; void __iomem *base = drvdata->base; int i; @@ -406,28 +407,46 @@ static int txx9aclc_pcm_remove(struct platform_device *pdev) return 0; } -struct snd_soc_platform txx9aclc_soc_platform = { - .name = "txx9aclc-audio", +static struct snd_soc_platform_driver txx9aclc_soc_platform = { .probe = txx9aclc_pcm_probe, .remove = txx9aclc_pcm_remove, - .pcm_ops = &txx9aclc_pcm_ops, + .ops = &txx9aclc_pcm_ops, .pcm_new = txx9aclc_pcm_new, .pcm_free = txx9aclc_pcm_free_dma_buffers, }; -EXPORT_SYMBOL_GPL(txx9aclc_soc_platform); -static int __init txx9aclc_soc_platform_init(void) +static int __devinit txx9aclc_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&txx9aclc_soc_platform); + return snd_soc_register_platform(&pdev->dev, &txx9aclc_soc_platform); } -static void __exit txx9aclc_soc_platform_exit(void) +static int __devexit txx9aclc_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&txx9aclc_soc_platform); + snd_soc_unregister_platform(&pdev->dev); + return 0; } -module_init(txx9aclc_soc_platform_init); -module_exit(txx9aclc_soc_platform_exit); +static struct platform_driver txx9aclc_pcm_driver = { + .driver = { + .name = "txx9aclc-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = txx9aclc_soc_platform_probe, + .remove = __devexit_p(txx9aclc_soc_platform_remove), +}; + +static int __init snd_txx9aclc_pcm_init(void) +{ + return platform_driver_register(&txx9aclc_pcm_driver); +} +module_init(snd_txx9aclc_pcm_init); + +static void __exit snd_txx9aclc_pcm_exit(void) +{ + platform_driver_unregister(&txx9aclc_pcm_driver); +} +module_exit(snd_txx9aclc_pcm_exit); MODULE_AUTHOR("Atsushi Nemoto "); MODULE_DESCRIPTION("TXx9 ACLC Audio DMA driver"); diff --git a/sound/soc/txx9/txx9aclc.h b/sound/soc/txx9/txx9aclc.h index 6769aab41b3..9c2de84fec3 100644 --- a/sound/soc/txx9/txx9aclc.h +++ b/sound/soc/txx9/txx9aclc.h @@ -65,19 +65,10 @@ struct txx9aclc_plat_drvdata { u64 physbase; }; -struct txx9aclc_soc_device { - struct snd_soc_device soc_dev; - struct platform_device *aclc_pdev; /* for ioresources, drvdata */ - struct txx9aclc_dmadata dmadata[2]; -}; - static inline struct txx9aclc_plat_drvdata *txx9aclc_get_plat_drvdata( - struct txx9aclc_soc_device *sdev) + struct snd_soc_dai *dai) { - return platform_get_drvdata(sdev->aclc_pdev); + return dev_get_drvdata(dai->dev); } -extern struct snd_soc_platform txx9aclc_soc_platform; -extern struct snd_soc_dai txx9aclc_ac97_dai; - #endif /* __TXX9ACLC_H */ -- cgit v1.2.3