2 * Freescale P1022DS ALSA SoC Machine driver
4 * Author: Timur Tabi <timur@freescale.com>
6 * Copyright 2010 Freescale Semiconductor, Inc.
8 * This file is licensed under the terms of the GNU General Public License
9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
13 #include <linux/module.h>
14 #include <linux/interrupt.h>
15 #include <linux/of_device.h>
16 #include <linux/slab.h>
17 #include <linux/of_i2c.h>
18 #include <sound/soc.h>
19 #include <asm/fsl_guts.h>
24 /* P1022-specific PMUXCR and DMUXCR bit definitions */
26 #define CCSR_GUTS_PMUXCR_UART0_I2C1_MASK 0x0001c000
27 #define CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI 0x00010000
28 #define CCSR_GUTS_PMUXCR_UART0_I2C1_SSI 0x00018000
30 #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK 0x00000c00
31 #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI 0x00000000
33 #define CCSR_GUTS_DMUXCR_PAD 1 /* DMA controller/channel set to pad */
34 #define CCSR_GUTS_DMUXCR_SSI 2 /* DMA controller/channel set to SSI */
37 * Set the DMACR register in the GUTS
39 * The DMACR register determines the source of initiated transfers for each
40 * channel on each DMA controller. Rather than have a bunch of repetitive
41 * macros for the bit patterns, we just have a function that calculates
44 * guts: Pointer to GUTS structure
45 * co: The DMA controller (0 or 1)
46 * ch: The channel on the DMA controller (0, 1, 2, or 3)
47 * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx)
49 static inline void guts_set_dmuxcr(struct ccsr_guts_85xx __iomem
*guts
,
50 unsigned int co
, unsigned int ch
, unsigned int device
)
52 unsigned int shift
= 16 + (8 * (1 - co
) + 2 * (3 - ch
));
54 clrsetbits_be32(&guts
->dmuxcr
, 3 << shift
, device
<< shift
);
57 /* There's only one global utilities register */
58 static phys_addr_t guts_phys
;
60 #define DAI_NAME_SIZE 32
63 * machine_data: machine-specific ASoC device data
65 * This structure contains data for a single sound platform device on an
66 * P1022 DS. Some of the data is taken from the device tree.
69 struct snd_soc_dai_link dai
[2];
70 struct snd_soc_card card
;
71 unsigned int dai_format
;
72 unsigned int codec_clk_direction
;
73 unsigned int cpu_clk_direction
;
74 unsigned int clk_frequency
;
75 unsigned int ssi_id
; /* 0 = SSI1, 1 = SSI2, etc */
76 unsigned int dma_id
[2]; /* 0 = DMA1, 1 = DMA2, etc */
77 unsigned int dma_channel_id
[2]; /* 0 = ch 0, 1 = ch 1, etc*/
78 char codec_name
[DAI_NAME_SIZE
];
79 char platform_name
[2][DAI_NAME_SIZE
]; /* One for each DMA channel */
83 * p1022_ds_machine_probe: initialize the board
85 * This function is used to initialize the board-specific hardware.
87 * Here we program the DMACR and PMUXCR registers.
89 static int p1022_ds_machine_probe(struct snd_soc_card
*card
)
91 struct machine_data
*mdata
=
92 container_of(card
, struct machine_data
, card
);
93 struct ccsr_guts_85xx __iomem
*guts
;
95 guts
= ioremap(guts_phys
, sizeof(struct ccsr_guts_85xx
));
97 dev_err(card
->dev
, "could not map global utilities\n");
101 /* Enable SSI Tx signal */
102 clrsetbits_be32(&guts
->pmuxcr
, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK
,
103 CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI
);
105 /* Enable SSI Rx signal */
106 clrsetbits_be32(&guts
->pmuxcr
, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK
,
107 CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI
);
109 /* Enable DMA Channel for SSI */
110 guts_set_dmuxcr(guts
, mdata
->dma_id
[0], mdata
->dma_channel_id
[0],
111 CCSR_GUTS_DMUXCR_SSI
);
113 guts_set_dmuxcr(guts
, mdata
->dma_id
[1], mdata
->dma_channel_id
[1],
114 CCSR_GUTS_DMUXCR_SSI
);
122 * p1022_ds_startup: program the board with various hardware parameters
124 * This function takes board-specific information, like clock frequencies
125 * and serial data formats, and passes that information to the codec and
128 static int p1022_ds_startup(struct snd_pcm_substream
*substream
)
130 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
131 struct machine_data
*mdata
=
132 container_of(rtd
->card
, struct machine_data
, card
);
133 struct device
*dev
= rtd
->card
->dev
;
136 /* Tell the codec driver what the serial protocol is. */
137 ret
= snd_soc_dai_set_fmt(rtd
->codec_dai
, mdata
->dai_format
);
139 dev_err(dev
, "could not set codec driver audio format\n");
144 * Tell the codec driver what the MCLK frequency is, and whether it's
147 ret
= snd_soc_dai_set_sysclk(rtd
->codec_dai
, 0, mdata
->clk_frequency
,
148 mdata
->codec_clk_direction
);
150 dev_err(dev
, "could not set codec driver clock params\n");
158 * p1022_ds_machine_remove: Remove the sound device
160 * This function is called to remove the sound device for one SSI. We
161 * de-program the DMACR and PMUXCR register.
163 static int p1022_ds_machine_remove(struct snd_soc_card
*card
)
165 struct machine_data
*mdata
=
166 container_of(card
, struct machine_data
, card
);
167 struct ccsr_guts_85xx __iomem
*guts
;
169 guts
= ioremap(guts_phys
, sizeof(struct ccsr_guts_85xx
));
171 dev_err(card
->dev
, "could not map global utilities\n");
175 /* Restore the signal routing */
176 clrbits32(&guts
->pmuxcr
, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK
);
177 clrbits32(&guts
->pmuxcr
, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK
);
178 guts_set_dmuxcr(guts
, mdata
->dma_id
[0], mdata
->dma_channel_id
[0], 0);
179 guts_set_dmuxcr(guts
, mdata
->dma_id
[1], mdata
->dma_channel_id
[1], 0);
187 * p1022_ds_ops: ASoC machine driver operations
189 static struct snd_soc_ops p1022_ds_ops
= {
190 .startup
= p1022_ds_startup
,
194 * get_node_by_phandle_name - get a node by its phandle name
196 * This function takes a node, the name of a property in that node, and a
197 * compatible string. Assuming the property is a phandle to another node,
198 * it returns that node, (optionally) if that node is compatible.
200 * If the property is not a phandle, or the node it points to is not compatible
201 * with the specific string, then NULL is returned.
203 static struct device_node
*get_node_by_phandle_name(struct device_node
*np
,
204 const char *name
, const char *compatible
)
206 np
= of_parse_phandle(np
, name
, 0);
210 if (!of_device_is_compatible(np
, compatible
)) {
219 * get_parent_cell_index -- return the cell-index of the parent of a node
221 * Return the value of the cell-index property of the parent of the given
222 * node. This is used for DMA channel nodes that need to know the DMA ID
223 * of the controller they are on.
225 static int get_parent_cell_index(struct device_node
*np
)
227 struct device_node
*parent
= of_get_parent(np
);
234 iprop
= of_get_property(parent
, "cell-index", NULL
);
236 ret
= be32_to_cpup(iprop
);
244 * codec_node_dev_name - determine the dev_name for a codec node
246 * This function determines the dev_name for an I2C node. This is the name
247 * that would be returned by dev_name() if this device_node were part of a
248 * 'struct device' It's ugly and hackish, but it works.
250 * The dev_name for such devices include the bus number and I2C address. For
251 * example, "cs4270-codec.0-004f".
253 static int codec_node_dev_name(struct device_node
*np
, char *buf
, size_t len
)
257 char temp
[DAI_NAME_SIZE
];
258 struct i2c_client
*i2c
;
260 of_modalias_node(np
, temp
, DAI_NAME_SIZE
);
262 iprop
= of_get_property(np
, "reg", NULL
);
266 addr
= be32_to_cpup(iprop
);
268 /* We need the adapter number */
269 i2c
= of_find_i2c_device_by_node(np
);
273 snprintf(buf
, len
, "%s.%u-%04x", temp
, i2c
->adapter
->nr
, addr
);
278 static int get_dma_channel(struct device_node
*ssi_np
,
279 const char *compatible
,
280 struct snd_soc_dai_link
*dai
,
281 unsigned int *dma_channel_id
,
282 unsigned int *dma_id
)
285 struct device_node
*dma_channel_np
;
289 dma_channel_np
= get_node_by_phandle_name(ssi_np
, compatible
,
290 "fsl,ssi-dma-channel");
294 /* Determine the dev_name for the device_node. This code mimics the
295 * behavior of of_device_make_bus_id(). We need this because ASoC uses
296 * the dev_name() of the device to match the platform (DMA) device with
297 * the CPU (SSI) device. It's all ugly and hackish, but it works (for
300 * dai->platform name should already point to an allocated buffer.
302 ret
= of_address_to_resource(dma_channel_np
, 0, &res
);
304 of_node_put(dma_channel_np
);
307 snprintf((char *)dai
->platform_name
, DAI_NAME_SIZE
, "%llx.%s",
308 (unsigned long long) res
.start
, dma_channel_np
->name
);
310 iprop
= of_get_property(dma_channel_np
, "cell-index", NULL
);
312 of_node_put(dma_channel_np
);
316 *dma_channel_id
= be32_to_cpup(iprop
);
317 *dma_id
= get_parent_cell_index(dma_channel_np
);
318 of_node_put(dma_channel_np
);
324 * p1022_ds_probe: platform probe function for the machine driver
326 * Although this is a machine driver, the SSI node is the "master" node with
327 * respect to audio hardware connections. Therefore, we create a new ASoC
328 * device for each new SSI node that has a codec attached.
330 static int p1022_ds_probe(struct platform_device
*pdev
)
332 struct device
*dev
= pdev
->dev
.parent
;
333 /* ssi_pdev is the platform device for the SSI node that probed us */
334 struct platform_device
*ssi_pdev
=
335 container_of(dev
, struct platform_device
, dev
);
336 struct device_node
*np
= ssi_pdev
->dev
.of_node
;
337 struct device_node
*codec_np
= NULL
;
338 struct platform_device
*sound_device
= NULL
;
339 struct machine_data
*mdata
;
344 /* Find the codec node for this SSI. */
345 codec_np
= of_parse_phandle(np
, "codec-handle", 0);
347 dev_err(dev
, "could not find codec node\n");
351 mdata
= kzalloc(sizeof(struct machine_data
), GFP_KERNEL
);
357 mdata
->dai
[0].cpu_dai_name
= dev_name(&ssi_pdev
->dev
);
358 mdata
->dai
[0].ops
= &p1022_ds_ops
;
360 /* Determine the codec name, it will be used as the codec DAI name */
361 ret
= codec_node_dev_name(codec_np
, mdata
->codec_name
, DAI_NAME_SIZE
);
363 dev_err(&pdev
->dev
, "invalid codec node %s\n",
364 codec_np
->full_name
);
368 mdata
->dai
[0].codec_name
= mdata
->codec_name
;
370 /* We register two DAIs per SSI, one for playback and the other for
371 * capture. We support codecs that have separate DAIs for both playback
374 memcpy(&mdata
->dai
[1], &mdata
->dai
[0], sizeof(struct snd_soc_dai_link
));
376 /* The DAI names from the codec (snd_soc_dai_driver.name) */
377 mdata
->dai
[0].codec_dai_name
= "wm8776-hifi-playback";
378 mdata
->dai
[1].codec_dai_name
= "wm8776-hifi-capture";
380 /* Get the device ID */
381 iprop
= of_get_property(np
, "cell-index", NULL
);
383 dev_err(&pdev
->dev
, "cell-index property not found\n");
387 mdata
->ssi_id
= be32_to_cpup(iprop
);
389 /* Get the serial format and clock direction. */
390 sprop
= of_get_property(np
, "fsl,mode", NULL
);
392 dev_err(&pdev
->dev
, "fsl,mode property not found\n");
397 if (strcasecmp(sprop
, "i2s-slave") == 0) {
398 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
399 SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_CBM_CFM
;
400 mdata
->codec_clk_direction
= SND_SOC_CLOCK_OUT
;
401 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_IN
;
403 /* In i2s-slave mode, the codec has its own clock source, so we
404 * need to get the frequency from the device tree and pass it to
407 iprop
= of_get_property(codec_np
, "clock-frequency", NULL
);
408 if (!iprop
|| !*iprop
) {
409 dev_err(&pdev
->dev
, "codec bus-frequency "
410 "property is missing or invalid\n");
414 mdata
->clk_frequency
= be32_to_cpup(iprop
);
415 } else if (strcasecmp(sprop
, "i2s-master") == 0) {
416 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
417 SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_CBS_CFS
;
418 mdata
->codec_clk_direction
= SND_SOC_CLOCK_IN
;
419 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_OUT
;
420 } else if (strcasecmp(sprop
, "lj-slave") == 0) {
421 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
422 SND_SOC_DAIFMT_LEFT_J
| SND_SOC_DAIFMT_CBM_CFM
;
423 mdata
->codec_clk_direction
= SND_SOC_CLOCK_OUT
;
424 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_IN
;
425 } else if (strcasecmp(sprop
, "lj-master") == 0) {
426 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
427 SND_SOC_DAIFMT_LEFT_J
| SND_SOC_DAIFMT_CBS_CFS
;
428 mdata
->codec_clk_direction
= SND_SOC_CLOCK_IN
;
429 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_OUT
;
430 } else if (strcasecmp(sprop
, "rj-slave") == 0) {
431 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
432 SND_SOC_DAIFMT_RIGHT_J
| SND_SOC_DAIFMT_CBM_CFM
;
433 mdata
->codec_clk_direction
= SND_SOC_CLOCK_OUT
;
434 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_IN
;
435 } else if (strcasecmp(sprop
, "rj-master") == 0) {
436 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
437 SND_SOC_DAIFMT_RIGHT_J
| SND_SOC_DAIFMT_CBS_CFS
;
438 mdata
->codec_clk_direction
= SND_SOC_CLOCK_IN
;
439 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_OUT
;
440 } else if (strcasecmp(sprop
, "ac97-slave") == 0) {
441 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
442 SND_SOC_DAIFMT_AC97
| SND_SOC_DAIFMT_CBM_CFM
;
443 mdata
->codec_clk_direction
= SND_SOC_CLOCK_OUT
;
444 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_IN
;
445 } else if (strcasecmp(sprop
, "ac97-master") == 0) {
446 mdata
->dai_format
= SND_SOC_DAIFMT_NB_NF
|
447 SND_SOC_DAIFMT_AC97
| SND_SOC_DAIFMT_CBS_CFS
;
448 mdata
->codec_clk_direction
= SND_SOC_CLOCK_IN
;
449 mdata
->cpu_clk_direction
= SND_SOC_CLOCK_OUT
;
452 "unrecognized fsl,mode property '%s'\n", sprop
);
457 if (!mdata
->clk_frequency
) {
458 dev_err(&pdev
->dev
, "unknown clock frequency\n");
463 /* Find the playback DMA channel to use. */
464 mdata
->dai
[0].platform_name
= mdata
->platform_name
[0];
465 ret
= get_dma_channel(np
, "fsl,playback-dma", &mdata
->dai
[0],
466 &mdata
->dma_channel_id
[0],
469 dev_err(&pdev
->dev
, "missing/invalid playback DMA phandle\n");
473 /* Find the capture DMA channel to use. */
474 mdata
->dai
[1].platform_name
= mdata
->platform_name
[1];
475 ret
= get_dma_channel(np
, "fsl,capture-dma", &mdata
->dai
[1],
476 &mdata
->dma_channel_id
[1],
479 dev_err(&pdev
->dev
, "missing/invalid capture DMA phandle\n");
483 /* Initialize our DAI data structure. */
484 mdata
->dai
[0].stream_name
= "playback";
485 mdata
->dai
[1].stream_name
= "capture";
486 mdata
->dai
[0].name
= mdata
->dai
[0].stream_name
;
487 mdata
->dai
[1].name
= mdata
->dai
[1].stream_name
;
489 mdata
->card
.probe
= p1022_ds_machine_probe
;
490 mdata
->card
.remove
= p1022_ds_machine_remove
;
491 mdata
->card
.name
= pdev
->name
; /* The platform driver name */
492 mdata
->card
.num_links
= 2;
493 mdata
->card
.dai_link
= mdata
->dai
;
495 /* Allocate a new audio platform device structure */
496 sound_device
= platform_device_alloc("soc-audio", -1);
498 dev_err(&pdev
->dev
, "platform device alloc failed\n");
503 /* Associate the card data with the sound device */
504 platform_set_drvdata(sound_device
, &mdata
->card
);
506 /* Register with ASoC */
507 ret
= platform_device_add(sound_device
);
509 dev_err(&pdev
->dev
, "platform device add failed\n");
512 dev_set_drvdata(&pdev
->dev
, sound_device
);
514 of_node_put(codec_np
);
520 platform_device_put(sound_device
);
524 of_node_put(codec_np
);
529 * p1022_ds_remove: remove the platform device
531 * This function is called when the platform device is removed.
533 static int __devexit
p1022_ds_remove(struct platform_device
*pdev
)
535 struct platform_device
*sound_device
= dev_get_drvdata(&pdev
->dev
);
536 struct snd_soc_card
*card
= platform_get_drvdata(sound_device
);
537 struct machine_data
*mdata
=
538 container_of(card
, struct machine_data
, card
);
540 platform_device_unregister(sound_device
);
543 sound_device
->dev
.platform_data
= NULL
;
545 dev_set_drvdata(&pdev
->dev
, NULL
);
550 static struct platform_driver p1022_ds_driver
= {
551 .probe
= p1022_ds_probe
,
552 .remove
= __devexit_p(p1022_ds_remove
),
554 .owner
= THIS_MODULE
,
559 * p1022_ds_init: machine driver initialization.
561 * This function is called when this module is loaded.
563 static int __init
p1022_ds_init(void)
565 struct device_node
*guts_np
;
570 * Check if we're actually running on a P1022DS. Older device trees
571 * have a model of "fsl,P1022" and newer ones use "fsl,P1022DS", so we
572 * need to support both. The SSI driver uses that property to link to
573 * the machine driver, so have to match it.
575 sprop
= of_get_property(of_find_node_by_path("/"), "model", NULL
);
577 pr_err("snd-soc-p1022ds: missing /model node");
581 pr_debug("snd-soc-p1022ds: board model name is %s\n", sprop
);
584 * The name of this board, taken from the device tree. Normally, this is a*
585 * fixed string, but some P1022DS device trees have a /model property of
586 * "fsl,P1022", and others have "fsl,P1022DS".
588 if (strcasecmp(sprop
, "fsl,p1022ds") == 0)
589 p1022_ds_driver
.driver
.name
= "snd-soc-p1022ds";
590 else if (strcasecmp(sprop
, "fsl,p1022") == 0)
591 p1022_ds_driver
.driver
.name
= "snd-soc-p1022";
595 /* Get the physical address of the global utilities registers */
596 guts_np
= of_find_compatible_node(NULL
, NULL
, "fsl,p1022-guts");
597 if (of_address_to_resource(guts_np
, 0, &res
)) {
598 pr_err("snd-soc-p1022ds: missing/invalid global utils node\n");
599 of_node_put(guts_np
);
602 guts_phys
= res
.start
;
603 of_node_put(guts_np
);
605 return platform_driver_register(&p1022_ds_driver
);
609 * p1022_ds_exit: machine driver exit
611 * This function is called when this driver is unloaded.
613 static void __exit
p1022_ds_exit(void)
615 platform_driver_unregister(&p1022_ds_driver
);
618 module_init(p1022_ds_init
);
619 module_exit(p1022_ds_exit
);
621 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
622 MODULE_DESCRIPTION("Freescale P1022 DS ALSA SoC machine driver");
623 MODULE_LICENSE("GPL v2");