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/fsl/Kconfig | 3 - sound/soc/fsl/Makefile | 3 - sound/soc/fsl/efika-audio-fabric.c | 20 +- sound/soc/fsl/fsl_dma.c | 312 ++++++++++------- sound/soc/fsl/fsl_dma.h | 20 -- sound/soc/fsl/fsl_ssi.c | 249 ++++++++------ sound/soc/fsl/fsl_ssi.h | 26 -- sound/soc/fsl/mpc5200_dma.c | 66 ++-- sound/soc/fsl/mpc5200_dma.h | 5 - sound/soc/fsl/mpc5200_psc_ac97.c | 34 +- sound/soc/fsl/mpc5200_psc_ac97.h | 2 - sound/soc/fsl/mpc5200_psc_i2s.c | 19 +- sound/soc/fsl/mpc8610_hpcd.c | 658 +++++++++++++++++------------------- sound/soc/fsl/pcm030-audio-fabric.c | 21 +- sound/soc/fsl/soc-of-simple.c | 172 ---------- 15 files changed, 728 insertions(+), 882 deletions(-) delete mode 100644 sound/soc/fsl/soc-of-simple.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8cb65ccad35f..981868700388 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,6 +1,3 @@ -config SND_SOC_OF_SIMPLE - tristate - config SND_MPC52xx_DMA tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index a83a73967ec6..7e472a53fcd3 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -1,6 +1,3 @@ -# Simple machine driver that extracts configuration from the OF device tree -obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o - # MPC8610 HPCD Machine Support snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index 1a5b8e0d6a34..53251e6b5bd5 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "mpc5200_dma.h" #include "mpc5200_psc_ac97.h" @@ -32,21 +31,24 @@ #define DRV_NAME "efika-audio-fabric" -static struct snd_soc_device device; static struct snd_soc_card card; static struct snd_soc_dai_link efika_fabric_dai[] = { { .name = "AC97", .stream_name = "AC97 Analog", - .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_ANALOG], - .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL], + .codec_dai_name = "stac9766-hifi-analog", + .cpu_dai_name = "mpc5200-psc-ac97.0", + .platform_name = "mpc5200-pcm-audio", + .codec_name = "stac9766-codec", }, { .name = "AC97", .stream_name = "AC97 IEC958", - .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_DIGITAL], - .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF], + .codec_dai_name = "stac9766-hifi-IEC958", + .cpu_dai_name = "mpc5200-psc-ac97.1", + .platform_name = "mpc5200-pcm-audio", + .codec_name = "stac9766-codec", }, }; @@ -58,13 +60,10 @@ static __init int efika_fabric_init(void) if (!of_machine_is_compatible("bplan,efika")) return -ENODEV; - card.platform = &mpc5200_audio_dma_platform; card.name = "Efika"; card.dai_link = efika_fabric_dai; card.num_links = ARRAY_SIZE(efika_fabric_dai); - device.card = &card; - device.codec_dev = &soc_codec_dev_stac9766; pdev = platform_device_alloc("soc-audio", 1); if (!pdev) { @@ -72,8 +71,7 @@ static __init int efika_fabric_init(void) return -ENODEV; } - platform_set_drvdata(pdev, &device); - device.dev = &pdev->dev; + platform_set_drvdata(pdev, &card); rc = platform_device_add(pdev); if (rc) { diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 410c7496a18d..d09e1941b1fa 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -3,10 +3,11 @@ * * Author: Timur Tabi * - * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed - * under the terms of the GNU General Public License version 2. This - * program is licensed "as is" without any warranty of any kind, whether - * express or implied. + * Copyright 2007-2010 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. * * This driver implements ASoC support for the Elo DMA controller, which is * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms, @@ -20,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -29,6 +32,7 @@ #include #include "fsl_dma.h" +#include "fsl_ssi.h" /* For the offset of stx0 and srx0 */ /* * The formats that the DMA controller supports, which is anything @@ -52,26 +56,16 @@ #define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ SNDRV_PCM_RATE_CONTINUOUS) -/* DMA global data. This structure is used by fsl_dma_open() to determine - * which DMA channels to assign to a substream. Unfortunately, ASoC V1 does - * not allow the machine driver to provide this information to the PCM - * driver in advance, and there's no way to differentiate between the two - * DMA controllers. So for now, this driver only supports one SSI device - * using two DMA channels. We cannot support multiple DMA devices. - * - * ssi_stx_phys: bus address of SSI STX register - * ssi_srx_phys: bus address of SSI SRX register - * dma_channel: pointer to the DMA channel's registers - * irq: IRQ for this DMA channel - * assigned: set to 1 if that DMA channel is assigned to a substream - */ -static struct { +struct dma_object { + struct list_head list; + struct snd_soc_platform_driver dai; dma_addr_t ssi_stx_phys; dma_addr_t ssi_srx_phys; - struct ccsr_dma_channel __iomem *dma_channel[2]; - unsigned int irq[2]; - unsigned int assigned[2]; -} dma_global_data; + struct ccsr_dma_channel __iomem *channel; + unsigned int irq; + bool assigned; + char path[1]; +}; /* * The number of DMA links to use. Two is the bare minimum, but if you @@ -88,8 +82,6 @@ static struct { * structure. * * @link[]: array of link descriptors - * @controller_id: which DMA controller (0, 1, ...) - * @channel_id: which DMA channel on the controller (0, 1, 2, ...) * @dma_channel: pointer to the DMA channel's registers * @irq: IRQ for this DMA channel * @substream: pointer to the substream object, needed by the ISR @@ -104,8 +96,6 @@ static struct { */ struct fsl_dma_private { struct fsl_dma_link_descriptor link[NUM_DMA_LINKS]; - unsigned int controller_id; - unsigned int channel_id; struct ccsr_dma_channel __iomem *dma_channel; unsigned int irq; struct snd_pcm_substream *substream; @@ -212,6 +202,9 @@ static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) static irqreturn_t fsl_dma_isr(int irq, void *dev_id) { struct fsl_dma_private *dma_private = dev_id; + struct snd_pcm_substream *substream = dma_private->substream; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; irqreturn_t ret = IRQ_NONE; u32 sr, sr2 = 0; @@ -222,11 +215,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) sr = in_be32(&dma_channel->sr); if (sr & CCSR_DMA_SR_TE) { - dev_err(dma_private->substream->pcm->card->dev, - "DMA transmit error (controller=%u channel=%u irq=%u\n", - dma_private->controller_id, - dma_private->channel_id, irq); - fsl_dma_abort_stream(dma_private->substream); + dev_err(dev, "dma transmit error\n"); + fsl_dma_abort_stream(substream); sr2 |= CCSR_DMA_SR_TE; ret = IRQ_HANDLED; } @@ -235,11 +225,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) ret = IRQ_HANDLED; if (sr & CCSR_DMA_SR_PE) { - dev_err(dma_private->substream->pcm->card->dev, - "DMA%u programming error (channel=%u irq=%u)\n", - dma_private->controller_id, - dma_private->channel_id, irq); - fsl_dma_abort_stream(dma_private->substream); + dev_err(dev, "dma programming error\n"); + fsl_dma_abort_stream(substream); sr2 |= CCSR_DMA_SR_PE; ret = IRQ_HANDLED; } @@ -253,8 +240,6 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) ret = IRQ_HANDLED; if (sr & CCSR_DMA_SR_EOSI) { - struct snd_pcm_substream *substream = dma_private->substream; - /* Tell ALSA we completed a period. */ snd_pcm_period_elapsed(substream); @@ -305,10 +290,8 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, fsl_dma_hardware.buffer_bytes_max, &pcm->streams[0].substream->dma_buffer); if (ret) { - dev_err(card->dev, - "Can't allocate playback DMA buffer (size=%u)\n", - fsl_dma_hardware.buffer_bytes_max); - return -ENOMEM; + dev_err(card->dev, "can't allocate playback dma buffer\n"); + return ret; } ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, @@ -316,10 +299,8 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, &pcm->streams[1].substream->dma_buffer); if (ret) { snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); - dev_err(card->dev, - "Can't allocate capture DMA buffer (size=%u)\n", - fsl_dma_hardware.buffer_bytes_max); - return -ENOMEM; + dev_err(card->dev, "can't allocate capture dma buffer\n"); + return ret; } return 0; @@ -390,6 +371,10 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, static int fsl_dma_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; + struct dma_object *dma = + container_of(rtd->platform->driver, struct dma_object, dai); struct fsl_dma_private *dma_private; struct ccsr_dma_channel __iomem *dma_channel; dma_addr_t ld_buf_phys; @@ -407,52 +392,44 @@ static int fsl_dma_open(struct snd_pcm_substream *substream) ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { - dev_err(substream->pcm->card->dev, "invalid buffer size\n"); + dev_err(dev, "invalid buffer size\n"); return ret; } channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; - if (dma_global_data.assigned[channel]) { - dev_err(substream->pcm->card->dev, - "DMA channel already assigned\n"); + if (dma->assigned) { + dev_err(dev, "dma channel already assigned\n"); return -EBUSY; } - dma_private = dma_alloc_coherent(substream->pcm->card->dev, - sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL); + dma_private = dma_alloc_coherent(dev, sizeof(struct fsl_dma_private), + &ld_buf_phys, GFP_KERNEL); if (!dma_private) { - dev_err(substream->pcm->card->dev, - "can't allocate DMA private data\n"); + dev_err(dev, "can't allocate dma private data\n"); return -ENOMEM; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_private->ssi_sxx_phys = dma_global_data.ssi_stx_phys; + dma_private->ssi_sxx_phys = dma->ssi_stx_phys; else - dma_private->ssi_sxx_phys = dma_global_data.ssi_srx_phys; + dma_private->ssi_sxx_phys = dma->ssi_srx_phys; - dma_private->dma_channel = dma_global_data.dma_channel[channel]; - dma_private->irq = dma_global_data.irq[channel]; + dma_private->dma_channel = dma->channel; + dma_private->irq = dma->irq; dma_private->substream = substream; dma_private->ld_buf_phys = ld_buf_phys; dma_private->dma_buf_phys = substream->dma_buffer.addr; - /* We only support one DMA controller for now */ - dma_private->controller_id = 0; - dma_private->channel_id = channel; - ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private); if (ret) { - dev_err(substream->pcm->card->dev, - "can't register ISR for IRQ %u (ret=%i)\n", + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", dma_private->irq, ret); - dma_free_coherent(substream->pcm->card->dev, - sizeof(struct fsl_dma_private), + dma_free_coherent(dev, sizeof(struct fsl_dma_private), dma_private, dma_private->ld_buf_phys); return ret; } - dma_global_data.assigned[channel] = 1; + dma->assigned = 1; snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); @@ -546,6 +523,8 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; /* Number of bits per sample */ unsigned int sample_size = @@ -606,8 +585,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, break; default: /* We should never get here */ - dev_err(substream->pcm->card->dev, - "unsupported sample size %u\n", sample_size); + dev_err(dev, "unsupported sample size %u\n", sample_size); return -EINVAL; } @@ -689,6 +667,8 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; dma_addr_t position; snd_pcm_uframes_t frames; @@ -710,8 +690,7 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) if ((position < dma_private->dma_buf_phys) || (position > dma_private->dma_buf_end)) { - dev_err(substream->pcm->card->dev, - "dma pointer is out of range, halting stream\n"); + dev_err(dev, "dma pointer is out of range, halting stream\n"); return SNDRV_PCM_POS_XRUN; } @@ -772,26 +751,28 @@ static int fsl_dma_close(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; - int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; + struct dma_object *dma = + container_of(rtd->platform->driver, struct dma_object, dai); if (dma_private) { if (dma_private->irq) free_irq(dma_private->irq, dma_private); if (dma_private->ld_buf_phys) { - dma_unmap_single(substream->pcm->card->dev, - dma_private->ld_buf_phys, - sizeof(dma_private->link), DMA_TO_DEVICE); + dma_unmap_single(dev, dma_private->ld_buf_phys, + sizeof(dma_private->link), + DMA_TO_DEVICE); } /* Deallocate the fsl_dma_private structure */ - dma_free_coherent(substream->pcm->card->dev, - sizeof(struct fsl_dma_private), - dma_private, dma_private->ld_buf_phys); + dma_free_coherent(dev, sizeof(struct fsl_dma_private), + dma_private, dma_private->ld_buf_phys); substream->runtime->private_data = NULL; } - dma_global_data.assigned[dir] = 0; + dma->assigned = 0; return 0; } @@ -814,6 +795,40 @@ static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) } } +/* List of DMA nodes that we've probed */ +static LIST_HEAD(dma_list); + +/** + * find_ssi_node -- returns the SSI node that points to his DMA channel node + * + * Although this DMA driver attempts to operate independently of the other + * devices, it still needs to determine some information about the SSI device + * that it's working with. Unfortunately, the device tree does not contain + * a pointer from the DMA channel node to the SSI node -- the pointer goes the + * other way. So we need to scan the device tree for SSI nodes until we find + * the one that points to the given DMA channel node. It's ugly, but at least + * it's contained in this one function. + */ +static struct device_node *find_ssi_node(struct device_node *dma_channel_np) +{ + struct device_node *ssi_np, *np; + + for_each_compatible_node(ssi_np, NULL, "fsl,mpc8610-ssi") { + /* Check each DMA phandle to see if it points to us. We + * assume that device_node pointers are a valid comparison. + */ + np = of_parse_phandle(ssi_np, "fsl,playback-dma", 0); + if (np == dma_channel_np) + return ssi_np; + + np = of_parse_phandle(ssi_np, "fsl,capture-dma", 0); + if (np == dma_channel_np) + return ssi_np; + } + + return NULL; +} + static struct snd_pcm_ops fsl_dma_ops = { .open = fsl_dma_open, .close = fsl_dma_close, @@ -823,59 +838,112 @@ static struct snd_pcm_ops fsl_dma_ops = { .pointer = fsl_dma_pointer, }; -struct snd_soc_platform fsl_soc_platform = { - .name = "fsl-dma", - .pcm_ops = &fsl_dma_ops, - .pcm_new = fsl_dma_new, - .pcm_free = fsl_dma_free_dma_buffers, -}; -EXPORT_SYMBOL_GPL(fsl_soc_platform); +static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, + const struct of_device_id *match) + { + struct dma_object *dma; + struct device_node *np = of_dev->dev.of_node; + struct device_node *ssi_np; + struct resource res; + int ret; -/** - * fsl_dma_configure: store the DMA parameters from the fabric driver. - * - * This function is called by the ASoC fabric driver to give us the DMA and - * SSI channel information. - * - * Unfortunately, ASoC V1 does make it possible to determine the DMA/SSI - * data when a substream is created, so for now we need to store this data - * into a global variable. This means that we can only support one DMA - * controller, and hence only one SSI. - */ -int fsl_dma_configure(struct fsl_dma_info *dma_info) + /* Find the SSI node that points to us. */ + ssi_np = find_ssi_node(np); + if (!ssi_np) { + dev_err(&of_dev->dev, "cannot find parent SSI node\n"); + return -ENODEV; + } + + ret = of_address_to_resource(ssi_np, 0, &res); + of_node_put(ssi_np); + if (ret) { + dev_err(&of_dev->dev, "could not determine device resources\n"); + return ret; + } + + dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL); + if (!dma) { + dev_err(&of_dev->dev, "could not allocate dma object\n"); + return -ENOMEM; + } + + strcpy(dma->path, np->full_name); + dma->dai.ops = &fsl_dma_ops; + dma->dai.pcm_new = fsl_dma_new; + dma->dai.pcm_free = fsl_dma_free_dma_buffers; + + /* Store the SSI-specific information that we need */ + dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); + dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); + + ret = snd_soc_register_platform(&of_dev->dev, &dma->dai); + if (ret) { + dev_err(&of_dev->dev, "could not register platform\n"); + kfree(dma); + return ret; + } + + dma->channel = of_iomap(np, 0); + dma->irq = irq_of_parse_and_map(np, 0); + list_add(&dma->list, &dma_list); + + return 0; +} + +static int __devexit fsl_soc_dma_remove(struct of_device *of_dev) { - static int initialized; + struct list_head *n, *ptr; + struct dma_object *dma; - /* We only support one DMA controller for now */ - if (initialized) - return 0; + list_for_each_safe(ptr, n, &dma_list) { + dma = list_entry(ptr, struct dma_object, list); + list_del_init(ptr); + + snd_soc_unregister_platform(&of_dev->dev); + iounmap(dma->channel); + irq_dispose_mapping(dma->irq); + kfree(dma); + } - dma_global_data.ssi_stx_phys = dma_info->ssi_stx_phys; - dma_global_data.ssi_srx_phys = dma_info->ssi_srx_phys; - dma_global_data.dma_channel[0] = dma_info->dma_channel[0]; - dma_global_data.dma_channel[1] = dma_info->dma_channel[1]; - dma_global_data.irq[0] = dma_info->dma_irq[0]; - dma_global_data.irq[1] = dma_info->dma_irq[1]; - dma_global_data.assigned[0] = 0; - dma_global_data.assigned[1] = 0; - - initialized = 1; - return 1; + return 0; } -EXPORT_SYMBOL_GPL(fsl_dma_configure); -static int __init fsl_soc_platform_init(void) +static const struct of_device_id fsl_soc_dma_ids[] = { + { .compatible = "fsl,ssi-dma-channel", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_soc_dma_ids); + +static struct of_platform_driver fsl_soc_dma_driver = { + .driver = { + .name = "fsl-pcm-audio", + .owner = THIS_MODULE, + .of_match_table = fsl_soc_dma_ids, + }, + .probe = fsl_soc_dma_probe, + .remove = __devexit_p(fsl_soc_dma_remove), +}; + +static int __init fsl_soc_dma_init(void) { - return snd_soc_register_platform(&fsl_soc_platform); + pr_info("Freescale Elo DMA ASoC PCM Driver\n"); + + return of_register_platform_driver(&fsl_soc_dma_driver); } -module_init(fsl_soc_platform_init); -static void __exit fsl_soc_platform_exit(void) +static void __exit fsl_soc_dma_exit(void) { - snd_soc_unregister_platform(&fsl_soc_platform); + of_unregister_platform_driver(&fsl_soc_dma_driver); } -module_exit(fsl_soc_platform_exit); + +/* We want the DMA driver to be initialized before the SSI driver, so that + * when the SSI driver calls fsl_soc_dma_dai_from_node(), the DMA driver + * will already have been probed. The easiest way to do that is to make the + * __init function called via arch_initcall(). + */ +module_init(fsl_soc_dma_init); +module_exit(fsl_soc_dma_exit); MODULE_AUTHOR("Timur Tabi "); -MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module"); -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h index 385d4a42603c..78fee97e8036 100644 --- a/sound/soc/fsl/fsl_dma.h +++ b/sound/soc/fsl/fsl_dma.h @@ -126,24 +126,4 @@ struct fsl_dma_link_descriptor { u8 res[4]; /* Reserved */ } __attribute__ ((aligned(32), packed)); -/* DMA information needed to create a snd_soc_dai object - * - * ssi_stx_phys: bus address of SSI STX register to use - * ssi_srx_phys: bus address of SSI SRX register to use - * dma[0]: points to the DMA channel to use for playback - * dma[1]: points to the DMA channel to use for capture - * dma_irq[0]: IRQ of the DMA channel to use for playback - * dma_irq[1]: IRQ of the DMA channel to use for capture - */ -struct fsl_dma_info { - dma_addr_t ssi_stx_phys; - dma_addr_t ssi_srx_phys; - struct ccsr_dma_channel __iomem *dma_channel[2]; - unsigned int dma_irq[2]; -}; - -extern struct snd_soc_platform fsl_soc_platform; - -int fsl_dma_configure(struct fsl_dma_info *dma_info); - #endif diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 762c1b8e8e4e..64f65910a7d7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -3,10 +3,11 @@ * * Author: Timur Tabi * - * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed - * under the terms of the GNU General Public License version 2. This - * program is licensed "as is" without any warranty of any kind, whether - * express or implied. + * Copyright 2007-2010 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. */ #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include @@ -71,33 +73,31 @@ /** * fsl_ssi_private: per-SSI private data * - * @name: short name for this device ("SSI0", "SSI1", etc) * @ssi: pointer to the SSI's registers * @ssi_phys: physical address of the SSI registers * @irq: IRQ of this SSI * @first_stream: pointer to the stream that was opened first * @second_stream: pointer to second stream - * @dev: struct device pointer * @playback: the number of playback streams opened * @capture: the number of capture streams opened * @asynchronous: 0=synchronous mode, 1=asynchronous mode * @cpu_dai: the CPU DAI for this device * @dev_attr: the sysfs device attribute structure * @stats: SSI statistics + * @name: name for this device */ struct fsl_ssi_private { - char name[8]; struct ccsr_ssi __iomem *ssi; dma_addr_t ssi_phys; unsigned int irq; struct snd_pcm_substream *first_stream; struct snd_pcm_substream *second_stream; - struct device *dev; unsigned int playback; unsigned int capture; int asynchronous; - struct snd_soc_dai cpu_dai; + struct snd_soc_dai_driver cpu_dai_drv; struct device_attribute dev_attr; + struct platform_device *pdev; struct { unsigned int rfrc; @@ -122,6 +122,8 @@ struct fsl_ssi_private { unsigned int tfe1; unsigned int tfe0; } stats; + + char name[1]; }; /** @@ -280,7 +282,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); /* * If this is the first stream opened, then request the IRQ @@ -290,6 +292,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct ccsr_ssi __iomem *ssi = ssi_private->ssi; int ret; + /* The 'name' should not have any slashes in it. */ ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name, ssi_private); if (ret < 0) { @@ -422,7 +425,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) { - struct fsl_ssi_private *ssi_private = cpu_dai->private_data; + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); if (substream == ssi_private->first_stream) { struct ccsr_ssi __iomem *ssi = ssi_private->ssi; @@ -458,7 +461,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct ccsr_ssi __iomem *ssi = ssi_private->ssi; switch (cmd) { @@ -497,7 +500,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ssi_private->playback--; @@ -523,56 +526,15 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, } } -/** - * fsl_ssi_set_sysclk: set the clock frequency and direction - * - * This function is called by the machine driver to tell us what the clock - * frequency and direction are. - * - * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN), - * and we don't care about the frequency. Return an error if the direction - * is not SND_SOC_CLOCK_IN. - * - * @clk_id: reserved, should be zero - * @freq: the frequency of the given clock ID, currently ignored - * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) - */ -static int fsl_ssi_set_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -{ - - return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; -} - -/** - * fsl_ssi_set_fmt: set the serial format. - * - * This function is called by the machine driver to tell us what serial - * format to use. - * - * Currently, we only support I2S mode. Return an error if the format is - * not SND_SOC_DAIFMT_I2S. - * - * @format: one of SND_SOC_DAIFMT_xxx - */ -static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) -{ - return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; -} - -/** - * fsl_ssi_dai_template: template CPU DAI for the SSI - */ static struct snd_soc_dai_ops fsl_ssi_dai_ops = { .startup = fsl_ssi_startup, .hw_params = fsl_ssi_hw_params, .shutdown = fsl_ssi_shutdown, .trigger = fsl_ssi_trigger, - .set_sysclk = fsl_ssi_set_sysclk, - .set_fmt = fsl_ssi_set_fmt, }; -static struct snd_soc_dai fsl_ssi_dai_template = { +/* Template for the CPU dai driver structure */ +static struct snd_soc_dai_driver fsl_ssi_dai_template = { .playback = { /* The SSI does not support monaural audio. */ .channels_min = 2, @@ -640,95 +602,176 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev, } /** - * fsl_ssi_create_dai: create a snd_soc_dai structure - * - * This function is called by the machine driver to create a snd_soc_dai - * structure. The function creates an ssi_private object, which contains - * the snd_soc_dai. It also creates the sysfs statistics device. + * Make every character in a string lower-case */ -struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) +static void make_lowercase(char *s) +{ + char *p = s; + char c; + + while ((c = *p)) { + if ((c >= 'A') && (c <= 'Z')) + *p = c + ('a' - 'A'); + p++; + } +} + +static int __devinit fsl_ssi_probe(struct of_device *of_dev, + const struct of_device_id *match) { - struct snd_soc_dai *fsl_ssi_dai; struct fsl_ssi_private *ssi_private; int ret = 0; struct device_attribute *dev_attr; + struct device_node *np = of_dev->dev.of_node; + const char *p, *sprop; + struct resource res; + char name[64]; - ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL); + /* We are only interested in SSIs with a codec phandle in them, so let's + * make sure this SSI has one. + */ + if (!of_get_property(np, "codec-handle", NULL)) + return -ENODEV; + + /* We only support the SSI in "I2S Slave" mode */ + sprop = of_get_property(np, "fsl,mode", NULL); + if (!sprop || strcmp(sprop, "i2s-slave")) { + dev_notice(&of_dev->dev, "mode %s is unsupported\n", sprop); + return -ENODEV; + } + + /* The DAI name is the last part of the full name of the node. */ + p = strrchr(np->full_name, '/') + 1; + ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), + GFP_KERNEL); if (!ssi_private) { - dev_err(ssi_info->dev, "could not allocate DAI object\n"); - return NULL; + dev_err(&of_dev->dev, "could not allocate DAI object\n"); + return -ENOMEM; } - memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template, - sizeof(struct snd_soc_dai)); - fsl_ssi_dai = &ssi_private->cpu_dai; - dev_attr = &ssi_private->dev_attr; + strcpy(ssi_private->name, p); - sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id); - ssi_private->ssi = ssi_info->ssi; - ssi_private->ssi_phys = ssi_info->ssi_phys; - ssi_private->irq = ssi_info->irq; - ssi_private->dev = ssi_info->dev; - ssi_private->asynchronous = ssi_info->asynchronous; + /* Initialize this copy of the CPU DAI driver structure */ + memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, + sizeof(fsl_ssi_dai_template)); + ssi_private->cpu_dai_drv.name = ssi_private->name; + + /* Get the addresses and IRQ */ + ret = of_address_to_resource(np, 0, &res); + if (ret) { + dev_err(&of_dev->dev, "could not determine device resources\n"); + kfree(ssi_private); + return ret; + } + ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start); + ssi_private->ssi_phys = res.start; + ssi_private->irq = irq_of_parse_and_map(np, 0); - dev_set_drvdata(ssi_private->dev, fsl_ssi_dai); + /* Are the RX and the TX clocks locked? */ + if (of_find_property(np, "fsl,ssi-asynchronous", NULL)) + ssi_private->asynchronous = 1; + else + ssi_private->cpu_dai_drv.symmetric_rates = 1; /* Initialize the the device_attribute structure */ - dev_attr->attr.name = "ssi-stats"; + dev_attr = &ssi_private->dev_attr; + dev_attr->attr.name = "statistics"; dev_attr->attr.mode = S_IRUGO; dev_attr->show = fsl_sysfs_ssi_show; - ret = device_create_file(ssi_private->dev, dev_attr); + ret = device_create_file(&of_dev->dev, dev_attr); if (ret) { - dev_err(ssi_info->dev, "could not create sysfs %s file\n", + dev_err(&of_dev->dev, "could not create sysfs %s file\n", ssi_private->dev_attr.attr.name); - kfree(fsl_ssi_dai); - return NULL; + kfree(ssi_private); + return ret; } - fsl_ssi_dai->private_data = ssi_private; - fsl_ssi_dai->name = ssi_private->name; - fsl_ssi_dai->id = ssi_info->id; - fsl_ssi_dai->dev = ssi_info->dev; - fsl_ssi_dai->symmetric_rates = 1; + /* Register with ASoC */ + dev_set_drvdata(&of_dev->dev, ssi_private); - ret = snd_soc_register_dai(fsl_ssi_dai); + ret = snd_soc_register_dai(&of_dev->dev, &ssi_private->cpu_dai_drv); if (ret != 0) { - dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret); - kfree(fsl_ssi_dai); - return NULL; + dev_err(&of_dev->dev, "failed to register DAI: %d\n", ret); + kfree(ssi_private); + return ret; + } + + /* Trigger the machine driver's probe function. The platform driver + * name of the machine driver is taken from the /model property of the + * device tree. We also pass the address of the CPU DAI driver + * structure. + */ + sprop = of_get_property(of_find_node_by_path("/"), "model", NULL); + /* Sometimes the model name has a "fsl," prefix, so we strip that. */ + p = strrchr(sprop, ','); + if (p) + sprop = p + 1; + snprintf(name, sizeof(name), "snd-soc-%s", sprop); + make_lowercase(name); + + ssi_private->pdev = + platform_device_register_data(&of_dev->dev, name, 0, NULL, 0); + if (IS_ERR(ssi_private->pdev)) { + ret = PTR_ERR(ssi_private->pdev); + dev_err(&of_dev->dev, "failed to register platform: %d\n", ret); + kfree(ssi_private); + return ret; } - return fsl_ssi_dai; + return 0; } -EXPORT_SYMBOL_GPL(fsl_ssi_create_dai); /** * fsl_ssi_destroy_dai: destroy the snd_soc_dai object * - * This function undoes the operations of fsl_ssi_create_dai() + * This function undoes the operations of fsl_ssi_probe() */ -void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) +static int fsl_ssi_remove(struct of_device *of_dev) { - struct fsl_ssi_private *ssi_private = - container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai); + struct fsl_ssi_private *ssi_private = dev_get_drvdata(&of_dev->dev); - device_remove_file(ssi_private->dev, &ssi_private->dev_attr); - - snd_soc_unregister_dai(&ssi_private->cpu_dai); + platform_device_unregister(ssi_private->pdev); + snd_soc_unregister_dai(&of_dev->dev); + device_remove_file(&of_dev->dev, &ssi_private->dev_attr); kfree(ssi_private); + dev_set_drvdata(&of_dev->dev, NULL); + + return 0; } -EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); + +static const struct of_device_id fsl_ssi_ids[] = { + { .compatible = "fsl,mpc8610-ssi", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_ssi_ids); + +static struct of_platform_driver fsl_ssi_driver = { + .driver = { + .name = "fsl-ssi-dai", + .owner = THIS_MODULE, + .of_match_table = fsl_ssi_ids, + }, + .probe = fsl_ssi_probe, + .remove = fsl_ssi_remove, +}; static int __init fsl_ssi_init(void) { printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); - return 0; + return of_register_platform_driver(&fsl_ssi_driver); +} + +static void __exit fsl_ssi_exit(void) +{ + of_unregister_platform_driver(&fsl_ssi_driver); } + module_init(fsl_ssi_init); +module_exit(fsl_ssi_exit); MODULE_AUTHOR("Timur Tabi "); MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index eade01feaab6..217300029b5b 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h @@ -196,31 +196,5 @@ struct ccsr_ssi { #define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) #define CCSR_SSI_SOR_SYNRST 0x00000001 -/* Instantiation data for an SSI interface - * - * This structure contains all the information that the the SSI driver needs - * to instantiate an SSI interface with ALSA. The machine driver should - * create this structure, fill it in, call fsl_ssi_create_dai(), and then - * delete the structure. - * - * id: which SSI this is (0, 1, etc. ) - * ssi: pointer to the SSI's registers - * ssi_phys: physical address of the SSI registers - * irq: IRQ of this SSI - * dev: struct device, used to create the sysfs statistics file - * asynchronous: 0=synchronous mode, 1=asynchronous mode -*/ -struct fsl_ssi_info { - unsigned int id; - struct ccsr_ssi __iomem *ssi; - dma_addr_t ssi_phys; - unsigned int irq; - struct device *dev; - int asynchronous; -}; - -struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); -void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai); - #endif diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 1d4e7164e80a..dce6b551cd78 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include @@ -107,7 +109,7 @@ static int psc_dma_hw_free(struct snd_pcm_substream *substream) static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct snd_pcm_runtime *runtime = substream->runtime; struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; @@ -212,7 +214,7 @@ static int psc_dma_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct psc_dma_stream *s; int rc; @@ -239,7 +241,7 @@ static int psc_dma_open(struct snd_pcm_substream *substream) static int psc_dma_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct psc_dma_stream *s; dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); @@ -264,7 +266,7 @@ static snd_pcm_uframes_t psc_dma_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct psc_dma_stream *s; dma_addr_t count; @@ -302,11 +304,11 @@ static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = pcm->private_data; - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); size_t size = psc_dma_hardware.buffer_bytes_max; int rc = 0; - dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", + dev_dbg(rtd->platform->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", card, dai, pcm); if (!card->dev->dma_mask) @@ -328,8 +330,8 @@ static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, goto capture_alloc_err; } - if (rtd->socdev->card->codec->ac97) - rtd->socdev->card->codec->ac97->private_data = psc_dma; + if (rtd->codec->ac97) + rtd->codec->ac97->private_data = psc_dma; return 0; @@ -349,7 +351,7 @@ static void psc_dma_free(struct snd_pcm *pcm) struct snd_pcm_substream *substream; int stream; - dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm); + dev_dbg(rtd->platform->dev, "psc_dma_free(pcm=%p)\n", pcm); for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; @@ -361,15 +363,14 @@ static void psc_dma_free(struct snd_pcm *pcm) } } -struct snd_soc_platform mpc5200_audio_dma_platform = { - .name = "mpc5200-psc-audio", - .pcm_ops = &psc_dma_ops, +static struct snd_soc_platform_driver mpc5200_audio_dma_platform = { + .ops = &psc_dma_ops, .pcm_new = &psc_dma_new, .pcm_free = &psc_dma_free, }; -EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform); -int mpc5200_audio_dma_create(struct of_device *op) +static int mpc5200_hpcd_probe(struct of_device *op, + const struct of_device_id *match) { phys_addr_t fifo; struct psc_dma *psc_dma; @@ -475,7 +476,7 @@ int mpc5200_audio_dma_create(struct of_device *op) dev_set_drvdata(&op->dev, psc_dma); /* Tell the ASoC OF helpers about it */ - return snd_soc_register_platform(&mpc5200_audio_dma_platform); + return snd_soc_register_platform(&op->dev, &mpc5200_audio_dma_platform); out_irq: free_irq(psc_dma->irq, psc_dma); free_irq(psc_dma->capture.irq, &psc_dma->capture); @@ -486,15 +487,14 @@ out_unmap: iounmap(regs); return ret; } -EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); -int mpc5200_audio_dma_destroy(struct of_device *op) +static int mpc5200_hpcd_remove(struct of_device *op) { struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); - snd_soc_unregister_platform(&mpc5200_audio_dma_platform); + snd_soc_unregister_platform(&op->dev); bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); @@ -510,7 +510,35 @@ int mpc5200_audio_dma_destroy(struct of_device *op) return 0; } -EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); + +static struct of_device_id mpc5200_hpcd_match[] = { + { + .compatible = "fsl,mpc5200-pcm", + }, + {} +}; +MODULE_DEVICE_TABLE(of, mpc5200_hpcd_match); + +static struct of_platform_driver mpc5200_hpcd_of_driver = { + .owner = THIS_MODULE, + .name = "mpc5200-pcm-audio", + .match_table = mpc5200_hpcd_match, + .probe = mpc5200_hpcd_probe, + .remove = mpc5200_hpcd_remove, +}; + +static int __init mpc5200_hpcd_init(void) +{ + return of_register_platform_driver(&mpc5200_hpcd_of_driver); +} + +static void __exit mpc5200_hpcd_exit(void) +{ + of_unregister_platform_driver(&mpc5200_hpcd_of_driver); +} + +module_init(mpc5200_hpcd_init); +module_exit(mpc5200_hpcd_exit); MODULE_AUTHOR("Grant Likely "); MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 22208b373fb9..7472531bc2a4 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -81,9 +81,4 @@ to_psc_dma_stream(struct snd_pcm_substream *substream, struct psc_dma *psc_dma) return &psc_dma->playback; } -int mpc5200_audio_dma_create(struct of_device *op); -int mpc5200_audio_dma_destroy(struct of_device *op); - -extern struct snd_soc_platform mpc5200_audio_dma_platform; - #endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index e2ee220bfb7e..11706c128c08 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -129,7 +129,7 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct psc_dma *psc_dma = cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" @@ -152,7 +152,7 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct psc_dma *psc_dma = cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream); @@ -167,8 +167,7 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(dai); struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); switch (cmd) { @@ -193,10 +192,9 @@ static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int psc_ac97_probe(struct platform_device *pdev, - struct snd_soc_dai *cpu_dai) +static int psc_ac97_probe(struct snd_soc_dai *cpu_dai) { - struct psc_dma *psc_dma = cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; /* Go */ @@ -223,9 +221,8 @@ static struct snd_soc_dai_ops psc_ac97_digital_ops = { .hw_params = psc_ac97_hw_digital_params, }; -struct snd_soc_dai psc_ac97_dai[] = { +static struct snd_soc_dai_driver psc_ac97_dai[] = { { - .name = "AC97", .ac97_control = 1, .probe = psc_ac97_probe, .playback = { @@ -243,7 +240,6 @@ struct snd_soc_dai psc_ac97_dai[] = { .ops = &psc_ac97_analog_ops, }, { - .name = "SPDIF", .ac97_control = 1, .playback = { .channels_min = 1, @@ -254,7 +250,6 @@ struct snd_soc_dai psc_ac97_dai[] = { }, .ops = &psc_ac97_digital_ops, } }; -EXPORT_SYMBOL_GPL(psc_ac97_dai); @@ -266,18 +261,11 @@ EXPORT_SYMBOL_GPL(psc_ac97_dai); static int __devinit psc_ac97_of_probe(struct of_device *op, const struct of_device_id *match) { - int rc, i; + int rc; struct snd_ac97 ac97; struct mpc52xx_psc __iomem *regs; - rc = mpc5200_audio_dma_create(op); - if (rc != 0) - return rc; - - for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) - psc_ac97_dai[i].dev = &op->dev; - - rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); + rc = snd_soc_register_dais(&op->dev, psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); if (rc != 0) { dev_err(&op->dev, "Failed to register DAI\n"); return rc; @@ -287,9 +275,6 @@ static int __devinit psc_ac97_of_probe(struct of_device *op, regs = psc_dma->psc_regs; ac97.private_data = psc_dma; - for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) - psc_ac97_dai[i].private_data = psc_dma; - psc_dma->imr = 0; out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); @@ -305,7 +290,8 @@ static int __devinit psc_ac97_of_probe(struct of_device *op, static int __devexit psc_ac97_of_remove(struct of_device *op) { - return mpc5200_audio_dma_destroy(op); + snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_ac97_dai)); + return 0; } /* Match table for of_platform binding */ diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h index 4bc18c35c369..e881e784b270 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.h +++ b/sound/soc/fsl/mpc5200_psc_ac97.h @@ -7,8 +7,6 @@ #ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ #define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ -extern struct snd_soc_dai psc_ac97_dai[]; - #define MPC5200_AC97_NORMAL 0 #define MPC5200_AC97_SPDIF 1 diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 676841cbae98..5b9f2c73f031 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -40,7 +40,7 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); u32 mode; dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" @@ -88,7 +88,7 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct psc_dma *psc_dma = cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", cpu_dai, dir); return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; @@ -107,7 +107,7 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, */ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) { - struct psc_dma *psc_dma = cpu_dai->private_data; + struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", cpu_dai, format); return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; @@ -129,8 +129,7 @@ static struct snd_soc_dai_ops psc_i2s_dai_ops = { .set_fmt = psc_i2s_set_fmt, }; -struct snd_soc_dai psc_i2s_dai[] = {{ - .name = "I2S", +static struct snd_soc_dai_driver psc_i2s_dai[] = {{ .playback = { .channels_min = 2, .channels_max = 2, @@ -145,7 +144,6 @@ struct snd_soc_dai psc_i2s_dai[] = {{ }, .ops = &psc_i2s_dai_ops, } }; -EXPORT_SYMBOL_GPL(psc_i2s_dai); /* --------------------------------------------------------------------- * OF platform bus binding code: @@ -159,11 +157,7 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, struct psc_dma *psc_dma; struct mpc52xx_psc __iomem *regs; - rc = mpc5200_audio_dma_create(op); - if (rc != 0) - return rc; - - rc = snd_soc_register_dais(psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); + rc = snd_soc_register_dais(&op->dev, psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); if (rc != 0) { pr_err("Failed to register DAI\n"); return 0; @@ -207,7 +201,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, static int __devexit psc_i2s_of_remove(struct of_device *op) { - return mpc5200_audio_dma_destroy(op); + snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_i2s_dai)); + return 0; } /* Match table for of_platform binding */ diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 6a2764ee8203..5ba823213abe 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -1,85 +1,96 @@ /** - * Freescale MPC8610HPCD ALSA SoC Fabric driver + * Freescale MPC8610HPCD ALSA SoC Machine driver * * Author: Timur Tabi * - * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed - * under the terms of the GNU General Public License version 2. This - * program is licensed "as is" without any warranty of any kind, whether - * express or implied. + * Copyright 2007-2010 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. */ -#include #include #include #include -#include #include #include -#include "../codecs/cs4270.h" #include "fsl_dma.h" #include "fsl_ssi.h" +/* There's only one global utilities register */ +static phys_addr_t guts_phys; + +#define DAI_NAME_SIZE 32 + /** - * mpc8610_hpcd_data: fabric-specific ASoC device data + * mpc8610_hpcd_data: machine-specific ASoC device data * * This structure contains data for a single sound platform device on an * MPC8610 HPCD. Some of the data is taken from the device tree. */ struct mpc8610_hpcd_data { - struct snd_soc_device sound_devdata; - struct snd_soc_dai_link dai; - struct snd_soc_card machine; + struct snd_soc_dai_link dai[2]; + struct snd_soc_card card; unsigned int dai_format; unsigned int codec_clk_direction; unsigned int cpu_clk_direction; unsigned int clk_frequency; - struct ccsr_guts __iomem *guts; - struct ccsr_ssi __iomem *ssi; - unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ - unsigned int ssi_irq; - unsigned int dma_id; /* 0 = DMA1, 1 = DMA2, etc */ - unsigned int dma_irq[2]; - struct ccsr_dma_channel __iomem *dma[2]; + unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ + unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ + char codec_dai_name[DAI_NAME_SIZE]; + char codec_name[DAI_NAME_SIZE]; + char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ }; /** - * mpc8610_hpcd_machine_probe: initalize the board + * mpc8610_hpcd_machine_probe: initialize the board * - * This function is called when platform_device_add() is called. It is used - * to initialize the board-specific hardware. + * This function is used to initialize the board-specific hardware. * * Here we program the DMACR and PMUXCR registers. */ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) { + struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = - sound_device->dev.platform_data; + container_of(card, struct mpc8610_hpcd_data, card); + struct ccsr_guts __iomem *guts; - /* Program the signal routing between the SSI and the DMA */ - guts_set_dmacr(machine_data->guts, machine_data->dma_id, - machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI); - guts_set_dmacr(machine_data->guts, machine_data->dma_id, - machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI); + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); + if (!guts) { + dev_err(card->dev, "could not map global utilities\n"); + return -ENOMEM; + } - guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, - machine_data->dma_channel_id[0], 0); - guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, - machine_data->dma_channel_id[1], 0); + /* Program the signal routing between the SSI and the DMA */ + guts_set_dmacr(guts, machine_data->dma_id[0], + machine_data->dma_channel_id[0], + CCSR_GUTS_DMACR_DEV_SSI); + guts_set_dmacr(guts, machine_data->dma_id[1], + machine_data->dma_channel_id[1], + CCSR_GUTS_DMACR_DEV_SSI); + + guts_set_pmuxcr_dma(guts, machine_data->dma_id[0], + machine_data->dma_channel_id[0], 0); + guts_set_pmuxcr_dma(guts, machine_data->dma_id[1], + machine_data->dma_channel_id[1], 0); switch (machine_data->ssi_id) { case 0: - clrsetbits_be32(&machine_data->guts->pmuxcr, + clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); break; case 1: - clrsetbits_be32(&machine_data->guts->pmuxcr, + clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); break; } + iounmap(guts); + return 0; } @@ -93,38 +104,15 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; struct mpc8610_hpcd_data *machine_data = - rtd->socdev->dev->platform_data; + container_of(rtd->card, struct mpc8610_hpcd_data, card); + struct device *dev = rtd->card->dev; int ret = 0; - /* Tell the CPU driver what the serial protocol is. */ - ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format); - if (ret < 0) { - dev_err(substream->pcm->card->dev, - "could not set CPU driver audio format\n"); - return ret; - } - /* Tell the codec driver what the serial protocol is. */ - ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format); + ret = snd_soc_dai_set_fmt(rtd->codec_dai, machine_data->dai_format); if (ret < 0) { - dev_err(substream->pcm->card->dev, - "could not set codec driver audio format\n"); - return ret; - } - - /* - * Tell the CPU driver what the clock frequency is, and whether it's a - * slave or master. - */ - ret = snd_soc_dai_set_sysclk(cpu_dai, 0, - machine_data->clk_frequency, - machine_data->cpu_clk_direction); - if (ret < 0) { - dev_err(substream->pcm->card->dev, - "could not set CPU driver clock parameters\n"); + dev_err(dev, "could not set codec driver audio format\n"); return ret; } @@ -132,12 +120,11 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * Tell the codec driver what the MCLK frequency is, and whether it's * a slave or master. */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, - machine_data->clk_frequency, - machine_data->codec_clk_direction); + ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, + machine_data->clk_frequency, + machine_data->codec_clk_direction); if (ret < 0) { - dev_err(substream->pcm->card->dev, - "could not set codec driver clock params\n"); + dev_err(dev, "could not set codec driver clock params\n"); return ret; } @@ -150,116 +137,254 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) { + struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = - sound_device->dev.platform_data; + container_of(card, struct mpc8610_hpcd_data, card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); + if (!guts) { + dev_err(card->dev, "could not map global utilities\n"); + return -ENOMEM; + } /* Restore the signal routing */ - guts_set_dmacr(machine_data->guts, machine_data->dma_id, - machine_data->dma_channel_id[0], 0); - guts_set_dmacr(machine_data->guts, machine_data->dma_id, - machine_data->dma_channel_id[1], 0); + guts_set_dmacr(guts, machine_data->dma_id[0], + machine_data->dma_channel_id[0], 0); + guts_set_dmacr(guts, machine_data->dma_id[1], + machine_data->dma_channel_id[1], 0); switch (machine_data->ssi_id) { case 0: - clrsetbits_be32(&machine_data->guts->pmuxcr, + clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); break; case 1: - clrsetbits_be32(&machine_data->guts->pmuxcr, + clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); break; } + iounmap(guts); + return 0; } /** - * mpc8610_hpcd_ops: ASoC fabric driver operations + * mpc8610_hpcd_ops: ASoC machine driver operations */ static struct snd_soc_ops mpc8610_hpcd_ops = { .startup = mpc8610_hpcd_startup, }; /** - * mpc8610_hpcd_probe: OF probe function for the fabric driver + * get_node_by_phandle_name - get a node by its phandle name * - * This function gets called when an SSI node is found in the device tree. + * This function takes a node, the name of a property in that node, and a + * compatible string. Assuming the property is a phandle to another node, + * it returns that node, (optionally) if that node is compatible. * - * Although this is a fabric driver, the SSI node is the "master" node with - * respect to audio hardware connections. Therefore, we create a new ASoC - * device for each new SSI node that has a codec attached. + * If the property is not a phandle, or the node it points to is not compatible + * with the specific string, then NULL is returned. + */ +static struct device_node *get_node_by_phandle_name(struct device_node *np, + const char *name, + const char *compatible) +{ + const phandle *ph; + int len; + + ph = of_get_property(np, name, &len); + if (!ph || (len != sizeof(phandle))) + return NULL; + + np = of_find_node_by_phandle(*ph); + if (!np) + return NULL; + + if (compatible && !of_device_is_compatible(np, compatible)) { + of_node_put(np); + return NULL; + } + + return np; +} + +/** + * get_parent_cell_index -- return the cell-index of the parent of a node * - * FIXME: Currently, we only support one DMA controller, so if there are - * multiple SSI nodes with codecs, only the first will be supported. + * Return the value of the cell-index property of the parent of the given + * node. This is used for DMA channel nodes that need to know the DMA ID + * of the controller they are on. + */ +static int get_parent_cell_index(struct device_node *np) +{ + struct device_node *parent = of_get_parent(np); + const u32 *iprop; + + if (!parent) + return -1; + + iprop = of_get_property(parent, "cell-index", NULL); + of_node_put(parent); + + if (!iprop) + return -1; + + return *iprop; +} + +/** + * codec_node_dev_name - determine the dev_name for a codec node * - * FIXME: Even if we did support multiple DMA controllers, we have no - * mechanism for assigning DMA controllers and channels to the individual - * SSI devices. We also probably aren't compatible with the generic Elo DMA - * device driver. + * This function determines the dev_name for an I2C node. This is the name + * that would be returned by dev_name() if this device_node were part of a + * 'struct device' It's ugly and hackish, but it works. + * + * The dev_name for such devices include the bus number and I2C address. For + * example, "cs4270-codec.0-004f". */ -static int mpc8610_hpcd_probe(struct of_device *ofdev, - const struct of_device_id *match) +static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) { - struct device_node *np = ofdev->dev.of_node; - struct device_node *codec_np = NULL; - struct device_node *guts_np = NULL; - struct device_node *dma_np = NULL; - struct device_node *dma_channel_np = NULL; - const phandle *codec_ph; - const char *sprop; const u32 *iprop; + int bus, addr; + char temp[DAI_NAME_SIZE]; + + of_modalias_node(np, temp, DAI_NAME_SIZE); + + iprop = of_get_property(np, "reg", NULL); + if (!iprop) + return -EINVAL; + + addr = *iprop; + + bus = get_parent_cell_index(np); + if (bus < 0) + return bus; + + snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); + + return 0; +} + +static int get_dma_channel(struct device_node *ssi_np, + const char *compatible, + struct snd_soc_dai_link *dai, + unsigned int *dma_channel_id, + unsigned int *dma_id) +{ struct resource res; + struct device_node *dma_channel_np; + const u32 *iprop; + int ret; + + dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, + "fsl,ssi-dma-channel"); + if (!dma_channel_np) + return -EINVAL; + + /* Determine the dev_name for the device_node. This code mimics the + * behavior of of_device_make_bus_id(). We need this because ASoC uses + * the dev_name() of the device to match the platform (DMA) device with + * the CPU (SSI) device. It's all ugly and hackish, but it works (for + * now). + * + * dai->platform name should already point to an allocated buffer. + */ + ret = of_address_to_resource(dma_channel_np, 0, &res); + if (ret) + return ret; + snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", + (unsigned long long) res.start, dma_channel_np->name); + + iprop = of_get_property(dma_channel_np, "cell-index", NULL); + if (!iprop) { + of_node_put(dma_channel_np); + return -EINVAL; + } + + *dma_channel_id = *iprop; + *dma_id = get_parent_cell_index(dma_channel_np); + of_node_put(dma_channel_np); + + return 0; +} + +/** + * mpc8610_hpcd_probe: platform probe function for the machine driver + * + * Although this is a machine driver, the SSI node is the "master" node with + * respect to audio hardware connections. Therefore, we create a new ASoC + * device for each new SSI node that has a codec attached. + */ +static int mpc8610_hpcd_probe(struct platform_device *pdev) +{ + struct device *dev = pdev->dev.parent; + /* of_dev is the OF device for the SSI node that probed us */ + struct of_device *of_dev = container_of(dev, struct of_device, dev); + struct device_node *np = of_dev->dev.of_node; + struct device_node *codec_np = NULL; struct platform_device *sound_device = NULL; struct mpc8610_hpcd_data *machine_data; - struct fsl_ssi_info ssi_info; - struct fsl_dma_info dma_info; int ret = -ENODEV; - unsigned int playback_dma_channel; - unsigned int capture_dma_channel; + const char *sprop; + const u32 *iprop; + + /* We are only interested in SSIs with a codec phandle in them, + * so let's make sure this SSI has one. The MPC8610 HPCD only + * knows about the CS4270 codec, so reject anything else. + */ + codec_np = get_node_by_phandle_name(np, "codec-handle", + "cirrus,cs4270"); + if (!codec_np) { + dev_err(dev, "invalid codec node\n"); + return -EINVAL; + } machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); if (!machine_data) return -ENOMEM; - memset(&ssi_info, 0, sizeof(ssi_info)); - memset(&dma_info, 0, sizeof(dma_info)); + machine_data->dai[0].cpu_dai_name = dev_name(&of_dev->dev); + machine_data->dai[0].ops = &mpc8610_hpcd_ops; - ssi_info.dev = &ofdev->dev; - - /* - * We are only interested in SSIs with a codec phandle in them, so let's - * make sure this SSI has one. - */ - codec_ph = of_get_property(np, "codec-handle", NULL); - if (!codec_ph) + /* Determine the codec name, it will be used as the codec DAI name */ + ret = codec_node_dev_name(codec_np, machine_data->codec_name, + DAI_NAME_SIZE); + if (ret) { + dev_err(&pdev->dev, "invalid codec node %s\n", + codec_np->full_name); + ret = -EINVAL; goto error; + } + machine_data->dai[0].codec_name = machine_data->codec_name; - codec_np = of_find_node_by_phandle(*codec_ph); - if (!codec_np) - goto error; + /* The DAI name from the codec (snd_soc_dai_driver.name) */ + machine_data->dai[0].codec_dai_name = "cs4270-hifi"; - /* The MPC8610 HPCD only knows about the CS4270 codec, so reject - anything else. */ - if (!of_device_is_compatible(codec_np, "cirrus,cs4270")) - goto error; + /* We register two DAIs per SSI, one for playback and the other for + * capture. Currently, we only support codecs that have one DAI for + * both playback and capture. + */ + memcpy(&machine_data->dai[1], &machine_data->dai[0], + sizeof(struct snd_soc_dai_link)); /* Get the device ID */ iprop = of_get_property(np, "cell-index", NULL); if (!iprop) { - dev_err(&ofdev->dev, "cell-index property not found\n"); + dev_err(&pdev->dev, "cell-index property not found\n"); ret = -EINVAL; goto error; } machine_data->ssi_id = *iprop; - ssi_info.id = *iprop; /* Get the serial format and clock direction. */ sprop = of_get_property(np, "fsl,mode", NULL); if (!sprop) { - dev_err(&ofdev->dev, "fsl,mode property not found\n"); + dev_err(&pdev->dev, "fsl,mode property not found\n"); ret = -EINVAL; goto error; } @@ -269,15 +394,14 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; - /* - * In i2s-slave mode, the codec has its own clock source, so we + /* In i2s-slave mode, the codec has its own clock source, so we * need to get the frequency from the device tree and pass it to * the codec driver. */ iprop = of_get_property(codec_np, "clock-frequency", NULL); if (!iprop || !*iprop) { - dev_err(&ofdev->dev, "codec bus-frequency property " - "is missing or invalid\n"); + dev_err(&pdev->dev, "codec bus-frequency " + "property is missing or invalid\n"); ret = -EINVAL; goto error; } @@ -311,317 +435,153 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; } else { - dev_err(&ofdev->dev, - "unrecognized fsl,mode property \"%s\"\n", sprop); + dev_err(&pdev->dev, + "unrecognized fsl,mode property '%s'\n", sprop); ret = -EINVAL; goto error; } if (!machine_data->clk_frequency) { - dev_err(&ofdev->dev, "unknown clock frequency\n"); + dev_err(&pdev->dev, "unknown clock frequency\n"); ret = -EINVAL; goto error; } - /* Read the SSI information from the device tree */ - ret = of_address_to_resource(np, 0, &res); + /* Find the playback DMA channel to use. */ + machine_data->dai[0].platform_name = machine_data->platform_name[0]; + ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0], + &machine_data->dma_channel_id[0], + &machine_data->dma_id[0]); if (ret) { - dev_err(&ofdev->dev, "could not obtain SSI address\n"); - goto error; - } - if (!res.start) { - dev_err(&ofdev->dev, "invalid SSI address\n"); - goto error; - } - ssi_info.ssi_phys = res.start; - - machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi)); - if (!machine_data->ssi) { - dev_err(&ofdev->dev, "could not map SSI address %x\n", - ssi_info.ssi_phys); - ret = -EINVAL; - goto error; - } - ssi_info.ssi = machine_data->ssi; - - - /* Get the IRQ of the SSI */ - machine_data->ssi_irq = irq_of_parse_and_map(np, 0); - if (!machine_data->ssi_irq) { - dev_err(&ofdev->dev, "could not get SSI IRQ\n"); - ret = -EINVAL; - goto error; - } - ssi_info.irq = machine_data->ssi_irq; - - /* Do we want to use asynchronous mode? */ - ssi_info.asynchronous = - of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0; - if (ssi_info.asynchronous) - dev_info(&ofdev->dev, "using asynchronous mode\n"); - - /* Map the global utilities registers. */ - guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); - if (!guts_np) { - dev_err(&ofdev->dev, "could not obtain address of GUTS\n"); - ret = -EINVAL; - goto error; - } - machine_data->guts = of_iomap(guts_np, 0); - of_node_put(guts_np); - if (!machine_data->guts) { - dev_err(&ofdev->dev, "could not map GUTS\n"); - ret = -EINVAL; - goto error; - } - - /* Find the DMA channels to use. Both SSIs need to use the same DMA - * controller, so let's use DMA#1. - */ - for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") { - iprop = of_get_property(dma_np, "cell-index", NULL); - if (iprop && (*iprop == 0)) { - of_node_put(dma_np); - break; - } - } - if (!dma_np) { - dev_err(&ofdev->dev, "could not find DMA node\n"); - ret = -EINVAL; - goto error; - } - machine_data->dma_id = *iprop; - - /* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA - * channels 2 and 3. This is just how the MPC8610 is wired - * internally. - */ - playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2; - capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3; - - /* - * Find the DMA channels to use. - */ - while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) { - iprop = of_get_property(dma_channel_np, "cell-index", NULL); - if (iprop && (*iprop == playback_dma_channel)) { - /* dma_channel[0] and dma_irq[0] are for playback */ - dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0); - dma_info.dma_irq[0] = - irq_of_parse_and_map(dma_channel_np, 0); - machine_data->dma_channel_id[0] = *iprop; - continue; - } - if (iprop && (*iprop == capture_dma_channel)) { - /* dma_channel[1] and dma_irq[1] are for capture */ - dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0); - dma_info.dma_irq[1] = - irq_of_parse_and_map(dma_channel_np, 0); - machine_data->dma_channel_id[1] = *iprop; - continue; - } - } - if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] || - !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) { - dev_err(&ofdev->dev, "could not find DMA channels\n"); - ret = -EINVAL; + dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); goto error; } - dma_info.ssi_stx_phys = ssi_info.ssi_phys + - offsetof(struct ccsr_ssi, stx0); - dma_info.ssi_srx_phys = ssi_info.ssi_phys + - offsetof(struct ccsr_ssi, srx0); - - /* We have the DMA information, so tell the DMA driver what it is */ - if (!fsl_dma_configure(&dma_info)) { - dev_err(&ofdev->dev, "could not instantiate DMA device\n"); - ret = -EBUSY; + /* Find the capture DMA channel to use. */ + machine_data->dai[1].platform_name = machine_data->platform_name[1]; + ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1], + &machine_data->dma_channel_id[1], + &machine_data->dma_id[1]); + if (ret) { + dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); goto error; } - /* - * Initialize our DAI data structure. We should probably get this - * information from the device tree. - */ - machine_data->dai.name = "CS4270"; - machine_data->dai.stream_name = "CS4270"; - - machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info); - machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */ - machine_data->dai.ops = &mpc8610_hpcd_ops; + /* Initialize our DAI data structure. */ + machine_data->dai[0].stream_name = "playback"; + machine_data->dai[1].stream_name = "capture"; + machine_data->dai[0].name = machine_data->dai[0].stream_name; + machine_data->dai[1].name = machine_data->dai[1].stream_name; - machine_data->machine.probe = mpc8610_hpcd_machine_probe; - machine_data->machine.remove = mpc8610_hpcd_machine_remove; - machine_data->machine.name = "MPC8610 HPCD"; - machine_data->machine.num_links = 1; - machine_data->machine.dai_link = &machine_data->dai; + machine_data->card.probe = mpc8610_hpcd_machine_probe; + machine_data->card.remove = mpc8610_hpcd_machine_remove; + machine_data->card.name = pdev->name; /* The platform driver name */ + machine_data->card.num_links = 2; + machine_data->card.dai_link = machine_data->dai; /* Allocate a new audio platform device structure */ sound_device = platform_device_alloc("soc-audio", -1); if (!sound_device) { - dev_err(&ofdev->dev, "platform device allocation failed\n"); + dev_err(&pdev->dev, "platform device alloc failed\n"); ret = -ENOMEM; goto error; } - machine_data->sound_devdata.card = &machine_data->machine; - machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; - machine_data->machine.platform = &fsl_soc_platform; - - sound_device->dev.platform_data = machine_data; - + /* Associate the card data with the sound device */ + platform_set_drvdata(sound_device, &machine_data->card); - /* Set the platform device and ASoC device to point to each other */ - platform_set_drvdata(sound_device, &machine_data->sound_devdata); - - machine_data->sound_devdata.dev = &sound_device->dev; - - - /* Tell ASoC to probe us. This will call mpc8610_hpcd_machine.probe(), - if it exists. */ + /* Register with ASoC */ ret = platform_device_add(sound_device); - if (ret) { - dev_err(&ofdev->dev, "platform device add failed\n"); + dev_err(&pdev->dev, "platform device add failed\n"); goto error; } - dev_set_drvdata(&ofdev->dev, sound_device); + of_node_put(codec_np); return 0; error: of_node_put(codec_np); - of_node_put(guts_np); - of_node_put(dma_np); - of_node_put(dma_channel_np); if (sound_device) platform_device_unregister(sound_device); - if (machine_data->dai.cpu_dai) - fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); - - if (ssi_info.ssi) - iounmap(ssi_info.ssi); - - if (ssi_info.irq) - irq_dispose_mapping(ssi_info.irq); - - if (dma_info.dma_channel[0]) - iounmap(dma_info.dma_channel[0]); - - if (dma_info.dma_channel[1]) - iounmap(dma_info.dma_channel[1]); - - if (dma_info.dma_irq[0]) - irq_dispose_mapping(dma_info.dma_irq[0]); - - if (dma_info.dma_irq[1]) - irq_dispose_mapping(dma_info.dma_irq[1]); - - if (machine_data->guts) - iounmap(machine_data->guts); - kfree(machine_data); return ret; } /** - * mpc8610_hpcd_remove: remove the OF device + * mpc8610_hpcd_remove: remove the platform device * - * This function is called when the OF device is removed. + * This function is called when the platform device is removed. */ -static int mpc8610_hpcd_remove(struct of_device *ofdev) +static int __devexit mpc8610_hpcd_remove(struct platform_device *pdev) { - struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); + struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); + struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = - sound_device->dev.platform_data; + container_of(card, struct mpc8610_hpcd_data, card); platform_device_unregister(sound_device); - if (machine_data->dai.cpu_dai) - fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); - - if (machine_data->ssi) - iounmap(machine_data->ssi); - - if (machine_data->dma[0]) - iounmap(machine_data->dma[0]); - - if (machine_data->dma[1]) - iounmap(machine_data->dma[1]); - - if (machine_data->dma_irq[0]) - irq_dispose_mapping(machine_data->dma_irq[0]); - - if (machine_data->dma_irq[1]) - irq_dispose_mapping(machine_data->dma_irq[1]); - - if (machine_data->guts) - iounmap(machine_data->guts); - kfree(machine_data); sound_device->dev.platform_data = NULL; - dev_set_drvdata(&ofdev->dev, NULL); + dev_set_drvdata(&pdev->dev, NULL); return 0; } -static struct of_device_id mpc8610_hpcd_match[] = { - { - .compatible = "fsl,mpc8610-ssi", - }, - {} -}; -MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match); - -static struct of_platform_driver mpc8610_hpcd_of_driver = { +static struct platform_driver mpc8610_hpcd_driver = { + .probe = mpc8610_hpcd_probe, + .remove = __devexit_p(mpc8610_hpcd_remove), .driver = { - .name = "mpc8610_hpcd", + /* The name must match the 'model' property in the device tree, + * in lowercase letters. + */ + .name = "snd-soc-mpc8610hpcd", .owner = THIS_MODULE, - .of_match_table = mpc8610_hpcd_match, }, - .probe = mpc8610_hpcd_probe, - .remove = mpc8610_hpcd_remove, }; /** - * mpc8610_hpcd_init: fabric driver initialization. + * mpc8610_hpcd_init: machine driver initialization. * * This function is called when this module is loaded. */ static int __init mpc8610_hpcd_init(void) { - int ret; - - printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n"); + struct device_node *guts_np; + struct resource res; - ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); + pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n"); - if (ret) - printk(KERN_ERR - "mpc8610-hpcd: failed to register platform driver\n"); + /* Get the physical address of the global utilities registers */ + guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); + if (of_address_to_resource(guts_np, 0, &res)) { + pr_err("mpc8610-hpcd: missing/invalid global utilities node\n"); + return -EINVAL; + } + guts_phys = res.start; - return ret; + return platform_driver_register(&mpc8610_hpcd_driver); } /** - * mpc8610_hpcd_exit: fabric driver exit + * mpc8610_hpcd_exit: machine driver exit * * This function is called when this driver is unloaded. */ static void __exit mpc8610_hpcd_exit(void) { - of_unregister_platform_driver(&mpc8610_hpcd_of_driver); + platform_driver_unregister(&mpc8610_hpcd_driver); } module_init(mpc8610_hpcd_init); module_exit(mpc8610_hpcd_exit); MODULE_AUTHOR("Timur Tabi "); -MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c index 6644cba7cbf2..fe15bb26e484 100644 --- a/sound/soc/fsl/pcm030-audio-fabric.c +++ b/sound/soc/fsl/pcm030-audio-fabric.c @@ -32,21 +32,24 @@ #define DRV_NAME "pcm030-audio-fabric" -static struct snd_soc_device device; static struct snd_soc_card card; static struct snd_soc_dai_link pcm030_fabric_dai[] = { { .name = "AC97", .stream_name = "AC97 Analog", - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], - .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL], + .codec_dai_name = "wm9712-hifi", + .cpu_dai_name = "mpc5200-psc-ac97.0", + .platform_name = "mpc5200-pcm-audio", + .codec_name = "wm9712-codec", }, { .name = "AC97", .stream_name = "AC97 IEC958", - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], - .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF], + .codec_dai_name = "wm9712-aux", + .cpu_dai_name = "mpc5200-psc-ac97.1", + .platform_name = "mpc5200-pcm-audio", + ..codec_name = "wm9712-codec", }, }; @@ -58,22 +61,18 @@ static __init int pcm030_fabric_init(void) if (!of_machine_is_compatible("phytec,pcm030")) return -ENODEV; - card.platform = &mpc5200_audio_dma_platform; + card.name = "pcm030"; card.dai_link = pcm030_fabric_dai; card.num_links = ARRAY_SIZE(pcm030_fabric_dai); - device.card = &card; - device.codec_dev = &soc_codec_dev_wm9712; - pdev = platform_device_alloc("soc-audio", 1); if (!pdev) { pr_err("pcm030_fabric_init: platform_device_alloc() failed\n"); return -ENODEV; } - platform_set_drvdata(pdev, &device); - device.dev = &pdev->dev; + platform_set_drvdata(pdev, &card); rc = platform_device_add(pdev); if (rc) { diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c deleted file mode 100644 index 3bc13fd89096..000000000000 --- a/sound/soc/fsl/soc-of-simple.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * OF helpers for ALSA SoC Layer - * - * Copyright (C) 2008, Secret Lab Technologies Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Grant Likely "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings"); - -static DEFINE_MUTEX(of_snd_soc_mutex); -static LIST_HEAD(of_snd_soc_device_list); -static int of_snd_soc_next_index; - -struct of_snd_soc_device { - int id; - struct list_head list; - struct snd_soc_device device; - struct snd_soc_card card; - struct snd_soc_dai_link dai_link; - struct platform_device *pdev; - struct device_node *platform_node; - struct device_node *codec_node; -}; - -static struct snd_soc_ops of_snd_soc_ops = { -}; - -static struct of_snd_soc_device * -of_snd_soc_get_device(struct device_node *codec_node) -{ - struct of_snd_soc_device *of_soc; - - list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { - if (of_soc->codec_node == codec_node) - return of_soc; - } - - of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL); - if (!of_soc) - return NULL; - - /* Initialize the structure and add it to the global list */ - of_soc->codec_node = codec_node; - of_soc->id = of_snd_soc_next_index++; - of_soc->card.dai_link = &of_soc->dai_link; - of_soc->card.num_links = 1; - of_soc->device.card = &of_soc->card; - of_soc->dai_link.ops = &of_snd_soc_ops; - list_add(&of_soc->list, &of_snd_soc_device_list); - - return of_soc; -} - -static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc) -{ - struct platform_device *pdev; - int rc; - - /* Only register the device if both the codec and platform have - * been registered */ - if ((!of_soc->device.codec_data) || (!of_soc->platform_node)) - return; - - pr_info("platform<-->codec match achieved; registering machine\n"); - - pdev = platform_device_alloc("soc-audio", of_soc->id); - if (!pdev) { - pr_err("of_soc: platform_device_alloc() failed\n"); - return; - } - - pdev->dev.platform_data = of_soc; - platform_set_drvdata(pdev, &of_soc->device); - of_soc->device.dev = &pdev->dev; - - /* The ASoC device is complete; register it */ - rc = platform_device_add(pdev); - if (rc) { - pr_err("of_soc: platform_device_add() failed\n"); - return; - } - -} - -int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, - void *codec_data, struct snd_soc_dai *dai, - struct device_node *node) -{ - struct of_snd_soc_device *of_soc; - int rc = 0; - - pr_info("registering ASoC codec driver: %s\n", node->full_name); - - mutex_lock(&of_snd_soc_mutex); - of_soc = of_snd_soc_get_device(node); - if (!of_soc) { - rc = -ENOMEM; - goto out; - } - - /* Store the codec data */ - of_soc->device.codec_data = codec_data; - of_soc->device.codec_dev = codec_dev; - of_soc->dai_link.name = (char *)node->name; - of_soc->dai_link.stream_name = (char *)node->name; - of_soc->dai_link.codec_dai = dai; - - /* Now try to register the SoC device */ - of_snd_soc_register_device(of_soc); - - out: - mutex_unlock(&of_snd_soc_mutex); - return rc; -} -EXPORT_SYMBOL_GPL(of_snd_soc_register_codec); - -int of_snd_soc_register_platform(struct snd_soc_platform *platform, - struct device_node *node, - struct snd_soc_dai *cpu_dai) -{ - struct of_snd_soc_device *of_soc; - struct device_node *codec_node; - const phandle *handle; - int len, rc = 0; - - pr_info("registering ASoC platform driver: %s\n", node->full_name); - - handle = of_get_property(node, "codec-handle", &len); - if (!handle || len < sizeof(handle)) - return -ENODEV; - codec_node = of_find_node_by_phandle(*handle); - if (!codec_node) - return -ENODEV; - pr_info("looking for codec: %s\n", codec_node->full_name); - - mutex_lock(&of_snd_soc_mutex); - of_soc = of_snd_soc_get_device(codec_node); - if (!of_soc) { - rc = -ENOMEM; - goto out; - } - - of_soc->platform_node = node; - of_soc->dai_link.cpu_dai = cpu_dai; - of_soc->card.platform = platform; - of_soc->card.name = of_soc->dai_link.cpu_dai->name; - - /* Now try to register the SoC device */ - of_snd_soc_register_device(of_soc); - - out: - mutex_unlock(&of_snd_soc_mutex); - return rc; -} -EXPORT_SYMBOL_GPL(of_snd_soc_register_platform); -- cgit v1.2.2 From 6e6f66226f0092a39526f8d6f02ebb447d995be2 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jul 2010 11:33:30 -0500 Subject: powerpc: rename immap_86xx.h to fsl_guts.h, and add 85xx support The immap_86xx.h header file only defines one data structure: the "global utilities" register set found on Freescale PowerPC SOCs. Rename this file to fsl_guts.h to reflect its true purpose, and extend it to cover the "GUTS" register set on 85xx chips. Signed-off-by: Timur Tabi Acked-by: Mark Brown Acked-by: Kumar Gala Signed-off-by: Liam Girdwood --- sound/soc/fsl/fsl_ssi.c | 2 -- sound/soc/fsl/mpc8610_hpcd.c | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 64f65910a7d7..51b089f9751c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -24,8 +24,6 @@ #include #include -#include - #include "fsl_ssi.h" /** diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 5ba823213abe..38339c158ed9 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "fsl_dma.h" #include "fsl_ssi.h" @@ -57,9 +57,9 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); - struct ccsr_guts __iomem *guts; + struct ccsr_guts_86xx __iomem *guts; - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); + guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); if (!guts) { dev_err(card->dev, "could not map global utilities\n"); return -ENOMEM; @@ -142,9 +142,9 @@ static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); - struct ccsr_guts __iomem *guts; + struct ccsr_guts_86xx __iomem *guts; - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); + guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); if (!guts) { dev_err(card->dev, "could not map global utilities\n"); return -ENOMEM; -- cgit v1.2.2 From 1a3c5a491af6756dbba6ee166a9dee72bb414ba8 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Mon, 2 Aug 2010 12:44:36 -0500 Subject: asoc/multi-component: fsl: add support for 36-bit physical addresses Update the DMA driver used by the Freescale MPC8610 HPCD audio driver to support 36-bit physical addresses, for both DMA buffers and the SSI registers. The DMA driver calls snd_dma_alloc_pages() to allocate the DMA buffers for playback and capture. This function is just a front-end for dma_alloc_coherent(). Currently, dma_alloc_coherent() only allocates buffers in low memory (it ignores GFP_HIGHMEM), so we never actually get a DMA buffer with a real 36-bit physical address. Signed-off-by: Timur Tabi Signed-off-by: Liam Girdwood --- sound/soc/fsl/fsl_dma.c | 77 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 26 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index d09e1941b1fa..4450f9d845c6 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -175,13 +175,23 @@ static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) struct fsl_dma_link_descriptor *link = &dma_private->link[dma_private->current_link]; - /* Update our link descriptors to point to the next period */ - if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - link->source_addr = - cpu_to_be32(dma_private->dma_buf_next); - else - link->dest_addr = - cpu_to_be32(dma_private->dma_buf_next); + /* Update our link descriptors to point to the next period. On a 36-bit + * system, we also need to update the ESAD bits. We also set (keep) the + * snoop bits. See the comments in fsl_dma_hw_params() about snooping. + */ + if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + link->source_addr = cpu_to_be32(dma_private->dma_buf_next); +#ifdef CONFIG_PHYS_64BIT + link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | + upper_32_bits(dma_private->dma_buf_next)); +#endif + } else { + link->dest_addr = cpu_to_be32(dma_private->dma_buf_next); +#ifdef CONFIG_PHYS_64BIT + link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | + upper_32_bits(dma_private->dma_buf_next)); +#endif + } /* Update our variables for next time */ dma_private->dma_buf_next += dma_private->period_size; @@ -273,11 +283,19 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) * This function is called when the codec driver calls snd_soc_new_pcms(), * once for each .dai_link in the machine driver's snd_soc_card * structure. + * + * snd_dma_alloc_pages() is just a front-end to dma_alloc_coherent(), which + * (currently) always allocates the DMA buffer in lowmem, even if GFP_HIGHMEM + * is specified. Therefore, any DMA buffers we allocate will always be in low + * memory, but we support for 36-bit physical addresses anyway. + * + * Regardless of where the memory is actually allocated, since the device can + * technically DMA to any 36-bit address, we do need to set the DMA mask to 36. */ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { - static u64 fsl_dma_dmamask = DMA_BIT_MASK(32); + static u64 fsl_dma_dmamask = DMA_BIT_MASK(36); int ret; if (!card->dev->dma_mask) @@ -609,12 +627,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, link->count = cpu_to_be32(period_size); - /* Even though the DMA controller supports 36-bit addressing, - * for simplicity we allow only 32-bit addresses for the audio - * buffer itself. This was enforced in fsl_dma_new() with the - * DMA mask. - * - * The snoop bit tells the DMA controller whether it should tell + /* The snoop bit tells the DMA controller whether it should tell * the ECM to snoop during a read or write to an address. For * audio, we use DMA to transfer data between memory and an I/O * device (the SSI's STX0 or SRX0 register). Snooping is only @@ -629,20 +642,24 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, * flush out the data for the previous period. So if you * increased period_bytes_min to a large enough size, you might * get more performance by not snooping, and you'll still be - * okay. + * okay. You'll need to update fsl_dma_update_pointers() also. */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { link->source_addr = cpu_to_be32(temp_addr); - link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); + link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | + upper_32_bits(temp_addr)); link->dest_addr = cpu_to_be32(ssi_sxx_phys); - link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP); + link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP | + upper_32_bits(ssi_sxx_phys)); } else { link->source_addr = cpu_to_be32(ssi_sxx_phys); - link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP); + link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP | + upper_32_bits(ssi_sxx_phys)); link->dest_addr = cpu_to_be32(temp_addr); - link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); + link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | + upper_32_bits(temp_addr)); } temp_addr += period_size; @@ -673,10 +690,23 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) dma_addr_t position; snd_pcm_uframes_t frames; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + /* Obtain the current DMA pointer, but don't read the ESAD bits if we + * only have 32-bit DMA addresses. This function is typically called + * in interrupt context, so we need to optimize it. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { position = in_be32(&dma_channel->sar); - else +#ifdef CONFIG_PHYS_64BIT + position |= (u64)(in_be32(&dma_channel->satr) & + CCSR_DMA_ATR_ESAD_MASK) << 32; +#endif + } else { position = in_be32(&dma_channel->dar); +#ifdef CONFIG_PHYS_64BIT + position |= (u64)(in_be32(&dma_channel->datr) & + CCSR_DMA_ATR_ESAD_MASK) << 32; +#endif + } /* * When capture is started, the SSI immediately starts to fill its FIFO. @@ -936,11 +966,6 @@ static void __exit fsl_soc_dma_exit(void) of_unregister_platform_driver(&fsl_soc_dma_driver); } -/* We want the DMA driver to be initialized before the SSI driver, so that - * when the SSI driver calls fsl_soc_dma_dai_from_node(), the DMA driver - * will already have been probed. The easiest way to do that is to make the - * __init function called via arch_initcall(). - */ module_init(fsl_soc_dma_init); module_exit(fsl_soc_dma_exit); -- cgit v1.2.2 From 87a0632b29410bab5c1783d7eb979c8d942d4209 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 3 Aug 2010 17:55:28 -0500 Subject: asoc/multi-component: fsl: fix exit and error paths in DMA and SSI drivers The error handling code in the OF probe function of the SSI driver is not freeing all resources correctly. Since the machine driver no longer calls the DMA driver to provide information about the SSI, we don't need to keep a list of DMA objects any more. In addition, the fsl_soc_dma_remove() function is incorrectly removing *all* DMA objects when it should only remove one. Signed-off-by: Timur Tabi Signed-off-by: Liam Girdwood --- sound/soc/fsl/fsl_dma.c | 23 +++++++---------------- sound/soc/fsl/fsl_ssi.c | 29 ++++++++++++++++------------- 2 files changed, 23 insertions(+), 29 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 4450f9d845c6..57774cb91ae3 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -57,7 +57,6 @@ SNDRV_PCM_RATE_CONTINUOUS) struct dma_object { - struct list_head list; struct snd_soc_platform_driver dai; dma_addr_t ssi_stx_phys; dma_addr_t ssi_srx_phys; @@ -825,9 +824,6 @@ static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) } } -/* List of DMA nodes that we've probed */ -static LIST_HEAD(dma_list); - /** * find_ssi_node -- returns the SSI node that points to his DMA channel node * @@ -915,25 +911,20 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, dma->channel = of_iomap(np, 0); dma->irq = irq_of_parse_and_map(np, 0); - list_add(&dma->list, &dma_list); + + dev_set_drvdata(&of_dev->dev, dma); return 0; } static int __devexit fsl_soc_dma_remove(struct of_device *of_dev) { - struct list_head *n, *ptr; - struct dma_object *dma; + struct dma_object *dma = dev_get_drvdata(&of_dev->dev); - list_for_each_safe(ptr, n, &dma_list) { - dma = list_entry(ptr, struct dma_object, list); - list_del_init(ptr); - - snd_soc_unregister_platform(&of_dev->dev); - iounmap(dma->channel); - irq_dispose_mapping(dma->irq); - kfree(dma); - } + snd_soc_unregister_platform(&of_dev->dev); + iounmap(dma->channel); + irq_dispose_mapping(dma->irq); + kfree(dma); return 0; } diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 51b089f9751c..00e3e625b52a 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -619,7 +619,7 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, { struct fsl_ssi_private *ssi_private; int ret = 0; - struct device_attribute *dev_attr; + struct device_attribute *dev_attr = NULL; struct device_node *np = of_dev->dev.of_node; const char *p, *sprop; struct resource res; @@ -681,18 +681,16 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, if (ret) { dev_err(&of_dev->dev, "could not create sysfs %s file\n", ssi_private->dev_attr.attr.name); - kfree(ssi_private); - return ret; + goto error; } /* Register with ASoC */ dev_set_drvdata(&of_dev->dev, ssi_private); ret = snd_soc_register_dai(&of_dev->dev, &ssi_private->cpu_dai_drv); - if (ret != 0) { + if (ret) { dev_err(&of_dev->dev, "failed to register DAI: %d\n", ret); - kfree(ssi_private); - return ret; + goto error; } /* Trigger the machine driver's probe function. The platform driver @@ -713,18 +711,23 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, if (IS_ERR(ssi_private->pdev)) { ret = PTR_ERR(ssi_private->pdev); dev_err(&of_dev->dev, "failed to register platform: %d\n", ret); - kfree(ssi_private); - return ret; + goto error; } return 0; + +error: + snd_soc_unregister_dai(&of_dev->dev); + dev_set_drvdata(&of_dev->dev, NULL); + if (dev_attr) + device_remove_file(&of_dev->dev, dev_attr); + irq_dispose_mapping(ssi_private->irq); + iounmap(ssi_private->ssi); + kfree(ssi_private); + + return ret; } -/** - * fsl_ssi_destroy_dai: destroy the snd_soc_dai object - * - * This function undoes the operations of fsl_ssi_probe() - */ static int fsl_ssi_remove(struct of_device *of_dev) { struct fsl_ssi_private *ssi_private = dev_get_drvdata(&of_dev->dev); -- cgit v1.2.2 From ff71334a46844d0ae6ff0055c549790bcd27bb10 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 4 Aug 2010 17:51:08 -0500 Subject: asoc/multi-component: fsl: add support for disabled SSI nodes Add support for adding "status = disabled" to an SSI node to incidate that it is not wired on the board. This replaces the not-so-intuitive previous method of omitting a codec-handle property. Signed-off-by: Timur Tabi Acked-by: Kumar Gala Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/fsl/fsl_ssi.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 00e3e625b52a..7939c337ed9d 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -625,12 +625,19 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, struct resource res; char name[64]; - /* We are only interested in SSIs with a codec phandle in them, so let's - * make sure this SSI has one. + /* SSIs that are not connected on the board should have a + * status = "disabled" + * property in their device tree nodes. */ - if (!of_get_property(np, "codec-handle", NULL)) + if (!of_device_is_available(np)) return -ENODEV; + /* Check for a codec-handle property. */ + if (!of_get_property(np, "codec-handle", NULL)) { + dev_err(&of_dev->dev, "missing codec-handle property\n"); + return -ENODEV; + } + /* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); if (!sprop || strcmp(sprop, "i2s-slave")) { -- cgit v1.2.2 From 8e9d869028f3ce13631af5ef41910ad8d8e6c246 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 6 Aug 2010 12:16:12 -0500 Subject: asoc/multi-component: fsl: add support for variable SSI FIFO depth Add code that programs the DMA and SSI controllers differently based on the FIFO depth of the SSI. The SSI devices on the MPC8610 and the P1022 are identical in every way except one: the transmit and receive FIFO depth. On the MPC8610, the depth is eight. On the P1022, it's fifteen. The device tree nodes for the SSI include a "fsl,fifo-depth" property that specifies the FIFO depth. Signed-off-by: Timur Tabi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/fsl/fsl_dma.c | 67 +++++++++++++++++++++++++++++++++++++------------ sound/soc/fsl/fsl_ssi.c | 25 +++++++++++++++--- 2 files changed, 73 insertions(+), 19 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 57774cb91ae3..dfe1cb94a70f 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -60,6 +60,7 @@ struct dma_object { struct snd_soc_platform_driver dai; dma_addr_t ssi_stx_phys; dma_addr_t ssi_srx_phys; + unsigned int ssi_fifo_depth; struct ccsr_dma_channel __iomem *channel; unsigned int irq; bool assigned; @@ -99,6 +100,7 @@ struct fsl_dma_private { unsigned int irq; struct snd_pcm_substream *substream; dma_addr_t ssi_sxx_phys; + unsigned int ssi_fifo_depth; dma_addr_t ld_buf_phys; unsigned int current_link; dma_addr_t dma_buf_phys; @@ -431,6 +433,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream) else dma_private->ssi_sxx_phys = dma->ssi_srx_phys; + dma_private->ssi_fifo_depth = dma->ssi_fifo_depth; dma_private->dma_channel = dma->channel; dma_private->irq = dma->irq; dma_private->substream = substream; @@ -544,11 +547,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, struct device *dev = rtd->platform->dev; /* Number of bits per sample */ - unsigned int sample_size = + unsigned int sample_bits = snd_pcm_format_physical_width(params_format(hw_params)); /* Number of bytes per frame */ - unsigned int frame_size = 2 * (sample_size / 8); + unsigned int sample_bytes = sample_bits / 8; /* Bus address of SSI STX register */ dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys; @@ -588,7 +591,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, * that offset here. While we're at it, also tell the DMA controller * how much data to transfer per sample. */ - switch (sample_size) { + switch (sample_bits) { case 8: mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1; ssi_sxx_phys += 3; @@ -602,22 +605,42 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, break; default: /* We should never get here */ - dev_err(dev, "unsupported sample size %u\n", sample_size); + dev_err(dev, "unsupported sample size %u\n", sample_bits); return -EINVAL; } /* - * BWC should always be a multiple of the frame size. BWC determines - * how many bytes are sent/received before the DMA controller checks the - * SSI to see if it needs to stop. For playback, the transmit FIFO can - * hold three frames, so we want to send two frames at a time. For - * capture, the receive FIFO is triggered when it contains one frame, so - * we want to receive one frame at a time. + * BWC determines how many bytes are sent/received before the DMA + * controller checks the SSI to see if it needs to stop. BWC should + * always be a multiple of the frame size, so that we always transmit + * whole frames. Each frame occupies two slots in the FIFO. The + * parameter for CCSR_DMA_MR_BWC() is rounded down the next power of two + * (MR[BWC] can only represent even powers of two). + * + * To simplify the process, we set BWC to the largest value that is + * less than or equal to the FIFO watermark. For playback, this ensures + * that we transfer the maximum amount without overrunning the FIFO. + * For capture, this ensures that we transfer the maximum amount without + * underrunning the FIFO. + * + * f = SSI FIFO depth + * w = SSI watermark value (which equals f - 2) + * b = DMA bandwidth count (in bytes) + * s = sample size (in bytes, which equals frame_size * 2) + * + * For playback, we never transmit more than the transmit FIFO + * watermark, otherwise we might write more data than the FIFO can hold. + * The watermark is equal to the FIFO depth minus two. + * + * For capture, two equations must hold: + * w > f - (b / s) + * w >= b / s + * + * So, b > 2 * s, but b must also be <= s * w. To simplify, we set + * b = s * w, which is equal to + * (dma_private->ssi_fifo_depth - 2) * sample_bytes. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - mr |= CCSR_DMA_MR_BWC(2 * frame_size); - else - mr |= CCSR_DMA_MR_BWC(frame_size); + mr |= CCSR_DMA_MR_BWC((dma_private->ssi_fifo_depth - 2) * sample_bytes); out_be32(&dma_channel->mr, mr); @@ -871,6 +894,7 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, struct device_node *np = of_dev->dev.of_node; struct device_node *ssi_np; struct resource res; + const uint32_t *iprop; int ret; /* Find the SSI node that points to us. */ @@ -881,15 +905,17 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, } ret = of_address_to_resource(ssi_np, 0, &res); - of_node_put(ssi_np); if (ret) { - dev_err(&of_dev->dev, "could not determine device resources\n"); + dev_err(&of_dev->dev, "could not determine resources for %s\n", + ssi_np->full_name); + of_node_put(ssi_np); return ret; } dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL); if (!dma) { dev_err(&of_dev->dev, "could not allocate dma object\n"); + of_node_put(ssi_np); return -ENOMEM; } @@ -902,6 +928,15 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); + iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); + if (iprop) + dma->ssi_fifo_depth = *iprop; + else + /* Older 8610 DTs didn't have the fifo-depth property */ + dma->ssi_fifo_depth = 8; + + of_node_put(ssi_np); + ret = snd_soc_register_platform(&of_dev->dev, &dma->dai); if (ret) { dev_err(&of_dev->dev, "could not register platform\n"); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 7939c337ed9d..d1c855ade8fb 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -93,6 +93,7 @@ struct fsl_ssi_private { unsigned int playback; unsigned int capture; int asynchronous; + unsigned int fifo_depth; struct snd_soc_dai_driver cpu_dai_drv; struct device_attribute dev_attr; struct platform_device *pdev; @@ -337,11 +338,20 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, /* * Set the watermark for transmit FIFI 0 and receive FIFO 0. We - * don't use FIFO 1. Since the SSI only supports stereo, the - * watermark should never be an odd number. + * don't use FIFO 1. We program the transmit water to signal a + * DMA transfer if there are only two (or fewer) elements left + * in the FIFO. Two elements equals one frame (left channel, + * right channel). This value, however, depends on the depth of + * the transmit buffer. + * + * We program the receive FIFO to notify us if at least two + * elements (one frame) have been written to the FIFO. We could + * make this value larger (and maybe we should), but this way + * data will be written to memory as soon as it's available. */ out_be32(&ssi->sfcsr, - CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2)); + CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | + CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2)); /* * We keep the SSI disabled because if we enable it, then the @@ -622,6 +632,7 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, struct device_attribute *dev_attr = NULL; struct device_node *np = of_dev->dev.of_node; const char *p, *sprop; + const uint32_t *iprop; struct resource res; char name[64]; @@ -678,6 +689,14 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, else ssi_private->cpu_dai_drv.symmetric_rates = 1; + /* Determine the FIFO depth. */ + iprop = of_get_property(np, "fsl,fifo-depth", NULL); + if (iprop) + ssi_private->fifo_depth = *iprop; + else + /* Older 8610 DTs didn't have the fifo-depth property */ + ssi_private->fifo_depth = 8; + /* Initialize the the device_attribute structure */ dev_attr = &ssi_private->dev_attr; dev_attr->attr.name = "statistics"; -- cgit v1.2.2 From c04019d450a885a095a2ca38fcd5db8d57cd2718 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Aug 2010 16:43:42 -0500 Subject: ASoC: add support for separate codec DAIs to the fsl_dma driver Some codecs have separate DAIs for playback and capture, so the DMA driver should allocate a DMA buffer only for the streams that are valid when the driver is opened. Signed-off-by: Timur Tabi Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 57774cb91ae3..5a6f56d63756 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -303,21 +303,29 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = fsl_dma_dmamask; - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, - fsl_dma_hardware.buffer_bytes_max, - &pcm->streams[0].substream->dma_buffer); - if (ret) { - dev_err(card->dev, "can't allocate playback dma buffer\n"); - return ret; + /* Some codecs have separate DAIs for playback and capture, so we + * should allocate a DMA buffer only for the streams that are valid. + */ + + if (dai->driver->playback.channels_min) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, + fsl_dma_hardware.buffer_bytes_max, + &pcm->streams[0].substream->dma_buffer); + if (ret) { + dev_err(card->dev, "can't alloc playback dma buffer\n"); + return ret; + } } - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, - fsl_dma_hardware.buffer_bytes_max, - &pcm->streams[1].substream->dma_buffer); - if (ret) { - snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); - dev_err(card->dev, "can't allocate capture dma buffer\n"); - return ret; + if (dai->driver->capture.channels_min) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, + fsl_dma_hardware.buffer_bytes_max, + &pcm->streams[1].substream->dma_buffer); + if (ret) { + snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); + dev_err(card->dev, "can't alloc capture dma buffer\n"); + return ret; + } } return 0; -- cgit v1.2.2 From 27ef3744f85bbbd00175ce7e9ac46b52089ee83c Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Aug 2010 17:11:40 -0500 Subject: ASoC: add support for the Freescale P1022 DS reference board The Freescale P1022 is a dual-core e500-based SOC with multimedia capabilities, specifically the same SSI audio controller on the MPC8610. The P1022 DS reference board includes a P1022 and a Wolfson Microelectronics WM8776 codec. Signed-off-by: Timur Tabi Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 24 +- sound/soc/fsl/Makefile | 8 +- sound/soc/fsl/p1022_ds.c | 590 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 614 insertions(+), 8 deletions(-) create mode 100644 sound/soc/fsl/p1022_ds.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 981868700388..d754d34d68a6 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,24 +1,36 @@ config SND_MPC52xx_DMA tristate -# ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers -# for the SSI and the Elo DMA controller. You will still need to select -# a platform driver and a codec driver. -config SND_SOC_MPC8610 +# ASoC platform support for the Freescale PowerPC SOCs that have an SSI and +# an Elo DMA controller, such as the MPC8610 and P1022. You will still need to +# select a platform driver and a codec driver. +config SND_SOC_POWERPC_SSI tristate - depends on MPC8610 + depends on FSL_SOC config SND_SOC_MPC8610_HPCD tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" # I2C is necessary for the CS4270 driver depends on MPC8610_HPCD && I2C - select SND_SOC_MPC8610 + select SND_SOC_POWERPC_SSI select SND_SOC_CS4270 select SND_SOC_CS4270_VD33_ERRATA default y if MPC8610_HPCD help Say Y if you want to enable audio on the Freescale MPC8610 HPCD. +config SND_SOC_P1022_DS + tristate "ALSA SoC support for the Freescale P1022 DS board" + # I2C is necessary for the WM8776 driver + depends on P1022_DS && I2C + select SND_SOC_POWERPC_SSI + select SND_SOC_WM8776 + default y if P1022_DS + help + Say Y if you want to enable audio on the Freescale P1022 DS board. + This will also include the Wolfson Microelectronics WM8776 codec + driver. + config SND_SOC_MPC5200_I2S tristate "Freescale MPC5200 PSC in I2S mode driver" depends on PPC_MPC52xx && PPC_BESTCOMM diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 7e472a53fcd3..b4a38c0ac58c 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -2,10 +2,14 @@ snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o -# MPC8610 Platform Support +# P1022 DS Machine Support +snd-soc-p1022-ds-objs := p1022_ds.o +obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o + +# Freescale PowerPC SSI/DMA Platform Support snd-soc-fsl-ssi-objs := fsl_ssi.o snd-soc-fsl-dma-objs := fsl_dma.o -obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o +obj-$(CONFIG_SND_SOC_POWERPC_SSI) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c new file mode 100644 index 000000000000..f8176e8e1adf --- /dev/null +++ b/sound/soc/fsl/p1022_ds.c @@ -0,0 +1,590 @@ +/** + * Freescale P1022DS ALSA SoC Machine driver + * + * Author: Timur Tabi + * + * Copyright 2010 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include "fsl_dma.h" +#include "fsl_ssi.h" + +/* P1022-specific PMUXCR and DMUXCR bit definitions */ + +#define CCSR_GUTS_PMUXCR_UART0_I2C1_MASK 0x0001c000 +#define CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI 0x00010000 +#define CCSR_GUTS_PMUXCR_UART0_I2C1_SSI 0x00018000 + +#define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK 0x00000c00 +#define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI 0x00000000 + +#define CCSR_GUTS_DMUXCR_PAD 1 /* DMA controller/channel set to pad */ +#define CCSR_GUTS_DMUXCR_SSI 2 /* DMA controller/channel set to SSI */ + +/* + * Set the DMACR register in the GUTS + * + * The DMACR register determines the source of initiated transfers for each + * channel on each DMA controller. Rather than have a bunch of repetitive + * macros for the bit patterns, we just have a function that calculates + * them. + * + * guts: Pointer to GUTS structure + * co: The DMA controller (0 or 1) + * ch: The channel on the DMA controller (0, 1, 2, or 3) + * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx) + */ +static inline void guts_set_dmuxcr(struct ccsr_guts_85xx __iomem *guts, + unsigned int co, unsigned int ch, unsigned int device) +{ + unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); + + clrsetbits_be32(&guts->dmuxcr, 3 << shift, device << shift); +} + +/* There's only one global utilities register */ +static phys_addr_t guts_phys; + +#define DAI_NAME_SIZE 32 + +/** + * machine_data: machine-specific ASoC device data + * + * This structure contains data for a single sound platform device on an + * P1022 DS. Some of the data is taken from the device tree. + */ +struct machine_data { + struct snd_soc_dai_link dai[2]; + struct snd_soc_card card; + unsigned int dai_format; + unsigned int codec_clk_direction; + unsigned int cpu_clk_direction; + unsigned int clk_frequency; + unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ + unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ + unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ + char codec_name[DAI_NAME_SIZE]; + char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ +}; + +/** + * p1022_ds_machine_probe: initialize the board + * + * This function is used to initialize the board-specific hardware. + * + * Here we program the DMACR and PMUXCR registers. + */ +static int p1022_ds_machine_probe(struct platform_device *sound_device) +{ + struct snd_soc_card *card = platform_get_drvdata(sound_device); + struct machine_data *mdata = + container_of(card, struct machine_data, card); + struct ccsr_guts_85xx __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); + if (!guts) { + dev_err(card->dev, "could not map global utilities\n"); + return -ENOMEM; + } + + /* Enable SSI Tx signal */ + clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK, + CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI); + + /* Enable SSI Rx signal */ + clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK, + CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI); + + /* Enable DMA Channel for SSI */ + guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], + CCSR_GUTS_DMUXCR_SSI); + + guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], + CCSR_GUTS_DMUXCR_SSI); + + iounmap(guts); + + return 0; +} + +/** + * p1022_ds_startup: program the board with various hardware parameters + * + * This function takes board-specific information, like clock frequencies + * and serial data formats, and passes that information to the codec and + * transport drivers. + */ +static int p1022_ds_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct machine_data *mdata = + container_of(rtd->card, struct machine_data, card); + struct device *dev = rtd->card->dev; + int ret = 0; + + /* Tell the codec driver what the serial protocol is. */ + ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format); + if (ret < 0) { + dev_err(dev, "could not set codec driver audio format\n"); + return ret; + } + + /* + * Tell the codec driver what the MCLK frequency is, and whether it's + * a slave or master. + */ + ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, mdata->clk_frequency, + mdata->codec_clk_direction); + if (ret < 0) { + dev_err(dev, "could not set codec driver clock params\n"); + return ret; + } + + return 0; +} + +/** + * p1022_ds_machine_remove: Remove the sound device + * + * This function is called to remove the sound device for one SSI. We + * de-program the DMACR and PMUXCR register. + */ +static int p1022_ds_machine_remove(struct platform_device *sound_device) +{ + struct snd_soc_card *card = platform_get_drvdata(sound_device); + struct machine_data *mdata = + container_of(card, struct machine_data, card); + struct ccsr_guts_85xx __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); + if (!guts) { + dev_err(card->dev, "could not map global utilities\n"); + return -ENOMEM; + } + + /* Restore the signal routing */ + clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK); + clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK); + guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], 0); + guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], 0); + + iounmap(guts); + + return 0; +} + +/** + * p1022_ds_ops: ASoC machine driver operations + */ +static struct snd_soc_ops p1022_ds_ops = { + .startup = p1022_ds_startup, +}; + +/** + * get_node_by_phandle_name - get a node by its phandle name + * + * This function takes a node, the name of a property in that node, and a + * compatible string. Assuming the property is a phandle to another node, + * it returns that node, (optionally) if that node is compatible. + * + * If the property is not a phandle, or the node it points to is not compatible + * with the specific string, then NULL is returned. + */ +static struct device_node *get_node_by_phandle_name(struct device_node *np, + const char *name, const char *compatible) +{ + np = of_parse_phandle(np, name, 0); + if (!np) + return NULL; + + if (!of_device_is_compatible(np, compatible)) { + of_node_put(np); + return NULL; + } + + return np; +} + +/** + * get_parent_cell_index -- return the cell-index of the parent of a node + * + * Return the value of the cell-index property of the parent of the given + * node. This is used for DMA channel nodes that need to know the DMA ID + * of the controller they are on. + */ +static int get_parent_cell_index(struct device_node *np) +{ + struct device_node *parent = of_get_parent(np); + const u32 *iprop; + int ret = -1; + + if (!parent) + return -1; + + iprop = of_get_property(parent, "cell-index", NULL); + if (iprop) + ret = *iprop; + + of_node_put(parent); + + return ret; +} + +/** + * codec_node_dev_name - determine the dev_name for a codec node + * + * This function determines the dev_name for an I2C node. This is the name + * that would be returned by dev_name() if this device_node were part of a + * 'struct device' It's ugly and hackish, but it works. + * + * The dev_name for such devices include the bus number and I2C address. For + * example, "cs4270-codec.0-004f". + */ +static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) +{ + const u32 *iprop; + int bus, addr; + char temp[DAI_NAME_SIZE]; + + of_modalias_node(np, temp, DAI_NAME_SIZE); + + iprop = of_get_property(np, "reg", NULL); + if (!iprop) + return -EINVAL; + + addr = *iprop; + + bus = get_parent_cell_index(np); + if (bus < 0) + return bus; + + snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); + + return 0; +} + +static int get_dma_channel(struct device_node *ssi_np, + const char *compatible, + struct snd_soc_dai_link *dai, + unsigned int *dma_channel_id, + unsigned int *dma_id) +{ + struct resource res; + struct device_node *dma_channel_np; + const u32 *iprop; + int ret; + + dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, + "fsl,ssi-dma-channel"); + if (!dma_channel_np) + return -EINVAL; + + /* Determine the dev_name for the device_node. This code mimics the + * behavior of of_device_make_bus_id(). We need this because ASoC uses + * the dev_name() of the device to match the platform (DMA) device with + * the CPU (SSI) device. It's all ugly and hackish, but it works (for + * now). + * + * dai->platform name should already point to an allocated buffer. + */ + ret = of_address_to_resource(dma_channel_np, 0, &res); + if (ret) + return ret; + snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", + (unsigned long long) res.start, dma_channel_np->name); + + iprop = of_get_property(dma_channel_np, "cell-index", NULL); + if (!iprop) { + of_node_put(dma_channel_np); + return -EINVAL; + } + + *dma_channel_id = *iprop; + *dma_id = get_parent_cell_index(dma_channel_np); + of_node_put(dma_channel_np); + + return 0; +} + +/** + * p1022_ds_probe: platform probe function for the machine driver + * + * Although this is a machine driver, the SSI node is the "master" node with + * respect to audio hardware connections. Therefore, we create a new ASoC + * device for each new SSI node that has a codec attached. + */ +static int p1022_ds_probe(struct platform_device *pdev) +{ + struct device *dev = pdev->dev.parent; + /* ssi_pdev is the platform device for the SSI node that probed us */ + struct platform_device *ssi_pdev = + container_of(dev, struct platform_device, dev); + struct device_node *np = ssi_pdev->dev.of_node; + struct device_node *codec_np = NULL; + struct platform_device *sound_device = NULL; + struct machine_data *mdata; + int ret = -ENODEV; + const char *sprop; + const u32 *iprop; + + /* Find the codec node for this SSI. */ + codec_np = of_parse_phandle(np, "codec-handle", 0); + if (!codec_np) { + dev_err(dev, "could not find codec node\n"); + return -EINVAL; + } + + mdata = kzalloc(sizeof(struct machine_data), GFP_KERNEL); + if (!mdata) + return -ENOMEM; + + mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); + mdata->dai[0].ops = &p1022_ds_ops; + + /* Determine the codec name, it will be used as the codec DAI name */ + ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE); + if (ret) { + dev_err(&pdev->dev, "invalid codec node %s\n", + codec_np->full_name); + ret = -EINVAL; + goto error; + } + mdata->dai[0].codec_name = mdata->codec_name; + + /* We register two DAIs per SSI, one for playback and the other for + * capture. We support codecs that have separate DAIs for both playback + * and capture. + */ + memcpy(&mdata->dai[1], &mdata->dai[0], sizeof(struct snd_soc_dai_link)); + + /* The DAI names from the codec (snd_soc_dai_driver.name) */ + mdata->dai[0].codec_dai_name = "wm8776-hifi-playback"; + mdata->dai[1].codec_dai_name = "wm8776-hifi-capture"; + + /* Get the device ID */ + iprop = of_get_property(np, "cell-index", NULL); + if (!iprop) { + dev_err(&pdev->dev, "cell-index property not found\n"); + ret = -EINVAL; + goto error; + } + mdata->ssi_id = *iprop; + + /* Get the serial format and clock direction. */ + sprop = of_get_property(np, "fsl,mode", NULL); + if (!sprop) { + dev_err(&pdev->dev, "fsl,mode property not found\n"); + ret = -EINVAL; + goto error; + } + + if (strcasecmp(sprop, "i2s-slave") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_I2S; + mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; + mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; + + /* In i2s-slave mode, the codec has its own clock source, so we + * need to get the frequency from the device tree and pass it to + * the codec driver. + */ + iprop = of_get_property(codec_np, "clock-frequency", NULL); + if (!iprop || !*iprop) { + dev_err(&pdev->dev, "codec bus-frequency " + "property is missing or invalid\n"); + ret = -EINVAL; + goto error; + } + mdata->clk_frequency = *iprop; + } else if (strcasecmp(sprop, "i2s-master") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_I2S; + mdata->codec_clk_direction = SND_SOC_CLOCK_IN; + mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else if (strcasecmp(sprop, "lj-slave") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_LEFT_J; + mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; + mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; + } else if (strcasecmp(sprop, "lj-master") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_LEFT_J; + mdata->codec_clk_direction = SND_SOC_CLOCK_IN; + mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else if (strcasecmp(sprop, "rj-slave") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_RIGHT_J; + mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; + mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; + } else if (strcasecmp(sprop, "rj-master") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_RIGHT_J; + mdata->codec_clk_direction = SND_SOC_CLOCK_IN; + mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else if (strcasecmp(sprop, "ac97-slave") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_AC97; + mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; + mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; + } else if (strcasecmp(sprop, "ac97-master") == 0) { + mdata->dai_format = SND_SOC_DAIFMT_AC97; + mdata->codec_clk_direction = SND_SOC_CLOCK_IN; + mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else { + dev_err(&pdev->dev, + "unrecognized fsl,mode property '%s'\n", sprop); + ret = -EINVAL; + goto error; + } + + if (!mdata->clk_frequency) { + dev_err(&pdev->dev, "unknown clock frequency\n"); + ret = -EINVAL; + goto error; + } + + /* Find the playback DMA channel to use. */ + mdata->dai[0].platform_name = mdata->platform_name[0]; + ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], + &mdata->dma_channel_id[0], + &mdata->dma_id[0]); + if (ret) { + dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); + goto error; + } + + /* Find the capture DMA channel to use. */ + mdata->dai[1].platform_name = mdata->platform_name[1]; + ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], + &mdata->dma_channel_id[1], + &mdata->dma_id[1]); + if (ret) { + dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); + goto error; + } + + /* Initialize our DAI data structure. */ + mdata->dai[0].stream_name = "playback"; + mdata->dai[1].stream_name = "capture"; + mdata->dai[0].name = mdata->dai[0].stream_name; + mdata->dai[1].name = mdata->dai[1].stream_name; + + mdata->card.probe = p1022_ds_machine_probe; + mdata->card.remove = p1022_ds_machine_remove; + mdata->card.name = pdev->name; /* The platform driver name */ + mdata->card.num_links = 2; + mdata->card.dai_link = mdata->dai; + + /* Allocate a new audio platform device structure */ + sound_device = platform_device_alloc("soc-audio", -1); + if (!sound_device) { + dev_err(&pdev->dev, "platform device alloc failed\n"); + ret = -ENOMEM; + goto error; + } + + /* Associate the card data with the sound device */ + platform_set_drvdata(sound_device, &mdata->card); + + /* Register with ASoC */ + ret = platform_device_add(sound_device); + if (ret) { + dev_err(&pdev->dev, "platform device add failed\n"); + goto error; + } + + of_node_put(codec_np); + + return 0; + +error: + of_node_put(codec_np); + + if (sound_device) + platform_device_unregister(sound_device); + + kfree(mdata); + + return ret; +} + +/** + * p1022_ds_remove: remove the platform device + * + * This function is called when the platform device is removed. + */ +static int __devexit p1022_ds_remove(struct platform_device *pdev) +{ + struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); + struct snd_soc_card *card = platform_get_drvdata(sound_device); + struct machine_data *mdata = + container_of(card, struct machine_data, card); + + platform_device_unregister(sound_device); + + kfree(mdata); + sound_device->dev.platform_data = NULL; + + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static struct platform_driver p1022_ds_driver = { + .probe = p1022_ds_probe, + .remove = __devexit_p(p1022_ds_remove), + .driver = { + /* The name must match the 'model' property in the device tree, + * in lowercase letters, but only the part after that last + * comma. This is because some model properties have a "fsl," + * prefix. + */ + .name = "snd-soc-p1022", + .owner = THIS_MODULE, + }, +}; + +/** + * p1022_ds_init: machine driver initialization. + * + * This function is called when this module is loaded. + */ +static int __init p1022_ds_init(void) +{ + struct device_node *guts_np; + struct resource res; + + pr_info("Freescale P1022 DS ALSA SoC machine driver\n"); + + /* Get the physical address of the global utilities registers */ + guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts"); + if (of_address_to_resource(guts_np, 0, &res)) { + pr_err("p1022-ds: missing/invalid global utilities node\n"); + return -EINVAL; + } + guts_phys = res.start; + of_node_put(guts_np); + + return platform_driver_register(&p1022_ds_driver); +} + +/** + * p1022_ds_exit: machine driver exit + * + * This function is called when this driver is unloaded. + */ +static void __exit p1022_ds_exit(void) +{ + platform_driver_unregister(&p1022_ds_driver); +} + +module_init(p1022_ds_init); +module_exit(p1022_ds_exit); + +MODULE_AUTHOR("Timur Tabi "); +MODULE_DESCRIPTION("Freescale P1022 DS ALSA SoC machine driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.2 From 38fec7272bc033b75a0eb8976c56c2024d371b7d Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Aug 2010 15:26:58 -0500 Subject: ASoC: mpc8610: replace of_device with platform_device 'struct of_device' no longer exists, and its functionality has been merged into platform_device. Update the MPC8610 HPCD audio drivers (fsl_ssi, fsl_dma, and mpc8610_hpcd) accordingly. Also add a #include for slab.h, which is now needed for kmalloc and kfree. Signed-off-by: Timur Tabi Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.c | 23 ++++++++++++----------- sound/soc/fsl/fsl_ssi.c | 42 +++++++++++++++++++++--------------------- sound/soc/fsl/mpc8610_hpcd.c | 10 ++++++---- 3 files changed, 39 insertions(+), 36 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index f039e8db0765..4cf98c03af22 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -895,11 +896,11 @@ static struct snd_pcm_ops fsl_dma_ops = { .pointer = fsl_dma_pointer, }; -static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, +static int __devinit fsl_soc_dma_probe(struct platform_device *pdev, const struct of_device_id *match) { struct dma_object *dma; - struct device_node *np = of_dev->dev.of_node; + struct device_node *np = pdev->dev.of_node; struct device_node *ssi_np; struct resource res; const uint32_t *iprop; @@ -908,13 +909,13 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, /* Find the SSI node that points to us. */ ssi_np = find_ssi_node(np); if (!ssi_np) { - dev_err(&of_dev->dev, "cannot find parent SSI node\n"); + dev_err(&pdev->dev, "cannot find parent SSI node\n"); return -ENODEV; } ret = of_address_to_resource(ssi_np, 0, &res); if (ret) { - dev_err(&of_dev->dev, "could not determine resources for %s\n", + dev_err(&pdev->dev, "could not determine resources for %s\n", ssi_np->full_name); of_node_put(ssi_np); return ret; @@ -922,7 +923,7 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL); if (!dma) { - dev_err(&of_dev->dev, "could not allocate dma object\n"); + dev_err(&pdev->dev, "could not allocate dma object\n"); of_node_put(ssi_np); return -ENOMEM; } @@ -945,9 +946,9 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, of_node_put(ssi_np); - ret = snd_soc_register_platform(&of_dev->dev, &dma->dai); + ret = snd_soc_register_platform(&pdev->dev, &dma->dai); if (ret) { - dev_err(&of_dev->dev, "could not register platform\n"); + dev_err(&pdev->dev, "could not register platform\n"); kfree(dma); return ret; } @@ -955,16 +956,16 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, dma->channel = of_iomap(np, 0); dma->irq = irq_of_parse_and_map(np, 0); - dev_set_drvdata(&of_dev->dev, dma); + dev_set_drvdata(&pdev->dev, dma); return 0; } -static int __devexit fsl_soc_dma_remove(struct of_device *of_dev) +static int __devexit fsl_soc_dma_remove(struct platform_device *pdev) { - struct dma_object *dma = dev_get_drvdata(&of_dev->dev); + struct dma_object *dma = dev_get_drvdata(&pdev->dev); - snd_soc_unregister_platform(&of_dev->dev); + snd_soc_unregister_platform(&pdev->dev); iounmap(dma->channel); irq_dispose_mapping(dma->irq); kfree(dma); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index d1c855ade8fb..4cc167a7aeb8 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -624,13 +624,13 @@ static void make_lowercase(char *s) } } -static int __devinit fsl_ssi_probe(struct of_device *of_dev, +static int __devinit fsl_ssi_probe(struct platform_device *pdev, const struct of_device_id *match) { struct fsl_ssi_private *ssi_private; int ret = 0; struct device_attribute *dev_attr = NULL; - struct device_node *np = of_dev->dev.of_node; + struct device_node *np = pdev->dev.of_node; const char *p, *sprop; const uint32_t *iprop; struct resource res; @@ -645,14 +645,14 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, /* Check for a codec-handle property. */ if (!of_get_property(np, "codec-handle", NULL)) { - dev_err(&of_dev->dev, "missing codec-handle property\n"); + dev_err(&pdev->dev, "missing codec-handle property\n"); return -ENODEV; } /* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); if (!sprop || strcmp(sprop, "i2s-slave")) { - dev_notice(&of_dev->dev, "mode %s is unsupported\n", sprop); + dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); return -ENODEV; } @@ -661,7 +661,7 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), GFP_KERNEL); if (!ssi_private) { - dev_err(&of_dev->dev, "could not allocate DAI object\n"); + dev_err(&pdev->dev, "could not allocate DAI object\n"); return -ENOMEM; } @@ -675,7 +675,7 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, /* Get the addresses and IRQ */ ret = of_address_to_resource(np, 0, &res); if (ret) { - dev_err(&of_dev->dev, "could not determine device resources\n"); + dev_err(&pdev->dev, "could not determine device resources\n"); kfree(ssi_private); return ret; } @@ -703,19 +703,19 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, dev_attr->attr.mode = S_IRUGO; dev_attr->show = fsl_sysfs_ssi_show; - ret = device_create_file(&of_dev->dev, dev_attr); + ret = device_create_file(&pdev->dev, dev_attr); if (ret) { - dev_err(&of_dev->dev, "could not create sysfs %s file\n", + dev_err(&pdev->dev, "could not create sysfs %s file\n", ssi_private->dev_attr.attr.name); goto error; } /* Register with ASoC */ - dev_set_drvdata(&of_dev->dev, ssi_private); + dev_set_drvdata(&pdev->dev, ssi_private); - ret = snd_soc_register_dai(&of_dev->dev, &ssi_private->cpu_dai_drv); + ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv); if (ret) { - dev_err(&of_dev->dev, "failed to register DAI: %d\n", ret); + dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); goto error; } @@ -733,20 +733,20 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, make_lowercase(name); ssi_private->pdev = - platform_device_register_data(&of_dev->dev, name, 0, NULL, 0); + platform_device_register_data(&pdev->dev, name, 0, NULL, 0); if (IS_ERR(ssi_private->pdev)) { ret = PTR_ERR(ssi_private->pdev); - dev_err(&of_dev->dev, "failed to register platform: %d\n", ret); + dev_err(&pdev->dev, "failed to register platform: %d\n", ret); goto error; } return 0; error: - snd_soc_unregister_dai(&of_dev->dev); - dev_set_drvdata(&of_dev->dev, NULL); + snd_soc_unregister_dai(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); if (dev_attr) - device_remove_file(&of_dev->dev, dev_attr); + device_remove_file(&pdev->dev, dev_attr); irq_dispose_mapping(ssi_private->irq); iounmap(ssi_private->ssi); kfree(ssi_private); @@ -754,16 +754,16 @@ error: return ret; } -static int fsl_ssi_remove(struct of_device *of_dev) +static int fsl_ssi_remove(struct platform_device *pdev) { - struct fsl_ssi_private *ssi_private = dev_get_drvdata(&of_dev->dev); + struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); platform_device_unregister(ssi_private->pdev); - snd_soc_unregister_dai(&of_dev->dev); - device_remove_file(&of_dev->dev, &ssi_private->dev_attr); + snd_soc_unregister_dai(&pdev->dev); + device_remove_file(&pdev->dev, &ssi_private->dev_attr); kfree(ssi_private); - dev_set_drvdata(&of_dev->dev, NULL); + dev_set_drvdata(&pdev->dev, NULL); return 0; } diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 38339c158ed9..0d7dcf1e4863 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -323,9 +324,10 @@ static int get_dma_channel(struct device_node *ssi_np, static int mpc8610_hpcd_probe(struct platform_device *pdev) { struct device *dev = pdev->dev.parent; - /* of_dev is the OF device for the SSI node that probed us */ - struct of_device *of_dev = container_of(dev, struct of_device, dev); - struct device_node *np = of_dev->dev.of_node; + /* ssi_pdev is the platform device for the SSI node that probed us */ + struct platform_device *ssi_pdev = + container_of(dev, struct platform_device, dev); + struct device_node *np = ssi_pdev->dev.of_node; struct device_node *codec_np = NULL; struct platform_device *sound_device = NULL; struct mpc8610_hpcd_data *machine_data; @@ -348,7 +350,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) if (!machine_data) return -ENOMEM; - machine_data->dai[0].cpu_dai_name = dev_name(&of_dev->dev); + machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); machine_data->dai[0].ops = &mpc8610_hpcd_ops; /* Determine the codec name, it will be used as the codec DAI name */ -- cgit v1.2.2 From 880b8ffd45bf46f783b8b0d009487d89fd37b608 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 31 Aug 2010 17:48:56 +0200 Subject: ASoC: pl022_ds.c: Add of_node_put to avoid memory leak Add a call to of_node_put in the error handling code following a call to of_parse_phandle. This patch also moves the existing call to of_node_put tothe end of the error handling code, to make it possible to jump to of_node_put without doing the other cleanup operations. These appear to be disjoint operations, so the ordering doesn't matter. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r exists@ local idexpression x; expression E,E1,E2; statement S; @@ *x = (of_find_node_by_path |of_find_node_by_name |of_find_node_by_phandle |of_get_parent |of_get_next_parent |of_get_next_child |of_find_compatible_node |of_match_node |of_find_node_by_type |of_find_node_with_property |of_find_matching_node |of_parse_phandle )(...); ... if (x == NULL) S <... when != x = E *if (...) { ... when != of_node_put(x) when != if (...) { ... of_node_put(x); ... } ( return <+...x...+>; | * return ...; ) } ...> ( E2 = x; | of_node_put(x); ) // Signed-off-by: Julia Lawall Acked-by: Timur Tabi Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/fsl/p1022_ds.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index f8176e8e1adf..63b9eaa1ebc2 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -346,8 +346,10 @@ static int p1022_ds_probe(struct platform_device *pdev) } mdata = kzalloc(sizeof(struct machine_data), GFP_KERNEL); - if (!mdata) - return -ENOMEM; + if (!mdata) { + ret = -ENOMEM; + goto error_put; + } mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); mdata->dai[0].ops = &p1022_ds_ops; @@ -502,13 +504,12 @@ static int p1022_ds_probe(struct platform_device *pdev) return 0; error: - of_node_put(codec_np); - if (sound_device) platform_device_unregister(sound_device); kfree(mdata); - +error_put: + of_node_put(codec_np); return ret; } -- cgit v1.2.2