aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl/mpc8610_hpcd.c
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@slimlogic.co.uk>2010-03-17 16:15:21 -0400
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-08-12 09:00:00 -0400
commitf0fba2ad1b6b53d5360125c41953b7afcd6deff0 (patch)
treef6ad50905f8daa616593c978d7ae992e73241180 /sound/soc/fsl/mpc8610_hpcd.c
parentbda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff)
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 <timur@freescale.com> Some TLV320aic23 and Cirrus platform fixes. Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> TI CODEC and OMAP fixes Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> Signed-off-by: Jarkko Nikula <jhnikula@gmail.com> Samsung platform and misc fixes :- Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Jassi Brar <jassi.brar@samsung.com> Signed-off-by: Seungwhan Youn <sw.youn@samsung.com> MPC8610 and PPC fixes. Signed-off-by: Timur Tabi <timur@freescale.com> i.MX fixes and some core fixes. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> J4740 platform fixes:- Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> CC: Tony Lindgren <tony@atomide.com> CC: Nicolas Ferre <nicolas.ferre@atmel.com> CC: Kevin Hilman <khilman@deeprootsystems.com> CC: Sascha Hauer <s.hauer@pengutronix.de> CC: Atsushi Nemoto <anemo@mba.ocn.ne.jp> CC: Kuninori Morimoto <morimoto.kuninori@renesas.com> CC: Daniel Gloeckner <dg@emlix.com> CC: Manuel Lauss <mano@roarinelk.homelinux.net> CC: Mike Frysinger <vapier.adi@gmail.com> CC: Arnaud Patard <apatard@mandriva.com> CC: Wan ZongShun <mcuos.com@gmail.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/fsl/mpc8610_hpcd.c')
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c658
1 files changed, 309 insertions, 349 deletions
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 @@
1/** 1/**
2 * Freescale MPC8610HPCD ALSA SoC Fabric driver 2 * Freescale MPC8610HPCD ALSA SoC Machine driver
3 * 3 *
4 * Author: Timur Tabi <timur@freescale.com> 4 * Author: Timur Tabi <timur@freescale.com>
5 * 5 *
6 * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed 6 * Copyright 2007-2010 Freescale Semiconductor, Inc.
7 * under the terms of the GNU General Public License version 2. This 7 *
8 * program is licensed "as is" without any warranty of any kind, whether 8 * This file is licensed under the terms of the GNU General Public License
9 * express or implied. 9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
10 */ 11 */
11 12
12#include <linux/slab.h>
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/interrupt.h> 14#include <linux/interrupt.h>
15#include <linux/of_device.h> 15#include <linux/of_device.h>
16#include <linux/of_platform.h>
17#include <sound/soc.h> 16#include <sound/soc.h>
18#include <asm/immap_86xx.h> 17#include <asm/immap_86xx.h>
19 18
20#include "../codecs/cs4270.h"
21#include "fsl_dma.h" 19#include "fsl_dma.h"
22#include "fsl_ssi.h" 20#include "fsl_ssi.h"
23 21
22/* There's only one global utilities register */
23static phys_addr_t guts_phys;
24
25#define DAI_NAME_SIZE 32
26
24/** 27/**
25 * mpc8610_hpcd_data: fabric-specific ASoC device data 28 * mpc8610_hpcd_data: machine-specific ASoC device data
26 * 29 *
27 * This structure contains data for a single sound platform device on an 30 * This structure contains data for a single sound platform device on an
28 * MPC8610 HPCD. Some of the data is taken from the device tree. 31 * MPC8610 HPCD. Some of the data is taken from the device tree.
29 */ 32 */
30struct mpc8610_hpcd_data { 33struct mpc8610_hpcd_data {
31 struct snd_soc_device sound_devdata; 34 struct snd_soc_dai_link dai[2];
32 struct snd_soc_dai_link dai; 35 struct snd_soc_card card;
33 struct snd_soc_card machine;
34 unsigned int dai_format; 36 unsigned int dai_format;
35 unsigned int codec_clk_direction; 37 unsigned int codec_clk_direction;
36 unsigned int cpu_clk_direction; 38 unsigned int cpu_clk_direction;
37 unsigned int clk_frequency; 39 unsigned int clk_frequency;
38 struct ccsr_guts __iomem *guts; 40 unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */
39 struct ccsr_ssi __iomem *ssi; 41 unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
40 unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */
41 unsigned int ssi_irq;
42 unsigned int dma_id; /* 0 = DMA1, 1 = DMA2, etc */
43 unsigned int dma_irq[2];
44 struct ccsr_dma_channel __iomem *dma[2];
45 unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ 42 unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
43 char codec_dai_name[DAI_NAME_SIZE];
44 char codec_name[DAI_NAME_SIZE];
45 char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
46}; 46};
47 47
48/** 48/**
49 * mpc8610_hpcd_machine_probe: initalize the board 49 * mpc8610_hpcd_machine_probe: initialize the board
50 * 50 *
51 * This function is called when platform_device_add() is called. It is used 51 * This function is used to initialize the board-specific hardware.
52 * to initialize the board-specific hardware.
53 * 52 *
54 * Here we program the DMACR and PMUXCR registers. 53 * Here we program the DMACR and PMUXCR registers.
55 */ 54 */
56static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) 55static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
57{ 56{
57 struct snd_soc_card *card = platform_get_drvdata(sound_device);
58 struct mpc8610_hpcd_data *machine_data = 58 struct mpc8610_hpcd_data *machine_data =
59 sound_device->dev.platform_data; 59 container_of(card, struct mpc8610_hpcd_data, card);
60 struct ccsr_guts __iomem *guts;
60 61
61 /* Program the signal routing between the SSI and the DMA */ 62 guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
62 guts_set_dmacr(machine_data->guts, machine_data->dma_id, 63 if (!guts) {
63 machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI); 64 dev_err(card->dev, "could not map global utilities\n");
64 guts_set_dmacr(machine_data->guts, machine_data->dma_id, 65 return -ENOMEM;
65 machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI); 66 }
66 67
67 guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, 68 /* Program the signal routing between the SSI and the DMA */
68 machine_data->dma_channel_id[0], 0); 69 guts_set_dmacr(guts, machine_data->dma_id[0],
69 guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, 70 machine_data->dma_channel_id[0],
70 machine_data->dma_channel_id[1], 0); 71 CCSR_GUTS_DMACR_DEV_SSI);
72 guts_set_dmacr(guts, machine_data->dma_id[1],
73 machine_data->dma_channel_id[1],
74 CCSR_GUTS_DMACR_DEV_SSI);
75
76 guts_set_pmuxcr_dma(guts, machine_data->dma_id[0],
77 machine_data->dma_channel_id[0], 0);
78 guts_set_pmuxcr_dma(guts, machine_data->dma_id[1],
79 machine_data->dma_channel_id[1], 0);
71 80
72 switch (machine_data->ssi_id) { 81 switch (machine_data->ssi_id) {
73 case 0: 82 case 0:
74 clrsetbits_be32(&machine_data->guts->pmuxcr, 83 clrsetbits_be32(&guts->pmuxcr,
75 CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); 84 CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI);
76 break; 85 break;
77 case 1: 86 case 1:
78 clrsetbits_be32(&machine_data->guts->pmuxcr, 87 clrsetbits_be32(&guts->pmuxcr,
79 CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); 88 CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI);
80 break; 89 break;
81 } 90 }
82 91
92 iounmap(guts);
93
83 return 0; 94 return 0;
84} 95}
85 96
@@ -93,38 +104,15 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
93static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) 104static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
94{ 105{
95 struct snd_soc_pcm_runtime *rtd = substream->private_data; 106 struct snd_soc_pcm_runtime *rtd = substream->private_data;
96 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
97 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
98 struct mpc8610_hpcd_data *machine_data = 107 struct mpc8610_hpcd_data *machine_data =
99 rtd->socdev->dev->platform_data; 108 container_of(rtd->card, struct mpc8610_hpcd_data, card);
109 struct device *dev = rtd->card->dev;
100 int ret = 0; 110 int ret = 0;
101 111
102 /* Tell the CPU driver what the serial protocol is. */
103 ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format);
104 if (ret < 0) {
105 dev_err(substream->pcm->card->dev,
106 "could not set CPU driver audio format\n");
107 return ret;
108 }
109
110 /* Tell the codec driver what the serial protocol is. */ 112 /* Tell the codec driver what the serial protocol is. */
111 ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format); 113 ret = snd_soc_dai_set_fmt(rtd->codec_dai, machine_data->dai_format);
112 if (ret < 0) { 114 if (ret < 0) {
113 dev_err(substream->pcm->card->dev, 115 dev_err(dev, "could not set codec driver audio format\n");
114 "could not set codec driver audio format\n");
115 return ret;
116 }
117
118 /*
119 * Tell the CPU driver what the clock frequency is, and whether it's a
120 * slave or master.
121 */
122 ret = snd_soc_dai_set_sysclk(cpu_dai, 0,
123 machine_data->clk_frequency,
124 machine_data->cpu_clk_direction);
125 if (ret < 0) {
126 dev_err(substream->pcm->card->dev,
127 "could not set CPU driver clock parameters\n");
128 return ret; 116 return ret;
129 } 117 }
130 118
@@ -132,12 +120,11 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
132 * Tell the codec driver what the MCLK frequency is, and whether it's 120 * Tell the codec driver what the MCLK frequency is, and whether it's
133 * a slave or master. 121 * a slave or master.
134 */ 122 */
135 ret = snd_soc_dai_set_sysclk(codec_dai, 0, 123 ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0,
136 machine_data->clk_frequency, 124 machine_data->clk_frequency,
137 machine_data->codec_clk_direction); 125 machine_data->codec_clk_direction);
138 if (ret < 0) { 126 if (ret < 0) {
139 dev_err(substream->pcm->card->dev, 127 dev_err(dev, "could not set codec driver clock params\n");
140 "could not set codec driver clock params\n");
141 return ret; 128 return ret;
142 } 129 }
143 130
@@ -150,116 +137,254 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
150 * This function is called to remove the sound device for one SSI. We 137 * This function is called to remove the sound device for one SSI. We
151 * de-program the DMACR and PMUXCR register. 138 * de-program the DMACR and PMUXCR register.
152 */ 139 */
153int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) 140static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
154{ 141{
142 struct snd_soc_card *card = platform_get_drvdata(sound_device);
155 struct mpc8610_hpcd_data *machine_data = 143 struct mpc8610_hpcd_data *machine_data =
156 sound_device->dev.platform_data; 144 container_of(card, struct mpc8610_hpcd_data, card);
145 struct ccsr_guts __iomem *guts;
146
147 guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
148 if (!guts) {
149 dev_err(card->dev, "could not map global utilities\n");
150 return -ENOMEM;
151 }
157 152
158 /* Restore the signal routing */ 153 /* Restore the signal routing */
159 154
160 guts_set_dmacr(machine_data->guts, machine_data->dma_id, 155 guts_set_dmacr(guts, machine_data->dma_id[0],
161 machine_data->dma_channel_id[0], 0); 156 machine_data->dma_channel_id[0], 0);
162 guts_set_dmacr(machine_data->guts, machine_data->dma_id, 157 guts_set_dmacr(guts, machine_data->dma_id[1],
163 machine_data->dma_channel_id[1], 0); 158 machine_data->dma_channel_id[1], 0);
164 159
165 switch (machine_data->ssi_id) { 160 switch (machine_data->ssi_id) {
166 case 0: 161 case 0:
167 clrsetbits_be32(&machine_data->guts->pmuxcr, 162 clrsetbits_be32(&guts->pmuxcr,
168 CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); 163 CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
169 break; 164 break;
170 case 1: 165 case 1:
171 clrsetbits_be32(&machine_data->guts->pmuxcr, 166 clrsetbits_be32(&guts->pmuxcr,
172 CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); 167 CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
173 break; 168 break;
174 } 169 }
175 170
171 iounmap(guts);
172
176 return 0; 173 return 0;
177} 174}
178 175
179/** 176/**
180 * mpc8610_hpcd_ops: ASoC fabric driver operations 177 * mpc8610_hpcd_ops: ASoC machine driver operations
181 */ 178 */
182static struct snd_soc_ops mpc8610_hpcd_ops = { 179static struct snd_soc_ops mpc8610_hpcd_ops = {
183 .startup = mpc8610_hpcd_startup, 180 .startup = mpc8610_hpcd_startup,
184}; 181};
185 182
186/** 183/**
187 * mpc8610_hpcd_probe: OF probe function for the fabric driver 184 * get_node_by_phandle_name - get a node by its phandle name
188 * 185 *
189 * This function gets called when an SSI node is found in the device tree. 186 * This function takes a node, the name of a property in that node, and a
187 * compatible string. Assuming the property is a phandle to another node,
188 * it returns that node, (optionally) if that node is compatible.
190 * 189 *
191 * Although this is a fabric driver, the SSI node is the "master" node with 190 * If the property is not a phandle, or the node it points to is not compatible
192 * respect to audio hardware connections. Therefore, we create a new ASoC 191 * with the specific string, then NULL is returned.
193 * device for each new SSI node that has a codec attached. 192 */
193static struct device_node *get_node_by_phandle_name(struct device_node *np,
194 const char *name,
195 const char *compatible)
196{
197 const phandle *ph;
198 int len;
199
200 ph = of_get_property(np, name, &len);
201 if (!ph || (len != sizeof(phandle)))
202 return NULL;
203
204 np = of_find_node_by_phandle(*ph);
205 if (!np)
206 return NULL;
207
208 if (compatible && !of_device_is_compatible(np, compatible)) {
209 of_node_put(np);
210 return NULL;
211 }
212
213 return np;
214}
215
216/**
217 * get_parent_cell_index -- return the cell-index of the parent of a node
194 * 218 *
195 * FIXME: Currently, we only support one DMA controller, so if there are 219 * Return the value of the cell-index property of the parent of the given
196 * multiple SSI nodes with codecs, only the first will be supported. 220 * node. This is used for DMA channel nodes that need to know the DMA ID
221 * of the controller they are on.
222 */
223static int get_parent_cell_index(struct device_node *np)
224{
225 struct device_node *parent = of_get_parent(np);
226 const u32 *iprop;
227
228 if (!parent)
229 return -1;
230
231 iprop = of_get_property(parent, "cell-index", NULL);
232 of_node_put(parent);
233
234 if (!iprop)
235 return -1;
236
237 return *iprop;
238}
239
240/**
241 * codec_node_dev_name - determine the dev_name for a codec node
197 * 242 *
198 * FIXME: Even if we did support multiple DMA controllers, we have no 243 * This function determines the dev_name for an I2C node. This is the name
199 * mechanism for assigning DMA controllers and channels to the individual 244 * that would be returned by dev_name() if this device_node were part of a
200 * SSI devices. We also probably aren't compatible with the generic Elo DMA 245 * 'struct device' It's ugly and hackish, but it works.
201 * device driver. 246 *
247 * The dev_name for such devices include the bus number and I2C address. For
248 * example, "cs4270-codec.0-004f".
202 */ 249 */
203static int mpc8610_hpcd_probe(struct of_device *ofdev, 250static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
204 const struct of_device_id *match)
205{ 251{
206 struct device_node *np = ofdev->dev.of_node;
207 struct device_node *codec_np = NULL;
208 struct device_node *guts_np = NULL;
209 struct device_node *dma_np = NULL;
210 struct device_node *dma_channel_np = NULL;
211 const phandle *codec_ph;
212 const char *sprop;
213 const u32 *iprop; 252 const u32 *iprop;
253 int bus, addr;
254 char temp[DAI_NAME_SIZE];
255
256 of_modalias_node(np, temp, DAI_NAME_SIZE);
257
258 iprop = of_get_property(np, "reg", NULL);
259 if (!iprop)
260 return -EINVAL;
261
262 addr = *iprop;
263
264 bus = get_parent_cell_index(np);
265 if (bus < 0)
266 return bus;
267
268 snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr);
269
270 return 0;
271}
272
273static int get_dma_channel(struct device_node *ssi_np,
274 const char *compatible,
275 struct snd_soc_dai_link *dai,
276 unsigned int *dma_channel_id,
277 unsigned int *dma_id)
278{
214 struct resource res; 279 struct resource res;
280 struct device_node *dma_channel_np;
281 const u32 *iprop;
282 int ret;
283
284 dma_channel_np = get_node_by_phandle_name(ssi_np, compatible,
285 "fsl,ssi-dma-channel");
286 if (!dma_channel_np)
287 return -EINVAL;
288
289 /* Determine the dev_name for the device_node. This code mimics the
290 * behavior of of_device_make_bus_id(). We need this because ASoC uses
291 * the dev_name() of the device to match the platform (DMA) device with
292 * the CPU (SSI) device. It's all ugly and hackish, but it works (for
293 * now).
294 *
295 * dai->platform name should already point to an allocated buffer.
296 */
297 ret = of_address_to_resource(dma_channel_np, 0, &res);
298 if (ret)
299 return ret;
300 snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
301 (unsigned long long) res.start, dma_channel_np->name);
302
303 iprop = of_get_property(dma_channel_np, "cell-index", NULL);
304 if (!iprop) {
305 of_node_put(dma_channel_np);
306 return -EINVAL;
307 }
308
309 *dma_channel_id = *iprop;
310 *dma_id = get_parent_cell_index(dma_channel_np);
311 of_node_put(dma_channel_np);
312
313 return 0;
314}
315
316/**
317 * mpc8610_hpcd_probe: platform probe function for the machine driver
318 *
319 * Although this is a machine driver, the SSI node is the "master" node with
320 * respect to audio hardware connections. Therefore, we create a new ASoC
321 * device for each new SSI node that has a codec attached.
322 */
323static int mpc8610_hpcd_probe(struct platform_device *pdev)
324{
325 struct device *dev = pdev->dev.parent;
326 /* of_dev is the OF device for the SSI node that probed us */
327 struct of_device *of_dev = container_of(dev, struct of_device, dev);
328 struct device_node *np = of_dev->dev.of_node;
329 struct device_node *codec_np = NULL;
215 struct platform_device *sound_device = NULL; 330 struct platform_device *sound_device = NULL;
216 struct mpc8610_hpcd_data *machine_data; 331 struct mpc8610_hpcd_data *machine_data;
217 struct fsl_ssi_info ssi_info;
218 struct fsl_dma_info dma_info;
219 int ret = -ENODEV; 332 int ret = -ENODEV;
220 unsigned int playback_dma_channel; 333 const char *sprop;
221 unsigned int capture_dma_channel; 334 const u32 *iprop;
335
336 /* We are only interested in SSIs with a codec phandle in them,
337 * so let's make sure this SSI has one. The MPC8610 HPCD only
338 * knows about the CS4270 codec, so reject anything else.
339 */
340 codec_np = get_node_by_phandle_name(np, "codec-handle",
341 "cirrus,cs4270");
342 if (!codec_np) {
343 dev_err(dev, "invalid codec node\n");
344 return -EINVAL;
345 }
222 346
223 machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); 347 machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
224 if (!machine_data) 348 if (!machine_data)
225 return -ENOMEM; 349 return -ENOMEM;
226 350
227 memset(&ssi_info, 0, sizeof(ssi_info)); 351 machine_data->dai[0].cpu_dai_name = dev_name(&of_dev->dev);
228 memset(&dma_info, 0, sizeof(dma_info)); 352 machine_data->dai[0].ops = &mpc8610_hpcd_ops;
229 353
230 ssi_info.dev = &ofdev->dev; 354 /* Determine the codec name, it will be used as the codec DAI name */
231 355 ret = codec_node_dev_name(codec_np, machine_data->codec_name,
232 /* 356 DAI_NAME_SIZE);
233 * We are only interested in SSIs with a codec phandle in them, so let's 357 if (ret) {
234 * make sure this SSI has one. 358 dev_err(&pdev->dev, "invalid codec node %s\n",
235 */ 359 codec_np->full_name);
236 codec_ph = of_get_property(np, "codec-handle", NULL); 360 ret = -EINVAL;
237 if (!codec_ph)
238 goto error; 361 goto error;
362 }
363 machine_data->dai[0].codec_name = machine_data->codec_name;
239 364
240 codec_np = of_find_node_by_phandle(*codec_ph); 365 /* The DAI name from the codec (snd_soc_dai_driver.name) */
241 if (!codec_np) 366 machine_data->dai[0].codec_dai_name = "cs4270-hifi";
242 goto error;
243 367
244 /* The MPC8610 HPCD only knows about the CS4270 codec, so reject 368 /* We register two DAIs per SSI, one for playback and the other for
245 anything else. */ 369 * capture. Currently, we only support codecs that have one DAI for
246 if (!of_device_is_compatible(codec_np, "cirrus,cs4270")) 370 * both playback and capture.
247 goto error; 371 */
372 memcpy(&machine_data->dai[1], &machine_data->dai[0],
373 sizeof(struct snd_soc_dai_link));
248 374
249 /* Get the device ID */ 375 /* Get the device ID */
250 iprop = of_get_property(np, "cell-index", NULL); 376 iprop = of_get_property(np, "cell-index", NULL);
251 if (!iprop) { 377 if (!iprop) {
252 dev_err(&ofdev->dev, "cell-index property not found\n"); 378 dev_err(&pdev->dev, "cell-index property not found\n");
253 ret = -EINVAL; 379 ret = -EINVAL;
254 goto error; 380 goto error;
255 } 381 }
256 machine_data->ssi_id = *iprop; 382 machine_data->ssi_id = *iprop;
257 ssi_info.id = *iprop;
258 383
259 /* Get the serial format and clock direction. */ 384 /* Get the serial format and clock direction. */
260 sprop = of_get_property(np, "fsl,mode", NULL); 385 sprop = of_get_property(np, "fsl,mode", NULL);
261 if (!sprop) { 386 if (!sprop) {
262 dev_err(&ofdev->dev, "fsl,mode property not found\n"); 387 dev_err(&pdev->dev, "fsl,mode property not found\n");
263 ret = -EINVAL; 388 ret = -EINVAL;
264 goto error; 389 goto error;
265 } 390 }
@@ -269,15 +394,14 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
269 machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; 394 machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
270 machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; 395 machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
271 396
272 /* 397 /* In i2s-slave mode, the codec has its own clock source, so we
273 * In i2s-slave mode, the codec has its own clock source, so we
274 * need to get the frequency from the device tree and pass it to 398 * need to get the frequency from the device tree and pass it to
275 * the codec driver. 399 * the codec driver.
276 */ 400 */
277 iprop = of_get_property(codec_np, "clock-frequency", NULL); 401 iprop = of_get_property(codec_np, "clock-frequency", NULL);
278 if (!iprop || !*iprop) { 402 if (!iprop || !*iprop) {
279 dev_err(&ofdev->dev, "codec bus-frequency property " 403 dev_err(&pdev->dev, "codec bus-frequency "
280 "is missing or invalid\n"); 404 "property is missing or invalid\n");
281 ret = -EINVAL; 405 ret = -EINVAL;
282 goto error; 406 goto error;
283 } 407 }
@@ -311,317 +435,153 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
311 machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; 435 machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
312 machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; 436 machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
313 } else { 437 } else {
314 dev_err(&ofdev->dev, 438 dev_err(&pdev->dev,
315 "unrecognized fsl,mode property \"%s\"\n", sprop); 439 "unrecognized fsl,mode property '%s'\n", sprop);
316 ret = -EINVAL; 440 ret = -EINVAL;
317 goto error; 441 goto error;
318 } 442 }
319 443
320 if (!machine_data->clk_frequency) { 444 if (!machine_data->clk_frequency) {
321 dev_err(&ofdev->dev, "unknown clock frequency\n"); 445 dev_err(&pdev->dev, "unknown clock frequency\n");
322 ret = -EINVAL; 446 ret = -EINVAL;
323 goto error; 447 goto error;
324 } 448 }
325 449
326 /* Read the SSI information from the device tree */ 450 /* Find the playback DMA channel to use. */
327 ret = of_address_to_resource(np, 0, &res); 451 machine_data->dai[0].platform_name = machine_data->platform_name[0];
452 ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0],
453 &machine_data->dma_channel_id[0],
454 &machine_data->dma_id[0]);
328 if (ret) { 455 if (ret) {
329 dev_err(&ofdev->dev, "could not obtain SSI address\n"); 456 dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
330 goto error;
331 }
332 if (!res.start) {
333 dev_err(&ofdev->dev, "invalid SSI address\n");
334 goto error;
335 }
336 ssi_info.ssi_phys = res.start;
337
338 machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi));
339 if (!machine_data->ssi) {
340 dev_err(&ofdev->dev, "could not map SSI address %x\n",
341 ssi_info.ssi_phys);
342 ret = -EINVAL;
343 goto error;
344 }
345 ssi_info.ssi = machine_data->ssi;
346
347
348 /* Get the IRQ of the SSI */
349 machine_data->ssi_irq = irq_of_parse_and_map(np, 0);
350 if (!machine_data->ssi_irq) {
351 dev_err(&ofdev->dev, "could not get SSI IRQ\n");
352 ret = -EINVAL;
353 goto error;
354 }
355 ssi_info.irq = machine_data->ssi_irq;
356
357 /* Do we want to use asynchronous mode? */
358 ssi_info.asynchronous =
359 of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0;
360 if (ssi_info.asynchronous)
361 dev_info(&ofdev->dev, "using asynchronous mode\n");
362
363 /* Map the global utilities registers. */
364 guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
365 if (!guts_np) {
366 dev_err(&ofdev->dev, "could not obtain address of GUTS\n");
367 ret = -EINVAL;
368 goto error;
369 }
370 machine_data->guts = of_iomap(guts_np, 0);
371 of_node_put(guts_np);
372 if (!machine_data->guts) {
373 dev_err(&ofdev->dev, "could not map GUTS\n");
374 ret = -EINVAL;
375 goto error;
376 }
377
378 /* Find the DMA channels to use. Both SSIs need to use the same DMA
379 * controller, so let's use DMA#1.
380 */
381 for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") {
382 iprop = of_get_property(dma_np, "cell-index", NULL);
383 if (iprop && (*iprop == 0)) {
384 of_node_put(dma_np);
385 break;
386 }
387 }
388 if (!dma_np) {
389 dev_err(&ofdev->dev, "could not find DMA node\n");
390 ret = -EINVAL;
391 goto error;
392 }
393 machine_data->dma_id = *iprop;
394
395 /* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA
396 * channels 2 and 3. This is just how the MPC8610 is wired
397 * internally.
398 */
399 playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2;
400 capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3;
401
402 /*
403 * Find the DMA channels to use.
404 */
405 while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) {
406 iprop = of_get_property(dma_channel_np, "cell-index", NULL);
407 if (iprop && (*iprop == playback_dma_channel)) {
408 /* dma_channel[0] and dma_irq[0] are for playback */
409 dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0);
410 dma_info.dma_irq[0] =
411 irq_of_parse_and_map(dma_channel_np, 0);
412 machine_data->dma_channel_id[0] = *iprop;
413 continue;
414 }
415 if (iprop && (*iprop == capture_dma_channel)) {
416 /* dma_channel[1] and dma_irq[1] are for capture */
417 dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0);
418 dma_info.dma_irq[1] =
419 irq_of_parse_and_map(dma_channel_np, 0);
420 machine_data->dma_channel_id[1] = *iprop;
421 continue;
422 }
423 }
424 if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] ||
425 !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) {
426 dev_err(&ofdev->dev, "could not find DMA channels\n");
427 ret = -EINVAL;
428 goto error; 457 goto error;
429 } 458 }
430 459
431 dma_info.ssi_stx_phys = ssi_info.ssi_phys + 460 /* Find the capture DMA channel to use. */
432 offsetof(struct ccsr_ssi, stx0); 461 machine_data->dai[1].platform_name = machine_data->platform_name[1];
433 dma_info.ssi_srx_phys = ssi_info.ssi_phys + 462 ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1],
434 offsetof(struct ccsr_ssi, srx0); 463 &machine_data->dma_channel_id[1],
435 464 &machine_data->dma_id[1]);
436 /* We have the DMA information, so tell the DMA driver what it is */ 465 if (ret) {
437 if (!fsl_dma_configure(&dma_info)) { 466 dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
438 dev_err(&ofdev->dev, "could not instantiate DMA device\n");
439 ret = -EBUSY;
440 goto error; 467 goto error;
441 } 468 }
442 469
443 /* 470 /* Initialize our DAI data structure. */
444 * Initialize our DAI data structure. We should probably get this 471 machine_data->dai[0].stream_name = "playback";
445 * information from the device tree. 472 machine_data->dai[1].stream_name = "capture";
446 */ 473 machine_data->dai[0].name = machine_data->dai[0].stream_name;
447 machine_data->dai.name = "CS4270"; 474 machine_data->dai[1].name = machine_data->dai[1].stream_name;
448 machine_data->dai.stream_name = "CS4270";
449
450 machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info);
451 machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */
452 machine_data->dai.ops = &mpc8610_hpcd_ops;
453 475
454 machine_data->machine.probe = mpc8610_hpcd_machine_probe; 476 machine_data->card.probe = mpc8610_hpcd_machine_probe;
455 machine_data->machine.remove = mpc8610_hpcd_machine_remove; 477 machine_data->card.remove = mpc8610_hpcd_machine_remove;
456 machine_data->machine.name = "MPC8610 HPCD"; 478 machine_data->card.name = pdev->name; /* The platform driver name */
457 machine_data->machine.num_links = 1; 479 machine_data->card.num_links = 2;
458 machine_data->machine.dai_link = &machine_data->dai; 480 machine_data->card.dai_link = machine_data->dai;
459 481
460 /* Allocate a new audio platform device structure */ 482 /* Allocate a new audio platform device structure */
461 sound_device = platform_device_alloc("soc-audio", -1); 483 sound_device = platform_device_alloc("soc-audio", -1);
462 if (!sound_device) { 484 if (!sound_device) {
463 dev_err(&ofdev->dev, "platform device allocation failed\n"); 485 dev_err(&pdev->dev, "platform device alloc failed\n");
464 ret = -ENOMEM; 486 ret = -ENOMEM;
465 goto error; 487 goto error;
466 } 488 }
467 489
468 machine_data->sound_devdata.card = &machine_data->machine; 490 /* Associate the card data with the sound device */
469 machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; 491 platform_set_drvdata(sound_device, &machine_data->card);
470 machine_data->machine.platform = &fsl_soc_platform;
471
472 sound_device->dev.platform_data = machine_data;
473
474 492
475 /* Set the platform device and ASoC device to point to each other */ 493 /* Register with ASoC */
476 platform_set_drvdata(sound_device, &machine_data->sound_devdata);
477
478 machine_data->sound_devdata.dev = &sound_device->dev;
479
480
481 /* Tell ASoC to probe us. This will call mpc8610_hpcd_machine.probe(),
482 if it exists. */
483 ret = platform_device_add(sound_device); 494 ret = platform_device_add(sound_device);
484
485 if (ret) { 495 if (ret) {
486 dev_err(&ofdev->dev, "platform device add failed\n"); 496 dev_err(&pdev->dev, "platform device add failed\n");
487 goto error; 497 goto error;
488 } 498 }
489 499
490 dev_set_drvdata(&ofdev->dev, sound_device); 500 of_node_put(codec_np);
491 501
492 return 0; 502 return 0;
493 503
494error: 504error:
495 of_node_put(codec_np); 505 of_node_put(codec_np);
496 of_node_put(guts_np);
497 of_node_put(dma_np);
498 of_node_put(dma_channel_np);
499 506
500 if (sound_device) 507 if (sound_device)
501 platform_device_unregister(sound_device); 508 platform_device_unregister(sound_device);
502 509
503 if (machine_data->dai.cpu_dai)
504 fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
505
506 if (ssi_info.ssi)
507 iounmap(ssi_info.ssi);
508
509 if (ssi_info.irq)
510 irq_dispose_mapping(ssi_info.irq);
511
512 if (dma_info.dma_channel[0])
513 iounmap(dma_info.dma_channel[0]);
514
515 if (dma_info.dma_channel[1])
516 iounmap(dma_info.dma_channel[1]);
517
518 if (dma_info.dma_irq[0])
519 irq_dispose_mapping(dma_info.dma_irq[0]);
520
521 if (dma_info.dma_irq[1])
522 irq_dispose_mapping(dma_info.dma_irq[1]);
523
524 if (machine_data->guts)
525 iounmap(machine_data->guts);
526
527 kfree(machine_data); 510 kfree(machine_data);
528 511
529 return ret; 512 return ret;
530} 513}
531 514
532/** 515/**
533 * mpc8610_hpcd_remove: remove the OF device 516 * mpc8610_hpcd_remove: remove the platform device
534 * 517 *
535 * This function is called when the OF device is removed. 518 * This function is called when the platform device is removed.
536 */ 519 */
537static int mpc8610_hpcd_remove(struct of_device *ofdev) 520static int __devexit mpc8610_hpcd_remove(struct platform_device *pdev)
538{ 521{
539 struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); 522 struct platform_device *sound_device = dev_get_drvdata(&pdev->dev);
523 struct snd_soc_card *card = platform_get_drvdata(sound_device);
540 struct mpc8610_hpcd_data *machine_data = 524 struct mpc8610_hpcd_data *machine_data =
541 sound_device->dev.platform_data; 525 container_of(card, struct mpc8610_hpcd_data, card);
542 526
543 platform_device_unregister(sound_device); 527 platform_device_unregister(sound_device);
544 528
545 if (machine_data->dai.cpu_dai)
546 fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
547
548 if (machine_data->ssi)
549 iounmap(machine_data->ssi);
550
551 if (machine_data->dma[0])
552 iounmap(machine_data->dma[0]);
553
554 if (machine_data->dma[1])
555 iounmap(machine_data->dma[1]);
556
557 if (machine_data->dma_irq[0])
558 irq_dispose_mapping(machine_data->dma_irq[0]);
559
560 if (machine_data->dma_irq[1])
561 irq_dispose_mapping(machine_data->dma_irq[1]);
562
563 if (machine_data->guts)
564 iounmap(machine_data->guts);
565
566 kfree(machine_data); 529 kfree(machine_data);
567 sound_device->dev.platform_data = NULL; 530 sound_device->dev.platform_data = NULL;
568 531
569 dev_set_drvdata(&ofdev->dev, NULL); 532 dev_set_drvdata(&pdev->dev, NULL);
570 533
571 return 0; 534 return 0;
572} 535}
573 536
574static struct of_device_id mpc8610_hpcd_match[] = { 537static struct platform_driver mpc8610_hpcd_driver = {
575 { 538 .probe = mpc8610_hpcd_probe,
576 .compatible = "fsl,mpc8610-ssi", 539 .remove = __devexit_p(mpc8610_hpcd_remove),
577 },
578 {}
579};
580MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match);
581
582static struct of_platform_driver mpc8610_hpcd_of_driver = {
583 .driver = { 540 .driver = {
584 .name = "mpc8610_hpcd", 541 /* The name must match the 'model' property in the device tree,
542 * in lowercase letters.
543 */
544 .name = "snd-soc-mpc8610hpcd",
585 .owner = THIS_MODULE, 545 .owner = THIS_MODULE,
586 .of_match_table = mpc8610_hpcd_match,
587 }, 546 },
588 .probe = mpc8610_hpcd_probe,
589 .remove = mpc8610_hpcd_remove,
590}; 547};
591 548
592/** 549/**
593 * mpc8610_hpcd_init: fabric driver initialization. 550 * mpc8610_hpcd_init: machine driver initialization.
594 * 551 *
595 * This function is called when this module is loaded. 552 * This function is called when this module is loaded.
596 */ 553 */
597static int __init mpc8610_hpcd_init(void) 554static int __init mpc8610_hpcd_init(void)
598{ 555{
599 int ret; 556 struct device_node *guts_np;
600 557 struct resource res;
601 printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n");
602 558
603 ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); 559 pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n");
604 560
605 if (ret) 561 /* Get the physical address of the global utilities registers */
606 printk(KERN_ERR 562 guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
607 "mpc8610-hpcd: failed to register platform driver\n"); 563 if (of_address_to_resource(guts_np, 0, &res)) {
564 pr_err("mpc8610-hpcd: missing/invalid global utilities node\n");
565 return -EINVAL;
566 }
567 guts_phys = res.start;
608 568
609 return ret; 569 return platform_driver_register(&mpc8610_hpcd_driver);
610} 570}
611 571
612/** 572/**
613 * mpc8610_hpcd_exit: fabric driver exit 573 * mpc8610_hpcd_exit: machine driver exit
614 * 574 *
615 * This function is called when this driver is unloaded. 575 * This function is called when this driver is unloaded.
616 */ 576 */
617static void __exit mpc8610_hpcd_exit(void) 577static void __exit mpc8610_hpcd_exit(void)
618{ 578{
619 of_unregister_platform_driver(&mpc8610_hpcd_of_driver); 579 platform_driver_unregister(&mpc8610_hpcd_driver);
620} 580}
621 581
622module_init(mpc8610_hpcd_init); 582module_init(mpc8610_hpcd_init);
623module_exit(mpc8610_hpcd_exit); 583module_exit(mpc8610_hpcd_exit);
624 584
625MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 585MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
626MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); 586MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver");
627MODULE_LICENSE("GPL"); 587MODULE_LICENSE("GPL v2");