aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 16:05:43 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 16:05:43 -0400
commit2e341ca686042aa464efa755447e7bcee91d1eb6 (patch)
treec6b16b6b6a6e871fa04396cb2c7eb759bcad5be3 /sound/soc/fsl
parent927ad551031798d4cba49766549600bbb33872d7 (diff)
parent85e184e4c3cd3e2285ceab91ff8f0cac094e8a85 (diff)
Merge tag 'sound-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "This is the first big chunk for 3.5 merges of sound stuff. There are a few big changes in different areas. First off, the streaming logic of USB-audio endpoints has been largely rewritten for the better support of "implicit feedback". If anything about USB got broken, this change has to be checked. For HD-audio, the resume procedure was changed; instead of delaying the resume of the hardware until the first use, now waking up immediately at resume. This is for buggy BIOS. For ASoC, dynamic PCM support and the improved support for digital links between off-SoC devices are major framework changes. Some highlights are below: * HD-audio - Avoid accesses of invalid pin-control bits that may stall the codec - V-ref setup cleanups - Fix the races in power-saving code - Fix the races in codec cache hashes and connection lists - Split some common codes for BIOS auto-parser to hda_auto_parser.c - Changed the PM resume code to wake up immediately for buggy BIOS - Creative SoundCore3D support - Add Conexant CX20751/2/3/4 codec support * ASoC - Dynamic PCM support, allowing support for SoCs with internal routing through components with tight sequencing and formatting constraints within their internal paths or where there are multiple components connected with CPU managed DMA controllers inside the SoC. - Greatly improved support for direct digital links between off-SoC devices, providing a much simpler way of connecting things like digital basebands to CODECs. - Much more fine grained and robust locking, cleaning up some of the confusion that crept in with multi-component. - CPU support for nVidia Tegra 30 I2S and audio hub controllers and ST-Ericsson MSP I2S controolers - New CODEC drivers for Cirrus CS42L52, LAPIS Semiconductor ML26124, Texas Instruments LM49453. - Some regmap changes needed by the Tegra I2S driver. - mc13783 audio support. * Misc - Rewrite with module_pci_driver() - Xonar DGX support for snd-oxygen - Improvement of packet handling in snd-firewire driver - New USB-endpoint streaming logic - Enhanced M-audio FTU quirks and relevant cleanups - Increment the support of OSS devices to 256 - snd-aloop accuracy improvement There are a few more pending changes for 3.5, but they will be sent slightly later as partly depending on the changes of DRM." Fix up conflicts in regmap (due to duplicate patches, with some further updates then having already come in from the regmap tree). Also some fairly trivial context conflicts in the imx and mcx soc drivers. * tag 'sound-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (280 commits) ALSA: snd-usb: fix stream info output in /proc ALSA: pcm - Add proper state checks to snd_pcm_drain() ALSA: sh: Fix up namespace collision in sh_dac_audio. ALSA: hda/realtek - Fix unused variable compile warning ASoC: sh: fsi: enable chip specific data transfer mode ASoC: sh: fsi: call fsi_hw_startup/shutdown from fsi_dai_trigger() ASoC: sh: fsi: use same format for IN/OUT ASoC: sh: fsi: add fsi_version() and removed meaningless version check ASoC: sh: fsi: use register field macro name on IN/OUT_DMAC ASoC: tegra: Add machine driver for WM8753 codec ALSA: hda - Fix possible races of accesses to connection list array ASoC: OMAP: HDMI: Introduce codec ARM: mx31_3ds: Add sound support ASoC: imx-mc13783 cleanup mx31moboard: Add sound support ASoC: mc13783 codec cleanups ASoC: add imx-mc13783 sound support ASoC: Add mc13783 codec mfd: mc13xxx: add codec platform data ASoC: don't flip master of DT-instantiated DAI links ...
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r--sound/soc/fsl/Kconfig129
-rw-r--r--sound/soc/fsl/Makefile31
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c164
-rw-r--r--sound/soc/fsl/fsl_ssi.c167
-rw-r--r--sound/soc/fsl/fsl_utils.c91
-rw-r--r--sound/soc/fsl/fsl_utils.h26
-rw-r--r--sound/soc/fsl/imx-audmux.c311
-rw-r--r--sound/soc/fsl/imx-audmux.h60
-rw-r--r--sound/soc/fsl/imx-mc13783.c156
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c176
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c336
-rw-r--r--sound/soc/fsl/imx-pcm.c105
-rw-r--r--sound/soc/fsl/imx-pcm.h33
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c221
-rw-r--r--sound/soc/fsl/imx-ssi.c690
-rw-r--r--sound/soc/fsl/imx-ssi.h216
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c166
-rw-r--r--sound/soc/fsl/mx27vis-aic32x4.c245
-rw-r--r--sound/soc/fsl/p1022_ds.c158
-rw-r--r--sound/soc/fsl/phycore-ac97.c125
-rw-r--r--sound/soc/fsl/wm1133-ev1.c304
21 files changed, 3569 insertions, 341 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index d754d34d68a6..d70133086ac3 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,18 +1,31 @@
1config SND_MPC52xx_DMA 1config SND_SOC_FSL_SSI
2 tristate 2 tristate
3 3
4# ASoC platform support for the Freescale PowerPC SOCs that have an SSI and 4config SND_SOC_FSL_UTILS
5# an Elo DMA controller, such as the MPC8610 and P1022. You will still need to
6# select a platform driver and a codec driver.
7config SND_SOC_POWERPC_SSI
8 tristate 5 tristate
6
7menuconfig SND_POWERPC_SOC
8 tristate "SoC Audio for Freescale PowerPC CPUs"
9 depends on FSL_SOC 9 depends on FSL_SOC
10 help
11 Say Y or M if you want to add support for codecs attached to
12 the PowerPC CPUs.
13
14if SND_POWERPC_SOC
15
16config SND_MPC52xx_DMA
17 tristate
18
19config SND_SOC_POWERPC_DMA
20 tristate
10 21
11config SND_SOC_MPC8610_HPCD 22config SND_SOC_MPC8610_HPCD
12 tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" 23 tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
13 # I2C is necessary for the CS4270 driver 24 # I2C is necessary for the CS4270 driver
14 depends on MPC8610_HPCD && I2C 25 depends on MPC8610_HPCD && I2C
15 select SND_SOC_POWERPC_SSI 26 select SND_SOC_FSL_SSI
27 select SND_SOC_FSL_UTILS
28 select SND_SOC_POWERPC_DMA
16 select SND_SOC_CS4270 29 select SND_SOC_CS4270
17 select SND_SOC_CS4270_VD33_ERRATA 30 select SND_SOC_CS4270_VD33_ERRATA
18 default y if MPC8610_HPCD 31 default y if MPC8610_HPCD
@@ -23,7 +36,9 @@ config SND_SOC_P1022_DS
23 tristate "ALSA SoC support for the Freescale P1022 DS board" 36 tristate "ALSA SoC support for the Freescale P1022 DS board"
24 # I2C is necessary for the WM8776 driver 37 # I2C is necessary for the WM8776 driver
25 depends on P1022_DS && I2C 38 depends on P1022_DS && I2C
26 select SND_SOC_POWERPC_SSI 39 select SND_SOC_FSL_SSI
40 select SND_SOC_FSL_UTILS
41 select SND_SOC_POWERPC_DMA
27 select SND_SOC_WM8776 42 select SND_SOC_WM8776
28 default y if P1022_DS 43 default y if P1022_DS
29 help 44 help
@@ -65,3 +80,103 @@ config SND_MPC52xx_SOC_EFIKA
65 help 80 help
66 Say Y if you want to add support for sound on the Efika. 81 Say Y if you want to add support for sound on the Efika.
67 82
83endif # SND_POWERPC_SOC
84
85menuconfig SND_IMX_SOC
86 tristate "SoC Audio for Freescale i.MX CPUs"
87 depends on ARCH_MXC
88 help
89 Say Y or M if you want to add support for codecs attached to
90 the i.MX CPUs.
91
92if SND_IMX_SOC
93
94config SND_SOC_IMX_SSI
95 tristate
96
97config SND_SOC_IMX_PCM
98 tristate
99
100config SND_SOC_IMX_PCM_FIQ
101 tristate
102 select FIQ
103 select SND_SOC_IMX_PCM
104
105config SND_SOC_IMX_PCM_DMA
106 tristate
107 select SND_SOC_DMAENGINE_PCM
108 select SND_SOC_IMX_PCM
109
110config SND_SOC_IMX_AUDMUX
111 tristate
112
113config SND_MXC_SOC_WM1133_EV1
114 tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
115 depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
116 select SND_SOC_WM8350
117 select SND_SOC_IMX_PCM_FIQ
118 select SND_SOC_IMX_AUDMUX
119 select SND_SOC_IMX_SSI
120 help
121 Enable support for audio on the i.MX31ADS with the WM1133-EV1
122 PMIC board with WM8835x fitted.
123
124config SND_SOC_MX27VIS_AIC32X4
125 tristate "SoC audio support for Visstrim M10 boards"
126 depends on MACH_IMX27_VISSTRIM_M10 && I2C
127 select SND_SOC_TLV320AIC32X4
128 select SND_SOC_IMX_PCM_DMA
129 select SND_SOC_IMX_AUDMUX
130 select SND_SOC_IMX_SSI
131 help
132 Say Y if you want to add support for SoC audio on Visstrim SM10
133 board with TLV320AIC32X4 codec.
134
135config SND_SOC_PHYCORE_AC97
136 tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
137 depends on MACH_PCM043 || MACH_PCA100
138 select SND_SOC_AC97_BUS
139 select SND_SOC_WM9712
140 select SND_SOC_IMX_PCM_FIQ
141 select SND_SOC_IMX_AUDMUX
142 select SND_SOC_IMX_SSI
143 help
144 Say Y if you want to add support for SoC audio on Phytec phyCORE
145 and phyCARD boards in AC97 mode
146
147config SND_SOC_EUKREA_TLV320
148 tristate "Eukrea TLV320"
149 depends on MACH_EUKREA_MBIMX27_BASEBOARD \
150 || MACH_EUKREA_MBIMXSD25_BASEBOARD \
151 || MACH_EUKREA_MBIMXSD35_BASEBOARD \
152 || MACH_EUKREA_MBIMXSD51_BASEBOARD
153 depends on I2C
154 select SND_SOC_TLV320AIC23
155 select SND_SOC_IMX_PCM_FIQ
156 select SND_SOC_IMX_AUDMUX
157 select SND_SOC_IMX_SSI
158 help
159 Enable I2S based access to the TLV320AIC23B codec attached
160 to the SSI interface
161
162config SND_SOC_IMX_SGTL5000
163 tristate "SoC Audio support for i.MX boards with sgtl5000"
164 depends on OF && I2C
165 select SND_SOC_SGTL5000
166 select SND_SOC_IMX_PCM_DMA
167 select SND_SOC_IMX_AUDMUX
168 select SND_SOC_FSL_SSI
169 select SND_SOC_FSL_UTILS
170 help
171 Say Y if you want to add support for SoC audio on an i.MX board with
172 a sgtl5000 codec.
173
174config SND_SOC_IMX_MC13783
175 tristate "SoC Audio support for I.MX boards with mc13783"
176 depends on MFD_MC13783
177 select SND_SOC_IMX_SSI
178 select SND_SOC_IMX_AUDMUX
179 select SND_SOC_MC13783
180 select SND_SOC_IMX_PCM_DMA
181
182endif # SND_IMX_SOC
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index b4a38c0ac58c..5f3cf3f52ea0 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -8,8 +8,11 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o
8 8
9# Freescale PowerPC SSI/DMA Platform Support 9# Freescale PowerPC SSI/DMA Platform Support
10snd-soc-fsl-ssi-objs := fsl_ssi.o 10snd-soc-fsl-ssi-objs := fsl_ssi.o
11snd-soc-fsl-utils-objs := fsl_utils.o
11snd-soc-fsl-dma-objs := fsl_dma.o 12snd-soc-fsl-dma-objs := fsl_dma.o
12obj-$(CONFIG_SND_SOC_POWERPC_SSI) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o 13obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
14obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
15obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
13 16
14# MPC5200 Platform Support 17# MPC5200 Platform Support
15obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o 18obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
@@ -20,3 +23,29 @@ obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o
20obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o 23obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
21obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o 24obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
22 25
26# i.MX Platform Support
27snd-soc-imx-ssi-objs := imx-ssi.o
28snd-soc-imx-audmux-objs := imx-audmux.o
29
30obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
31obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
32
33obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
34snd-soc-imx-pcm-y := imx-pcm.o
35snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
36snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
37
38# i.MX Machine Support
39snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
40snd-soc-phycore-ac97-objs := phycore-ac97.o
41snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
42snd-soc-wm1133-ev1-objs := wm1133-ev1.o
43snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
44snd-soc-imx-mc13783-objs := imx-mc13783.o
45
46obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
47obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
48obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
49obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
50obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
51obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
new file mode 100644
index 000000000000..efb9ede01208
--- /dev/null
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -0,0 +1,164 @@
1/*
2 * eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
3 *
4 * Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
5 *
6 * based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
7 * which is Copyright 2009 Simtec Electronics
8 * and on sound/soc/imx/phycore-ac97.c which is
9 * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/moduleparam.h>
20#include <linux/device.h>
21#include <linux/i2c.h>
22#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/soc.h>
25#include <asm/mach-types.h>
26
27#include "../codecs/tlv320aic23.h"
28#include "imx-ssi.h"
29#include "imx-audmux.h"
30
31#define CODEC_CLOCK 12000000
32
33static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
34 struct snd_pcm_hw_params *params)
35{
36 struct snd_soc_pcm_runtime *rtd = substream->private_data;
37 struct snd_soc_dai *codec_dai = rtd->codec_dai;
38 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
39 int ret;
40
41 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
42 SND_SOC_DAIFMT_NB_NF |
43 SND_SOC_DAIFMT_CBM_CFM);
44 if (ret) {
45 pr_err("%s: failed set cpu dai format\n", __func__);
46 return ret;
47 }
48
49 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
50 SND_SOC_DAIFMT_NB_NF |
51 SND_SOC_DAIFMT_CBM_CFM);
52 if (ret) {
53 pr_err("%s: failed set codec dai format\n", __func__);
54 return ret;
55 }
56
57 ret = snd_soc_dai_set_sysclk(codec_dai, 0,
58 CODEC_CLOCK, SND_SOC_CLOCK_OUT);
59 if (ret) {
60 pr_err("%s: failed setting codec sysclk\n", __func__);
61 return ret;
62 }
63 snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
64
65 ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
66 SND_SOC_CLOCK_IN);
67 if (ret) {
68 pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
69 return ret;
70 }
71
72 return 0;
73}
74
75static struct snd_soc_ops eukrea_tlv320_snd_ops = {
76 .hw_params = eukrea_tlv320_hw_params,
77};
78
79static struct snd_soc_dai_link eukrea_tlv320_dai = {
80 .name = "tlv320aic23",
81 .stream_name = "TLV320AIC23",
82 .codec_dai_name = "tlv320aic23-hifi",
83 .platform_name = "imx-fiq-pcm-audio.0",
84 .codec_name = "tlv320aic23-codec.0-001a",
85 .cpu_dai_name = "imx-ssi.0",
86 .ops = &eukrea_tlv320_snd_ops,
87};
88
89static struct snd_soc_card eukrea_tlv320 = {
90 .name = "cpuimx-audio",
91 .owner = THIS_MODULE,
92 .dai_link = &eukrea_tlv320_dai,
93 .num_links = 1,
94};
95
96static struct platform_device *eukrea_tlv320_snd_device;
97
98static int __init eukrea_tlv320_init(void)
99{
100 int ret;
101 int int_port = 0, ext_port;
102
103 if (machine_is_eukrea_cpuimx27()) {
104 imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
105 IMX_AUDMUX_V1_PCR_SYN |
106 IMX_AUDMUX_V1_PCR_TFSDIR |
107 IMX_AUDMUX_V1_PCR_TCLKDIR |
108 IMX_AUDMUX_V1_PCR_RFSDIR |
109 IMX_AUDMUX_V1_PCR_RCLKDIR |
110 IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
111 IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
112 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4)
113 );
114 imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4,
115 IMX_AUDMUX_V1_PCR_SYN |
116 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
117 );
118 } else if (machine_is_eukrea_cpuimx25sd() ||
119 machine_is_eukrea_cpuimx35sd() ||
120 machine_is_eukrea_cpuimx51sd()) {
121 ext_port = machine_is_eukrea_cpuimx25sd() ? 4 : 3;
122 imx_audmux_v2_configure_port(int_port,
123 IMX_AUDMUX_V2_PTCR_SYN |
124 IMX_AUDMUX_V2_PTCR_TFSDIR |
125 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
126 IMX_AUDMUX_V2_PTCR_TCLKDIR |
127 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port),
128 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)
129 );
130 imx_audmux_v2_configure_port(ext_port,
131 IMX_AUDMUX_V2_PTCR_SYN,
132 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)
133 );
134 } else {
135 /* return happy. We might run on a totally different machine */
136 return 0;
137 }
138
139 eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1);
140 if (!eukrea_tlv320_snd_device)
141 return -ENOMEM;
142
143 platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320);
144 ret = platform_device_add(eukrea_tlv320_snd_device);
145
146 if (ret) {
147 printk(KERN_ERR "ASoC: Platform device allocation failed\n");
148 platform_device_put(eukrea_tlv320_snd_device);
149 }
150
151 return ret;
152}
153
154static void __exit eukrea_tlv320_exit(void)
155{
156 platform_device_unregister(eukrea_tlv320_snd_device);
157}
158
159module_init(eukrea_tlv320_init);
160module_exit(eukrea_tlv320_exit);
161
162MODULE_AUTHOR("Eric Bénard <eric@eukrea.com>");
163MODULE_DESCRIPTION("CPUIMX ALSA SoC driver");
164MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 2eb407fa3b48..4ed2afd47782 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -11,11 +11,15 @@
11 */ 11 */
12 12
13#include <linux/init.h> 13#include <linux/init.h>
14#include <linux/io.h>
14#include <linux/module.h> 15#include <linux/module.h>
15#include <linux/interrupt.h> 16#include <linux/interrupt.h>
17#include <linux/clk.h>
16#include <linux/device.h> 18#include <linux/device.h>
17#include <linux/delay.h> 19#include <linux/delay.h>
18#include <linux/slab.h> 20#include <linux/slab.h>
21#include <linux/of_address.h>
22#include <linux/of_irq.h>
19#include <linux/of_platform.h> 23#include <linux/of_platform.h>
20 24
21#include <sound/core.h> 25#include <sound/core.h>
@@ -25,6 +29,26 @@
25#include <sound/soc.h> 29#include <sound/soc.h>
26 30
27#include "fsl_ssi.h" 31#include "fsl_ssi.h"
32#include "imx-pcm.h"
33
34#ifdef PPC
35#define read_ssi(addr) in_be32(addr)
36#define write_ssi(val, addr) out_be32(addr, val)
37#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
38#elif defined ARM
39#define read_ssi(addr) readl(addr)
40#define write_ssi(val, addr) writel(val, addr)
41/*
42 * FIXME: Proper locking should be added at write_ssi_mask caller level
43 * to ensure this register read/modify/write sequence is race free.
44 */
45static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
46{
47 u32 val = readl(addr);
48 val = (val & ~clear) | set;
49 writel(val, addr);
50}
51#endif
28 52
29/** 53/**
30 * FSLSSI_I2S_RATES: sample rates supported by the I2S 54 * FSLSSI_I2S_RATES: sample rates supported by the I2S
@@ -94,6 +118,13 @@ struct fsl_ssi_private {
94 struct device_attribute dev_attr; 118 struct device_attribute dev_attr;
95 struct platform_device *pdev; 119 struct platform_device *pdev;
96 120
121 bool new_binding;
122 bool ssi_on_imx;
123 struct clk *clk;
124 struct platform_device *imx_pcm_pdev;
125 struct imx_pcm_dma_params dma_params_tx;
126 struct imx_pcm_dma_params dma_params_rx;
127
97 struct { 128 struct {
98 unsigned int rfrc; 129 unsigned int rfrc;
99 unsigned int tfrc; 130 unsigned int tfrc;
@@ -145,7 +176,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
145 were interrupted for. We mask it with the Interrupt Enable register 176 were interrupted for. We mask it with the Interrupt Enable register
146 so that we only check for events that we're interested in. 177 so that we only check for events that we're interested in.
147 */ 178 */
148 sisr = in_be32(&ssi->sisr) & SIER_FLAGS; 179 sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
149 180
150 if (sisr & CCSR_SSI_SISR_RFRC) { 181 if (sisr & CCSR_SSI_SISR_RFRC) {
151 ssi_private->stats.rfrc++; 182 ssi_private->stats.rfrc++;
@@ -260,7 +291,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
260 291
261 /* Clear the bits that we set */ 292 /* Clear the bits that we set */
262 if (sisr2) 293 if (sisr2)
263 out_be32(&ssi->sisr, sisr2); 294 write_ssi(sisr2, &ssi->sisr);
264 295
265 return ret; 296 return ret;
266} 297}
@@ -295,7 +326,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
295 * SSI needs to be disabled before updating the registers we set 326 * SSI needs to be disabled before updating the registers we set
296 * here. 327 * here.
297 */ 328 */
298 clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); 329 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
299 330
300 /* 331 /*
301 * Program the SSI into I2S Slave Non-Network Synchronous mode. 332 * Program the SSI into I2S Slave Non-Network Synchronous mode.
@@ -303,20 +334,18 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
303 * 334 *
304 * FIXME: Little-endian samples require a different shift dir 335 * FIXME: Little-endian samples require a different shift dir
305 */ 336 */
306 clrsetbits_be32(&ssi->scr, 337 write_ssi_mask(&ssi->scr,
307 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, 338 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
308 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE 339 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
309 | (synchronous ? CCSR_SSI_SCR_SYN : 0)); 340 | (synchronous ? CCSR_SSI_SCR_SYN : 0));
310 341
311 out_be32(&ssi->stcr, 342 write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
312 CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
313 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | 343 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
314 CCSR_SSI_STCR_TSCKP); 344 CCSR_SSI_STCR_TSCKP, &ssi->stcr);
315 345
316 out_be32(&ssi->srcr, 346 write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
317 CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
318 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | 347 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
319 CCSR_SSI_SRCR_RSCKP); 348 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
320 349
321 /* 350 /*
322 * The DC and PM bits are only used if the SSI is the clock 351 * The DC and PM bits are only used if the SSI is the clock
@@ -324,7 +353,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
324 */ 353 */
325 354
326 /* Enable the interrupts and DMA requests */ 355 /* Enable the interrupts and DMA requests */
327 out_be32(&ssi->sier, SIER_FLAGS); 356 write_ssi(SIER_FLAGS, &ssi->sier);
328 357
329 /* 358 /*
330 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We 359 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
@@ -339,9 +368,9 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
339 * make this value larger (and maybe we should), but this way 368 * make this value larger (and maybe we should), but this way
340 * data will be written to memory as soon as it's available. 369 * data will be written to memory as soon as it's available.
341 */ 370 */
342 out_be32(&ssi->sfcsr, 371 write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
343 CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | 372 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
344 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2)); 373 &ssi->sfcsr);
345 374
346 /* 375 /*
347 * We keep the SSI disabled because if we enable it, then the 376 * We keep the SSI disabled because if we enable it, then the
@@ -393,6 +422,12 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
393 ssi_private->second_stream = substream; 422 ssi_private->second_stream = substream;
394 } 423 }
395 424
425 if (ssi_private->ssi_on_imx)
426 snd_soc_dai_set_dma_data(dai, substream,
427 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
428 &ssi_private->dma_params_tx :
429 &ssi_private->dma_params_rx);
430
396 return 0; 431 return 0;
397} 432}
398 433
@@ -417,7 +452,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
417 unsigned int sample_size = 452 unsigned int sample_size =
418 snd_pcm_format_width(params_format(hw_params)); 453 snd_pcm_format_width(params_format(hw_params));
419 u32 wl = CCSR_SSI_SxCCR_WL(sample_size); 454 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
420 int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN; 455 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
421 456
422 /* 457 /*
423 * If we're in synchronous mode, and the SSI is already enabled, 458 * If we're in synchronous mode, and the SSI is already enabled,
@@ -439,9 +474,9 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
439 /* In synchronous mode, the SSI uses STCCR for capture */ 474 /* In synchronous mode, the SSI uses STCCR for capture */
440 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || 475 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
441 ssi_private->cpu_dai_drv.symmetric_rates) 476 ssi_private->cpu_dai_drv.symmetric_rates)
442 clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); 477 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
443 else 478 else
444 clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); 479 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
445 480
446 return 0; 481 return 0;
447} 482}
@@ -466,19 +501,19 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
466 case SNDRV_PCM_TRIGGER_START: 501 case SNDRV_PCM_TRIGGER_START:
467 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 502 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
468 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 503 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
469 setbits32(&ssi->scr, 504 write_ssi_mask(&ssi->scr, 0,
470 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); 505 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
471 else 506 else
472 setbits32(&ssi->scr, 507 write_ssi_mask(&ssi->scr, 0,
473 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); 508 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
474 break; 509 break;
475 510
476 case SNDRV_PCM_TRIGGER_STOP: 511 case SNDRV_PCM_TRIGGER_STOP:
477 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 512 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
478 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 513 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
479 clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); 514 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
480 else 515 else
481 clrbits32(&ssi->scr, CCSR_SSI_SCR_RE); 516 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
482 break; 517 break;
483 518
484 default: 519 default:
@@ -510,7 +545,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
510 if (!ssi_private->first_stream) { 545 if (!ssi_private->first_stream) {
511 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 546 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
512 547
513 clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); 548 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
514 } 549 }
515} 550}
516 551
@@ -622,12 +657,6 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
622 if (!of_device_is_available(np)) 657 if (!of_device_is_available(np))
623 return -ENODEV; 658 return -ENODEV;
624 659
625 /* Check for a codec-handle property. */
626 if (!of_get_property(np, "codec-handle", NULL)) {
627 dev_err(&pdev->dev, "missing codec-handle property\n");
628 return -ENODEV;
629 }
630
631 /* We only support the SSI in "I2S Slave" mode */ 660 /* We only support the SSI in "I2S Slave" mode */
632 sprop = of_get_property(np, "fsl,mode", NULL); 661 sprop = of_get_property(np, "fsl,mode", NULL);
633 if (!sprop || strcmp(sprop, "i2s-slave")) { 662 if (!sprop || strcmp(sprop, "i2s-slave")) {
@@ -692,6 +721,50 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
692 /* Older 8610 DTs didn't have the fifo-depth property */ 721 /* Older 8610 DTs didn't have the fifo-depth property */
693 ssi_private->fifo_depth = 8; 722 ssi_private->fifo_depth = 8;
694 723
724 if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
725 u32 dma_events[2];
726 ssi_private->ssi_on_imx = true;
727
728 ssi_private->clk = clk_get(&pdev->dev, NULL);
729 if (IS_ERR(ssi_private->clk)) {
730 ret = PTR_ERR(ssi_private->clk);
731 dev_err(&pdev->dev, "could not get clock: %d\n", ret);
732 goto error_irq;
733 }
734 clk_prepare_enable(ssi_private->clk);
735
736 /*
737 * We have burstsize be "fifo_depth - 2" to match the SSI
738 * watermark setting in fsl_ssi_startup().
739 */
740 ssi_private->dma_params_tx.burstsize =
741 ssi_private->fifo_depth - 2;
742 ssi_private->dma_params_rx.burstsize =
743 ssi_private->fifo_depth - 2;
744 ssi_private->dma_params_tx.dma_addr =
745 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
746 ssi_private->dma_params_rx.dma_addr =
747 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
748 /*
749 * TODO: This is a temporary solution and should be changed
750 * to use generic DMA binding later when the helplers get in.
751 */
752 ret = of_property_read_u32_array(pdev->dev.of_node,
753 "fsl,ssi-dma-events", dma_events, 2);
754 if (ret) {
755 dev_err(&pdev->dev, "could not get dma events\n");
756 goto error_clk;
757 }
758 ssi_private->dma_params_tx.dma = dma_events[0];
759 ssi_private->dma_params_rx.dma = dma_events[1];
760
761 ssi_private->dma_params_tx.shared_peripheral =
762 of_device_is_compatible(of_get_parent(np),
763 "fsl,spba-bus");
764 ssi_private->dma_params_rx.shared_peripheral =
765 ssi_private->dma_params_tx.shared_peripheral;
766 }
767
695 /* Initialize the the device_attribute structure */ 768 /* Initialize the the device_attribute structure */
696 dev_attr = &ssi_private->dev_attr; 769 dev_attr = &ssi_private->dev_attr;
697 sysfs_attr_init(&dev_attr->attr); 770 sysfs_attr_init(&dev_attr->attr);
@@ -715,6 +788,26 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
715 goto error_dev; 788 goto error_dev;
716 } 789 }
717 790
791 if (ssi_private->ssi_on_imx) {
792 ssi_private->imx_pcm_pdev =
793 platform_device_register_simple("imx-pcm-audio",
794 -1, NULL, 0);
795 if (IS_ERR(ssi_private->imx_pcm_pdev)) {
796 ret = PTR_ERR(ssi_private->imx_pcm_pdev);
797 goto error_dev;
798 }
799 }
800
801 /*
802 * If codec-handle property is missing from SSI node, we assume
803 * that the machine driver uses new binding which does not require
804 * SSI driver to trigger machine driver's probe.
805 */
806 if (!of_get_property(np, "codec-handle", NULL)) {
807 ssi_private->new_binding = true;
808 goto done;
809 }
810
718 /* Trigger the machine driver's probe function. The platform driver 811 /* Trigger the machine driver's probe function. The platform driver
719 * name of the machine driver is taken from /compatible property of the 812 * name of the machine driver is taken from /compatible property of the
720 * device tree. We also pass the address of the CPU DAI driver 813 * device tree. We also pass the address of the CPU DAI driver
@@ -736,15 +829,24 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
736 goto error_dai; 829 goto error_dai;
737 } 830 }
738 831
832done:
739 return 0; 833 return 0;
740 834
741error_dai: 835error_dai:
836 if (ssi_private->ssi_on_imx)
837 platform_device_unregister(ssi_private->imx_pcm_pdev);
742 snd_soc_unregister_dai(&pdev->dev); 838 snd_soc_unregister_dai(&pdev->dev);
743 839
744error_dev: 840error_dev:
745 dev_set_drvdata(&pdev->dev, NULL); 841 dev_set_drvdata(&pdev->dev, NULL);
746 device_remove_file(&pdev->dev, dev_attr); 842 device_remove_file(&pdev->dev, dev_attr);
747 843
844error_clk:
845 if (ssi_private->ssi_on_imx) {
846 clk_disable_unprepare(ssi_private->clk);
847 clk_put(ssi_private->clk);
848 }
849
748error_irq: 850error_irq:
749 free_irq(ssi_private->irq, ssi_private); 851 free_irq(ssi_private->irq, ssi_private);
750 852
@@ -764,7 +866,13 @@ static int fsl_ssi_remove(struct platform_device *pdev)
764{ 866{
765 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); 867 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
766 868
767 platform_device_unregister(ssi_private->pdev); 869 if (!ssi_private->new_binding)
870 platform_device_unregister(ssi_private->pdev);
871 if (ssi_private->ssi_on_imx) {
872 platform_device_unregister(ssi_private->imx_pcm_pdev);
873 clk_disable_unprepare(ssi_private->clk);
874 clk_put(ssi_private->clk);
875 }
768 snd_soc_unregister_dai(&pdev->dev); 876 snd_soc_unregister_dai(&pdev->dev);
769 device_remove_file(&pdev->dev, &ssi_private->dev_attr); 877 device_remove_file(&pdev->dev, &ssi_private->dev_attr);
770 878
@@ -779,6 +887,7 @@ static int fsl_ssi_remove(struct platform_device *pdev)
779 887
780static const struct of_device_id fsl_ssi_ids[] = { 888static const struct of_device_id fsl_ssi_ids[] = {
781 { .compatible = "fsl,mpc8610-ssi", }, 889 { .compatible = "fsl,mpc8610-ssi", },
890 { .compatible = "fsl,imx21-ssi", },
782 {} 891 {}
783}; 892};
784MODULE_DEVICE_TABLE(of, fsl_ssi_ids); 893MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c
new file mode 100644
index 000000000000..b9e42b503a37
--- /dev/null
+++ b/sound/soc/fsl/fsl_utils.c
@@ -0,0 +1,91 @@
1/**
2 * Freescale ALSA SoC Machine driver utility
3 *
4 * Author: Timur Tabi <timur@freescale.com>
5 *
6 * Copyright 2010 Freescale Semiconductor, Inc.
7 *
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.
11 */
12
13#include <linux/module.h>
14#include <linux/of_address.h>
15#include <sound/soc.h>
16
17#include "fsl_utils.h"
18
19/**
20 * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node
21 *
22 * @ssi_np: pointer to the SSI device tree node
23 * @name: name of the phandle pointing to the dma channel
24 * @dai: ASoC DAI link pointer to be filled with platform_name
25 * @dma_channel_id: dma channel id to be returned
26 * @dma_id: dma id to be returned
27 *
28 * This function determines the dma and channel id for given SSI node. It
29 * also discovers the platform_name for the ASoC DAI link.
30 */
31int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
32 const char *name,
33 struct snd_soc_dai_link *dai,
34 unsigned int *dma_channel_id,
35 unsigned int *dma_id)
36{
37 struct resource res;
38 struct device_node *dma_channel_np, *dma_np;
39 const u32 *iprop;
40 int ret;
41
42 dma_channel_np = of_parse_phandle(ssi_np, name, 0);
43 if (!dma_channel_np)
44 return -EINVAL;
45
46 if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) {
47 of_node_put(dma_channel_np);
48 return -EINVAL;
49 }
50
51 /* Determine the dev_name for the device_node. This code mimics the
52 * behavior of of_device_make_bus_id(). We need this because ASoC uses
53 * the dev_name() of the device to match the platform (DMA) device with
54 * the CPU (SSI) device. It's all ugly and hackish, but it works (for
55 * now).
56 *
57 * dai->platform name should already point to an allocated buffer.
58 */
59 ret = of_address_to_resource(dma_channel_np, 0, &res);
60 if (ret) {
61 of_node_put(dma_channel_np);
62 return ret;
63 }
64 snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
65 (unsigned long long) res.start, dma_channel_np->name);
66
67 iprop = of_get_property(dma_channel_np, "cell-index", NULL);
68 if (!iprop) {
69 of_node_put(dma_channel_np);
70 return -EINVAL;
71 }
72 *dma_channel_id = be32_to_cpup(iprop);
73
74 dma_np = of_get_parent(dma_channel_np);
75 iprop = of_get_property(dma_np, "cell-index", NULL);
76 if (!iprop) {
77 of_node_put(dma_np);
78 return -EINVAL;
79 }
80 *dma_id = be32_to_cpup(iprop);
81
82 of_node_put(dma_np);
83 of_node_put(dma_channel_np);
84
85 return 0;
86}
87EXPORT_SYMBOL(fsl_asoc_get_dma_channel);
88
89MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
90MODULE_DESCRIPTION("Freescale ASoC utility code");
91MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h
new file mode 100644
index 000000000000..b2951126527c
--- /dev/null
+++ b/sound/soc/fsl/fsl_utils.h
@@ -0,0 +1,26 @@
1/**
2 * Freescale ALSA SoC Machine driver utility
3 *
4 * Author: Timur Tabi <timur@freescale.com>
5 *
6 * Copyright 2010 Freescale Semiconductor, Inc.
7 *
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.
11 */
12
13#ifndef _FSL_UTILS_H
14#define _FSL_UTILS_H
15
16#define DAI_NAME_SIZE 32
17
18struct snd_soc_dai_link;
19struct device_node;
20
21int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name,
22 struct snd_soc_dai_link *dai,
23 unsigned int *dma_channel_id,
24 unsigned int *dma_id);
25
26#endif /* _FSL_UTILS_H */
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
new file mode 100644
index 000000000000..f23700359c67
--- /dev/null
+++ b/sound/soc/fsl/imx-audmux.c
@@ -0,0 +1,311 @@
1/*
2 * Copyright 2012 Freescale Semiconductor, Inc.
3 * Copyright 2012 Linaro Ltd.
4 * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
5 *
6 * Initial development of this code was funded by
7 * Phytec Messtechnik GmbH, http://www.phytec.de
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/clk.h>
21#include <linux/debugfs.h>
22#include <linux/err.h>
23#include <linux/io.h>
24#include <linux/module.h>
25#include <linux/of.h>
26#include <linux/of_device.h>
27#include <linux/platform_device.h>
28#include <linux/slab.h>
29
30#include "imx-audmux.h"
31
32#define DRIVER_NAME "imx-audmux"
33
34static struct clk *audmux_clk;
35static void __iomem *audmux_base;
36
37#define IMX_AUDMUX_V2_PTCR(x) ((x) * 8)
38#define IMX_AUDMUX_V2_PDCR(x) ((x) * 8 + 4)
39
40#ifdef CONFIG_DEBUG_FS
41static struct dentry *audmux_debugfs_root;
42
43/* There is an annoying discontinuity in the SSI numbering with regard
44 * to the Linux number of the devices */
45static const char *audmux_port_string(int port)
46{
47 switch (port) {
48 case MX31_AUDMUX_PORT1_SSI0:
49 return "imx-ssi.0";
50 case MX31_AUDMUX_PORT2_SSI1:
51 return "imx-ssi.1";
52 case MX31_AUDMUX_PORT3_SSI_PINS_3:
53 return "SSI3";
54 case MX31_AUDMUX_PORT4_SSI_PINS_4:
55 return "SSI4";
56 case MX31_AUDMUX_PORT5_SSI_PINS_5:
57 return "SSI5";
58 case MX31_AUDMUX_PORT6_SSI_PINS_6:
59 return "SSI6";
60 default:
61 return "UNKNOWN";
62 }
63}
64
65static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
66 size_t count, loff_t *ppos)
67{
68 ssize_t ret;
69 char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
70 int port = (int)file->private_data;
71 u32 pdcr, ptcr;
72
73 if (!buf)
74 return -ENOMEM;
75
76 if (!audmux_base)
77 return -ENOSYS;
78
79 if (audmux_clk)
80 clk_prepare_enable(audmux_clk);
81
82 ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port));
83 pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port));
84
85 if (audmux_clk)
86 clk_disable_unprepare(audmux_clk);
87
88 ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n",
89 pdcr, ptcr);
90
91 if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR)
92 ret += snprintf(buf + ret, PAGE_SIZE - ret,
93 "TxFS output from %s, ",
94 audmux_port_string((ptcr >> 27) & 0x7));
95 else
96 ret += snprintf(buf + ret, PAGE_SIZE - ret,
97 "TxFS input, ");
98
99 if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR)
100 ret += snprintf(buf + ret, PAGE_SIZE - ret,
101 "TxClk output from %s",
102 audmux_port_string((ptcr >> 22) & 0x7));
103 else
104 ret += snprintf(buf + ret, PAGE_SIZE - ret,
105 "TxClk input");
106
107 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
108
109 if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) {
110 ret += snprintf(buf + ret, PAGE_SIZE - ret,
111 "Port is symmetric");
112 } else {
113 if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR)
114 ret += snprintf(buf + ret, PAGE_SIZE - ret,
115 "RxFS output from %s, ",
116 audmux_port_string((ptcr >> 17) & 0x7));
117 else
118 ret += snprintf(buf + ret, PAGE_SIZE - ret,
119 "RxFS input, ");
120
121 if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR)
122 ret += snprintf(buf + ret, PAGE_SIZE - ret,
123 "RxClk output from %s",
124 audmux_port_string((ptcr >> 12) & 0x7));
125 else
126 ret += snprintf(buf + ret, PAGE_SIZE - ret,
127 "RxClk input");
128 }
129
130 ret += snprintf(buf + ret, PAGE_SIZE - ret,
131 "\nData received from %s\n",
132 audmux_port_string((pdcr >> 13) & 0x7));
133
134 ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
135
136 kfree(buf);
137
138 return ret;
139}
140
141static const struct file_operations audmux_debugfs_fops = {
142 .open = simple_open,
143 .read = audmux_read_file,
144 .llseek = default_llseek,
145};
146
147static void __init audmux_debugfs_init(void)
148{
149 int i;
150 char buf[20];
151
152 audmux_debugfs_root = debugfs_create_dir("audmux", NULL);
153 if (!audmux_debugfs_root) {
154 pr_warning("Failed to create AUDMUX debugfs root\n");
155 return;
156 }
157
158 for (i = 0; i < MX31_AUDMUX_PORT6_SSI_PINS_6 + 1; i++) {
159 snprintf(buf, sizeof(buf), "ssi%d", i);
160 if (!debugfs_create_file(buf, 0444, audmux_debugfs_root,
161 (void *)i, &audmux_debugfs_fops))
162 pr_warning("Failed to create AUDMUX port %d debugfs file\n",
163 i);
164 }
165}
166
167static void __devexit audmux_debugfs_remove(void)
168{
169 debugfs_remove_recursive(audmux_debugfs_root);
170}
171#else
172static inline void audmux_debugfs_init(void)
173{
174}
175
176static inline void audmux_debugfs_remove(void)
177{
178}
179#endif
180
181enum imx_audmux_type {
182 IMX21_AUDMUX,
183 IMX31_AUDMUX,
184} audmux_type;
185
186static struct platform_device_id imx_audmux_ids[] = {
187 {
188 .name = "imx21-audmux",
189 .driver_data = IMX21_AUDMUX,
190 }, {
191 .name = "imx31-audmux",
192 .driver_data = IMX31_AUDMUX,
193 }, {
194 /* sentinel */
195 }
196};
197MODULE_DEVICE_TABLE(platform, imx_audmux_ids);
198
199static const struct of_device_id imx_audmux_dt_ids[] = {
200 { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], },
201 { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], },
202 { /* sentinel */ }
203};
204MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids);
205
206static const uint8_t port_mapping[] = {
207 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c,
208};
209
210int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr)
211{
212 if (audmux_type != IMX21_AUDMUX)
213 return -EINVAL;
214
215 if (!audmux_base)
216 return -ENOSYS;
217
218 if (port >= ARRAY_SIZE(port_mapping))
219 return -EINVAL;
220
221 writel(pcr, audmux_base + port_mapping[port]);
222
223 return 0;
224}
225EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port);
226
227int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
228 unsigned int pdcr)
229{
230 if (audmux_type != IMX31_AUDMUX)
231 return -EINVAL;
232
233 if (!audmux_base)
234 return -ENOSYS;
235
236 if (audmux_clk)
237 clk_prepare_enable(audmux_clk);
238
239 writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port));
240 writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port));
241
242 if (audmux_clk)
243 clk_disable_unprepare(audmux_clk);
244
245 return 0;
246}
247EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
248
249static int __devinit imx_audmux_probe(struct platform_device *pdev)
250{
251 struct resource *res;
252 const struct of_device_id *of_id =
253 of_match_device(imx_audmux_dt_ids, &pdev->dev);
254
255 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
256 audmux_base = devm_request_and_ioremap(&pdev->dev, res);
257 if (!audmux_base)
258 return -EADDRNOTAVAIL;
259
260 audmux_clk = clk_get(&pdev->dev, "audmux");
261 if (IS_ERR(audmux_clk)) {
262 dev_dbg(&pdev->dev, "cannot get clock: %ld\n",
263 PTR_ERR(audmux_clk));
264 audmux_clk = NULL;
265 }
266
267 if (of_id)
268 pdev->id_entry = of_id->data;
269 audmux_type = pdev->id_entry->driver_data;
270 if (audmux_type == IMX31_AUDMUX)
271 audmux_debugfs_init();
272
273 return 0;
274}
275
276static int __devexit imx_audmux_remove(struct platform_device *pdev)
277{
278 if (audmux_type == IMX31_AUDMUX)
279 audmux_debugfs_remove();
280 clk_put(audmux_clk);
281
282 return 0;
283}
284
285static struct platform_driver imx_audmux_driver = {
286 .probe = imx_audmux_probe,
287 .remove = __devexit_p(imx_audmux_remove),
288 .id_table = imx_audmux_ids,
289 .driver = {
290 .name = DRIVER_NAME,
291 .owner = THIS_MODULE,
292 .of_match_table = imx_audmux_dt_ids,
293 }
294};
295
296static int __init imx_audmux_init(void)
297{
298 return platform_driver_register(&imx_audmux_driver);
299}
300subsys_initcall(imx_audmux_init);
301
302static void __exit imx_audmux_exit(void)
303{
304 platform_driver_unregister(&imx_audmux_driver);
305}
306module_exit(imx_audmux_exit);
307
308MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver");
309MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
310MODULE_LICENSE("GPL v2");
311MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h
new file mode 100644
index 000000000000..04ebbab8d7b9
--- /dev/null
+++ b/sound/soc/fsl/imx-audmux.h
@@ -0,0 +1,60 @@
1#ifndef __IMX_AUDMUX_H
2#define __IMX_AUDMUX_H
3
4#define MX27_AUDMUX_HPCR1_SSI0 0
5#define MX27_AUDMUX_HPCR2_SSI1 1
6#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2
7#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3
8#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4
9#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5
10
11#define MX31_AUDMUX_PORT1_SSI0 0
12#define MX31_AUDMUX_PORT2_SSI1 1
13#define MX31_AUDMUX_PORT3_SSI_PINS_3 2
14#define MX31_AUDMUX_PORT4_SSI_PINS_4 3
15#define MX31_AUDMUX_PORT5_SSI_PINS_5 4
16#define MX31_AUDMUX_PORT6_SSI_PINS_6 5
17
18#define MX51_AUDMUX_PORT1_SSI0 0
19#define MX51_AUDMUX_PORT2_SSI1 1
20#define MX51_AUDMUX_PORT3 2
21#define MX51_AUDMUX_PORT4 3
22#define MX51_AUDMUX_PORT5 4
23#define MX51_AUDMUX_PORT6 5
24#define MX51_AUDMUX_PORT7 6
25
26/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */
27#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff)
28#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8)
29#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10)
30#define IMX_AUDMUX_V1_PCR_SYN (1 << 12)
31#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13)
32#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20)
33#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24)
34#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25)
35#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26)
36#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30)
37#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31)
38
39/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */
40#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31)
41#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27)
42#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26)
43#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22)
44#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21)
45#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17)
46#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16)
47#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12)
48#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11)
49
50#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13)
51#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12)
52#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8)
53#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff)
54
55int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr);
56
57int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
58 unsigned int pdcr);
59
60#endif /* __IMX_AUDMUX_H */
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
new file mode 100644
index 000000000000..f59c34943662
--- /dev/null
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -0,0 +1,156 @@
1/*
2 * imx-mc13783.c -- SoC audio for imx based boards with mc13783 codec
3 *
4 * Copyright 2012 Philippe Retornaz, <philippe.retornaz@epfl.ch>
5 *
6 * Heavly based on phycore-mc13783:
7 * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 *
14 */
15
16#include <linux/module.h>
17#include <linux/moduleparam.h>
18#include <linux/device.h>
19#include <sound/core.h>
20#include <sound/pcm.h>
21#include <sound/soc.h>
22#include <sound/soc-dapm.h>
23#include <asm/mach-types.h>
24
25#include "../codecs/mc13783.h"
26#include "imx-ssi.h"
27#include "imx-audmux.h"
28
29#define FMT_SSI (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \
30 SND_SOC_DAIFMT_CBM_CFM)
31
32static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
33 struct snd_pcm_hw_params *params)
34{
35 struct snd_soc_pcm_runtime *rtd = substream->private_data;
36 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
37 struct snd_soc_dai *codec_dai = rtd->codec_dai;
38 int ret;
39
40 ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xfffffffc, 0xfffffffc,
41 4, 16);
42 if (ret)
43 return ret;
44
45 ret = snd_soc_dai_set_sysclk(codec_dai, MC13783_CLK_CLIA, 26000000, 0);
46 if (ret)
47 return ret;
48
49 ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x0, 0xfffffffc, 2, 16);
50 if (ret)
51 return ret;
52
53 return 0;
54}
55
56static struct snd_soc_ops imx_mc13783_hifi_ops = {
57 .hw_params = imx_mc13783_hifi_hw_params,
58};
59
60static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = {
61 {
62 .name = "MC13783",
63 .stream_name = "Sound",
64 .codec_dai_name = "mc13783-hifi",
65 .codec_name = "mc13783-codec",
66 .cpu_dai_name = "imx-ssi.0",
67 .platform_name = "imx-pcm-audio.0",
68 .ops = &imx_mc13783_hifi_ops,
69 .symmetric_rates = 1,
70 .dai_fmt = FMT_SSI,
71 },
72};
73
74static const struct snd_soc_dapm_widget imx_mc13783_widget[] = {
75 SND_SOC_DAPM_MIC("Mic", NULL),
76 SND_SOC_DAPM_HP("Headphone", NULL),
77 SND_SOC_DAPM_SPK("Speaker", NULL),
78};
79
80static const struct snd_soc_dapm_route imx_mc13783_routes[] = {
81 {"Speaker", NULL, "LSP"},
82 {"Headphone", NULL, "HSL"},
83 {"Headphone", NULL, "HSR"},
84
85 {"MC1LIN", NULL, "MC1 Bias"},
86 {"MC2IN", NULL, "MC2 Bias"},
87 {"MC1 Bias", NULL, "Mic"},
88 {"MC2 Bias", NULL, "Mic"},
89};
90
91static struct snd_soc_card imx_mc13783 = {
92 .name = "imx_mc13783",
93 .dai_link = imx_mc13783_dai_mc13783,
94 .num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783),
95 .dapm_widgets = imx_mc13783_widget,
96 .num_dapm_widgets = ARRAY_SIZE(imx_mc13783_widget),
97 .dapm_routes = imx_mc13783_routes,
98 .num_dapm_routes = ARRAY_SIZE(imx_mc13783_routes),
99};
100
101static int __devinit imx_mc13783_probe(struct platform_device *pdev)
102{
103 int ret;
104
105 imx_mc13783.dev = &pdev->dev;
106
107 ret = snd_soc_register_card(&imx_mc13783);
108 if (ret) {
109 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
110 ret);
111 return ret;
112 }
113
114 imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
115 IMX_AUDMUX_V2_PTCR_SYN,
116 IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
117 IMX_AUDMUX_V2_PDCR_MODE(1) |
118 IMX_AUDMUX_V2_PDCR_INMMASK(0xfc));
119 imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0,
120 IMX_AUDMUX_V2_PTCR_SYN |
121 IMX_AUDMUX_V2_PTCR_TFSDIR |
122 IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
123 IMX_AUDMUX_V2_PTCR_TCLKDIR |
124 IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
125 IMX_AUDMUX_V2_PTCR_RFSDIR |
126 IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
127 IMX_AUDMUX_V2_PTCR_RCLKDIR |
128 IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4),
129 IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4));
130
131 return ret;
132}
133
134static int __devexit imx_mc13783_remove(struct platform_device *pdev)
135{
136 snd_soc_unregister_card(&imx_mc13783);
137
138 return 0;
139}
140
141static struct platform_driver imx_mc13783_audio_driver = {
142 .driver = {
143 .name = "imx_mc13783",
144 .owner = THIS_MODULE,
145 },
146 .probe = imx_mc13783_probe,
147 .remove = __devexit_p(imx_mc13783_remove)
148};
149
150module_platform_driver(imx_mc13783_audio_driver);
151
152MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
153MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch");
154MODULE_DESCRIPTION("imx with mc13783 codec ALSA SoC driver");
155MODULE_LICENSE("GPL");
156MODULE_ALIAS("platform:imx_mc13783");
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
new file mode 100644
index 000000000000..f3c0a5ef35c8
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -0,0 +1,176 @@
1/*
2 * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer
3 *
4 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
5 *
6 * This code is based on code copyrighted by Freescale,
7 * Liam Girdwood, Javier Martin and probably others.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 */
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/dma-mapping.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/slab.h>
23#include <linux/dmaengine.h>
24#include <linux/types.h>
25
26#include <sound/core.h>
27#include <sound/initval.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/soc.h>
31#include <sound/dmaengine_pcm.h>
32
33#include <mach/dma.h>
34
35#include "imx-pcm.h"
36
37static bool filter(struct dma_chan *chan, void *param)
38{
39 if (!imx_dma_is_general_purpose(chan))
40 return false;
41
42 chan->private = param;
43
44 return true;
45}
46
47static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
48 struct snd_pcm_hw_params *params)
49{
50 struct snd_soc_pcm_runtime *rtd = substream->private_data;
51 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
52 struct imx_pcm_dma_params *dma_params;
53 struct dma_slave_config slave_config;
54 int ret;
55
56 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
57
58 ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
59 if (ret)
60 return ret;
61
62 slave_config.device_fc = false;
63
64 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
65 slave_config.dst_addr = dma_params->dma_addr;
66 slave_config.dst_maxburst = dma_params->burstsize;
67 } else {
68 slave_config.src_addr = dma_params->dma_addr;
69 slave_config.src_maxburst = dma_params->burstsize;
70 }
71
72 ret = dmaengine_slave_config(chan, &slave_config);
73 if (ret)
74 return ret;
75
76 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
77
78 return 0;
79}
80
81static struct snd_pcm_hardware snd_imx_hardware = {
82 .info = SNDRV_PCM_INFO_INTERLEAVED |
83 SNDRV_PCM_INFO_BLOCK_TRANSFER |
84 SNDRV_PCM_INFO_MMAP |
85 SNDRV_PCM_INFO_MMAP_VALID |
86 SNDRV_PCM_INFO_PAUSE |
87 SNDRV_PCM_INFO_RESUME,
88 .formats = SNDRV_PCM_FMTBIT_S16_LE,
89 .rate_min = 8000,
90 .channels_min = 2,
91 .channels_max = 2,
92 .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
93 .period_bytes_min = 128,
94 .period_bytes_max = 65535, /* Limited by SDMA engine */
95 .periods_min = 2,
96 .periods_max = 255,
97 .fifo_size = 0,
98};
99
100static int snd_imx_open(struct snd_pcm_substream *substream)
101{
102 struct snd_soc_pcm_runtime *rtd = substream->private_data;
103 struct imx_pcm_dma_params *dma_params;
104 struct imx_dma_data *dma_data;
105 int ret;
106
107 snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
108
109 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
110
111 dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
112 dma_data->peripheral_type = dma_params->shared_peripheral ?
113 IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI;
114 dma_data->priority = DMA_PRIO_HIGH;
115 dma_data->dma_request = dma_params->dma;
116
117 ret = snd_dmaengine_pcm_open(substream, filter, dma_data);
118 if (ret) {
119 kfree(dma_data);
120 return 0;
121 }
122
123 snd_dmaengine_pcm_set_data(substream, dma_data);
124
125 return 0;
126}
127
128static int snd_imx_close(struct snd_pcm_substream *substream)
129{
130 struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
131
132 snd_dmaengine_pcm_close(substream);
133 kfree(dma_data);
134
135 return 0;
136}
137
138static struct snd_pcm_ops imx_pcm_ops = {
139 .open = snd_imx_open,
140 .close = snd_imx_close,
141 .ioctl = snd_pcm_lib_ioctl,
142 .hw_params = snd_imx_pcm_hw_params,
143 .trigger = snd_dmaengine_pcm_trigger,
144 .pointer = snd_dmaengine_pcm_pointer,
145 .mmap = snd_imx_pcm_mmap,
146};
147
148static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
149 .ops = &imx_pcm_ops,
150 .pcm_new = imx_pcm_new,
151 .pcm_free = imx_pcm_free,
152};
153
154static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
155{
156 return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
157}
158
159static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
160{
161 snd_soc_unregister_platform(&pdev->dev);
162 return 0;
163}
164
165static struct platform_driver imx_pcm_driver = {
166 .driver = {
167 .name = "imx-pcm-audio",
168 .owner = THIS_MODULE,
169 },
170 .probe = imx_soc_platform_probe,
171 .remove = __devexit_p(imx_soc_platform_remove),
172};
173
174module_platform_driver(imx_pcm_driver);
175MODULE_LICENSE("GPL");
176MODULE_ALIAS("platform:imx-pcm-audio");
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
new file mode 100644
index 000000000000..456b7d723d66
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -0,0 +1,336 @@
1/*
2 * imx-pcm-fiq.c -- ALSA Soc Audio Layer
3 *
4 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
5 *
6 * This code is based on code copyrighted by Freescale,
7 * Liam Girdwood, Javier Martin and probably others.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 */
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/dma-mapping.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/slab.h>
23
24#include <sound/core.h>
25#include <sound/initval.h>
26#include <sound/pcm.h>
27#include <sound/pcm_params.h>
28#include <sound/soc.h>
29
30#include <asm/fiq.h>
31
32#include <mach/ssi.h>
33
34#include "imx-ssi.h"
35
36struct imx_pcm_runtime_data {
37 int period;
38 int periods;
39 unsigned long offset;
40 unsigned long last_offset;
41 unsigned long size;
42 struct hrtimer hrt;
43 int poll_time_ns;
44 struct snd_pcm_substream *substream;
45 atomic_t running;
46};
47
48static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
49{
50 struct imx_pcm_runtime_data *iprtd =
51 container_of(hrt, struct imx_pcm_runtime_data, hrt);
52 struct snd_pcm_substream *substream = iprtd->substream;
53 struct snd_pcm_runtime *runtime = substream->runtime;
54 struct pt_regs regs;
55 unsigned long delta;
56
57 if (!atomic_read(&iprtd->running))
58 return HRTIMER_NORESTART;
59
60 get_fiq_regs(&regs);
61
62 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
63 iprtd->offset = regs.ARM_r8 & 0xffff;
64 else
65 iprtd->offset = regs.ARM_r9 & 0xffff;
66
67 /* How much data have we transferred since the last period report? */
68 if (iprtd->offset >= iprtd->last_offset)
69 delta = iprtd->offset - iprtd->last_offset;
70 else
71 delta = runtime->buffer_size + iprtd->offset
72 - iprtd->last_offset;
73
74 /* If we've transferred at least a period then report it and
75 * reset our poll time */
76 if (delta >= iprtd->period) {
77 snd_pcm_period_elapsed(substream);
78 iprtd->last_offset = iprtd->offset;
79 }
80
81 hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns));
82
83 return HRTIMER_RESTART;
84}
85
86static struct fiq_handler fh = {
87 .name = DRV_NAME,
88};
89
90static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
91 struct snd_pcm_hw_params *params)
92{
93 struct snd_pcm_runtime *runtime = substream->runtime;
94 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
95
96 iprtd->size = params_buffer_bytes(params);
97 iprtd->periods = params_periods(params);
98 iprtd->period = params_period_bytes(params) ;
99 iprtd->offset = 0;
100 iprtd->last_offset = 0;
101 iprtd->poll_time_ns = 1000000000 / params_rate(params) *
102 params_period_size(params);
103 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
104
105 return 0;
106}
107
108static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
109{
110 struct snd_pcm_runtime *runtime = substream->runtime;
111 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
112 struct pt_regs regs;
113
114 get_fiq_regs(&regs);
115 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
116 regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16;
117 else
118 regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16;
119
120 set_fiq_regs(&regs);
121
122 return 0;
123}
124
125static int fiq_enable;
126static int imx_pcm_fiq;
127
128static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
129{
130 struct snd_pcm_runtime *runtime = substream->runtime;
131 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
132
133 switch (cmd) {
134 case SNDRV_PCM_TRIGGER_START:
135 case SNDRV_PCM_TRIGGER_RESUME:
136 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
137 atomic_set(&iprtd->running, 1);
138 hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns),
139 HRTIMER_MODE_REL);
140 if (++fiq_enable == 1)
141 enable_fiq(imx_pcm_fiq);
142
143 break;
144
145 case SNDRV_PCM_TRIGGER_STOP:
146 case SNDRV_PCM_TRIGGER_SUSPEND:
147 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
148 atomic_set(&iprtd->running, 0);
149
150 if (--fiq_enable == 0)
151 disable_fiq(imx_pcm_fiq);
152
153 break;
154 default:
155 return -EINVAL;
156 }
157
158 return 0;
159}
160
161static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
162{
163 struct snd_pcm_runtime *runtime = substream->runtime;
164 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
165
166 return bytes_to_frames(substream->runtime, iprtd->offset);
167}
168
169static struct snd_pcm_hardware snd_imx_hardware = {
170 .info = SNDRV_PCM_INFO_INTERLEAVED |
171 SNDRV_PCM_INFO_BLOCK_TRANSFER |
172 SNDRV_PCM_INFO_MMAP |
173 SNDRV_PCM_INFO_MMAP_VALID |
174 SNDRV_PCM_INFO_PAUSE |
175 SNDRV_PCM_INFO_RESUME,
176 .formats = SNDRV_PCM_FMTBIT_S16_LE,
177 .rate_min = 8000,
178 .channels_min = 2,
179 .channels_max = 2,
180 .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
181 .period_bytes_min = 128,
182 .period_bytes_max = 16 * 1024,
183 .periods_min = 4,
184 .periods_max = 255,
185 .fifo_size = 0,
186};
187
188static int snd_imx_open(struct snd_pcm_substream *substream)
189{
190 struct snd_pcm_runtime *runtime = substream->runtime;
191 struct imx_pcm_runtime_data *iprtd;
192 int ret;
193
194 iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
195 if (iprtd == NULL)
196 return -ENOMEM;
197 runtime->private_data = iprtd;
198
199 iprtd->substream = substream;
200
201 atomic_set(&iprtd->running, 0);
202 hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
203 iprtd->hrt.function = snd_hrtimer_callback;
204
205 ret = snd_pcm_hw_constraint_integer(substream->runtime,
206 SNDRV_PCM_HW_PARAM_PERIODS);
207 if (ret < 0) {
208 kfree(iprtd);
209 return ret;
210 }
211
212 snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
213 return 0;
214}
215
216static int snd_imx_close(struct snd_pcm_substream *substream)
217{
218 struct snd_pcm_runtime *runtime = substream->runtime;
219 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
220
221 hrtimer_cancel(&iprtd->hrt);
222
223 kfree(iprtd);
224
225 return 0;
226}
227
228static struct snd_pcm_ops imx_pcm_ops = {
229 .open = snd_imx_open,
230 .close = snd_imx_close,
231 .ioctl = snd_pcm_lib_ioctl,
232 .hw_params = snd_imx_pcm_hw_params,
233 .prepare = snd_imx_pcm_prepare,
234 .trigger = snd_imx_pcm_trigger,
235 .pointer = snd_imx_pcm_pointer,
236 .mmap = snd_imx_pcm_mmap,
237};
238
239static int ssi_irq = 0;
240
241static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
242{
243 struct snd_pcm *pcm = rtd->pcm;
244 struct snd_pcm_substream *substream;
245 int ret;
246
247 ret = imx_pcm_new(rtd);
248 if (ret)
249 return ret;
250
251 substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
252 if (substream) {
253 struct snd_dma_buffer *buf = &substream->dma_buffer;
254
255 imx_ssi_fiq_tx_buffer = (unsigned long)buf->area;
256 }
257
258 substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
259 if (substream) {
260 struct snd_dma_buffer *buf = &substream->dma_buffer;
261
262 imx_ssi_fiq_rx_buffer = (unsigned long)buf->area;
263 }
264
265 set_fiq_handler(&imx_ssi_fiq_start,
266 &imx_ssi_fiq_end - &imx_ssi_fiq_start);
267
268 return 0;
269}
270
271static void imx_pcm_fiq_free(struct snd_pcm *pcm)
272{
273 mxc_set_irq_fiq(ssi_irq, 0);
274 release_fiq(&fh);
275 imx_pcm_free(pcm);
276}
277
278static struct snd_soc_platform_driver imx_soc_platform_fiq = {
279 .ops = &imx_pcm_ops,
280 .pcm_new = imx_pcm_fiq_new,
281 .pcm_free = imx_pcm_fiq_free,
282};
283
284static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
285{
286 struct imx_ssi *ssi = platform_get_drvdata(pdev);
287 int ret;
288
289 ret = claim_fiq(&fh);
290 if (ret) {
291 dev_err(&pdev->dev, "failed to claim fiq: %d", ret);
292 return ret;
293 }
294
295 mxc_set_irq_fiq(ssi->irq, 1);
296 ssi_irq = ssi->irq;
297
298 imx_pcm_fiq = ssi->irq;
299
300 imx_ssi_fiq_base = (unsigned long)ssi->base;
301
302 ssi->dma_params_tx.burstsize = 4;
303 ssi->dma_params_rx.burstsize = 6;
304
305 ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq);
306 if (ret)
307 goto failed_register;
308
309 return 0;
310
311failed_register:
312 mxc_set_irq_fiq(ssi_irq, 0);
313 release_fiq(&fh);
314
315 return ret;
316}
317
318static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
319{
320 snd_soc_unregister_platform(&pdev->dev);
321 return 0;
322}
323
324static struct platform_driver imx_pcm_driver = {
325 .driver = {
326 .name = "imx-fiq-pcm-audio",
327 .owner = THIS_MODULE,
328 },
329
330 .probe = imx_soc_platform_probe,
331 .remove = __devexit_p(imx_soc_platform_remove),
332};
333
334module_platform_driver(imx_pcm_driver);
335
336MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c
new file mode 100644
index 000000000000..93dc360b1777
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm.c
@@ -0,0 +1,105 @@
1/*
2 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
3 *
4 * This code is based on code copyrighted by Freescale,
5 * Liam Girdwood, Javier Martin and probably others.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13#include <linux/dma-mapping.h>
14#include <linux/module.h>
15#include <sound/pcm.h>
16#include <sound/soc.h>
17#include "imx-pcm.h"
18
19int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
20 struct vm_area_struct *vma)
21{
22 struct snd_pcm_runtime *runtime = substream->runtime;
23 int ret;
24
25 ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
26 runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
27
28 pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret,
29 runtime->dma_area,
30 runtime->dma_addr,
31 runtime->dma_bytes);
32 return ret;
33}
34EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap);
35
36static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
37{
38 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
39 struct snd_dma_buffer *buf = &substream->dma_buffer;
40 size_t size = IMX_SSI_DMABUF_SIZE;
41
42 buf->dev.type = SNDRV_DMA_TYPE_DEV;
43 buf->dev.dev = pcm->card->dev;
44 buf->private_data = NULL;
45 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
46 &buf->addr, GFP_KERNEL);
47 if (!buf->area)
48 return -ENOMEM;
49 buf->bytes = size;
50
51 return 0;
52}
53
54static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
55
56int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
57{
58 struct snd_card *card = rtd->card->snd_card;
59 struct snd_pcm *pcm = rtd->pcm;
60 int ret = 0;
61
62 if (!card->dev->dma_mask)
63 card->dev->dma_mask = &imx_pcm_dmamask;
64 if (!card->dev->coherent_dma_mask)
65 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
66 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
67 ret = imx_pcm_preallocate_dma_buffer(pcm,
68 SNDRV_PCM_STREAM_PLAYBACK);
69 if (ret)
70 goto out;
71 }
72
73 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
74 ret = imx_pcm_preallocate_dma_buffer(pcm,
75 SNDRV_PCM_STREAM_CAPTURE);
76 if (ret)
77 goto out;
78 }
79
80out:
81 return ret;
82}
83EXPORT_SYMBOL_GPL(imx_pcm_new);
84
85void imx_pcm_free(struct snd_pcm *pcm)
86{
87 struct snd_pcm_substream *substream;
88 struct snd_dma_buffer *buf;
89 int stream;
90
91 for (stream = 0; stream < 2; stream++) {
92 substream = pcm->streams[stream].substream;
93 if (!substream)
94 continue;
95
96 buf = &substream->dma_buffer;
97 if (!buf->area)
98 continue;
99
100 dma_free_writecombine(pcm->card->dev, buf->bytes,
101 buf->area, buf->addr);
102 buf->area = NULL;
103 }
104}
105EXPORT_SYMBOL_GPL(imx_pcm_free);
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
new file mode 100644
index 000000000000..83c0ed7d55c9
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm.h
@@ -0,0 +1,33 @@
1/*
2 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
3 *
4 * This code is based on code copyrighted by Freescale,
5 * Liam Girdwood, Javier Martin and probably others.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13#ifndef _IMX_PCM_H
14#define _IMX_PCM_H
15
16/*
17 * Do not change this as the FIQ handler depends on this size
18 */
19#define IMX_SSI_DMABUF_SIZE (64 * 1024)
20
21struct imx_pcm_dma_params {
22 int dma;
23 unsigned long dma_addr;
24 int burstsize;
25 bool shared_peripheral; /* The peripheral is on SPBA bus */
26};
27
28int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
29 struct vm_area_struct *vma);
30int imx_pcm_new(struct snd_soc_pcm_runtime *rtd);
31void imx_pcm_free(struct snd_pcm *pcm);
32
33#endif /* _IMX_PCM_H */
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
new file mode 100644
index 000000000000..3a729caeb8c8
--- /dev/null
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -0,0 +1,221 @@
1/*
2 * Copyright 2012 Freescale Semiconductor, Inc.
3 * Copyright 2012 Linaro Ltd.
4 *
5 * The code contained herein is licensed under the GNU General Public
6 * License. You may obtain a copy of the GNU General Public License
7 * Version 2 or later at the following locations:
8 *
9 * http://www.opensource.org/licenses/gpl-license.html
10 * http://www.gnu.org/copyleft/gpl.html
11 */
12
13#include <linux/module.h>
14#include <linux/of.h>
15#include <linux/of_platform.h>
16#include <linux/of_i2c.h>
17#include <linux/clk.h>
18#include <sound/soc.h>
19
20#include "../codecs/sgtl5000.h"
21#include "imx-audmux.h"
22
23#define DAI_NAME_SIZE 32
24
25struct imx_sgtl5000_data {
26 struct snd_soc_dai_link dai;
27 struct snd_soc_card card;
28 char codec_dai_name[DAI_NAME_SIZE];
29 char platform_name[DAI_NAME_SIZE];
30 struct clk *codec_clk;
31 unsigned int clk_frequency;
32};
33
34static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd)
35{
36 struct imx_sgtl5000_data *data = container_of(rtd->card,
37 struct imx_sgtl5000_data, card);
38 struct device *dev = rtd->card->dev;
39 int ret;
40
41 ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK,
42 data->clk_frequency, SND_SOC_CLOCK_IN);
43 if (ret) {
44 dev_err(dev, "could not set codec driver clock params\n");
45 return ret;
46 }
47
48 return 0;
49}
50
51static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = {
52 SND_SOC_DAPM_MIC("Mic Jack", NULL),
53 SND_SOC_DAPM_LINE("Line In Jack", NULL),
54 SND_SOC_DAPM_HP("Headphone Jack", NULL),
55 SND_SOC_DAPM_SPK("Line Out Jack", NULL),
56 SND_SOC_DAPM_SPK("Ext Spk", NULL),
57};
58
59static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
60{
61 struct device_node *np = pdev->dev.of_node;
62 struct device_node *ssi_np, *codec_np;
63 struct platform_device *ssi_pdev;
64 struct i2c_client *codec_dev;
65 struct imx_sgtl5000_data *data;
66 int int_port, ext_port;
67 int ret;
68
69 ret = of_property_read_u32(np, "mux-int-port", &int_port);
70 if (ret) {
71 dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
72 return ret;
73 }
74 ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
75 if (ret) {
76 dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
77 return ret;
78 }
79
80 /*
81 * The port numbering in the hardware manual starts at 1, while
82 * the audmux API expects it starts at 0.
83 */
84 int_port--;
85 ext_port--;
86 ret = imx_audmux_v2_configure_port(int_port,
87 IMX_AUDMUX_V2_PTCR_SYN |
88 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
89 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
90 IMX_AUDMUX_V2_PTCR_TFSDIR |
91 IMX_AUDMUX_V2_PTCR_TCLKDIR,
92 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
93 if (ret) {
94 dev_err(&pdev->dev, "audmux internal port setup failed\n");
95 return ret;
96 }
97 imx_audmux_v2_configure_port(ext_port,
98 IMX_AUDMUX_V2_PTCR_SYN |
99 IMX_AUDMUX_V2_PTCR_TCSEL(int_port),
100 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
101 if (ret) {
102 dev_err(&pdev->dev, "audmux external port setup failed\n");
103 return ret;
104 }
105
106 ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
107 codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
108 if (!ssi_np || !codec_np) {
109 dev_err(&pdev->dev, "phandle missing or invalid\n");
110 ret = -EINVAL;
111 goto fail;
112 }
113
114 ssi_pdev = of_find_device_by_node(ssi_np);
115 if (!ssi_pdev) {
116 dev_err(&pdev->dev, "failed to find SSI platform device\n");
117 ret = -EINVAL;
118 goto fail;
119 }
120 codec_dev = of_find_i2c_device_by_node(codec_np);
121 if (!codec_dev) {
122 dev_err(&pdev->dev, "failed to find codec platform device\n");
123 return -EINVAL;
124 }
125
126 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
127 if (!data) {
128 ret = -ENOMEM;
129 goto fail;
130 }
131
132 data->codec_clk = clk_get(&codec_dev->dev, NULL);
133 if (IS_ERR(data->codec_clk)) {
134 /* assuming clock enabled by default */
135 data->codec_clk = NULL;
136 ret = of_property_read_u32(codec_np, "clock-frequency",
137 &data->clk_frequency);
138 if (ret) {
139 dev_err(&codec_dev->dev,
140 "clock-frequency missing or invalid\n");
141 goto fail;
142 }
143 } else {
144 data->clk_frequency = clk_get_rate(data->codec_clk);
145 clk_prepare_enable(data->codec_clk);
146 }
147
148 data->dai.name = "HiFi";
149 data->dai.stream_name = "HiFi";
150 data->dai.codec_dai_name = "sgtl5000";
151 data->dai.codec_of_node = codec_np;
152 data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev);
153 data->dai.platform_name = "imx-pcm-audio";
154 data->dai.init = &imx_sgtl5000_dai_init;
155 data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
156 SND_SOC_DAIFMT_CBM_CFM;
157
158 data->card.dev = &pdev->dev;
159 ret = snd_soc_of_parse_card_name(&data->card, "model");
160 if (ret)
161 goto clk_fail;
162 ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
163 if (ret)
164 goto clk_fail;
165 data->card.num_links = 1;
166 data->card.dai_link = &data->dai;
167 data->card.dapm_widgets = imx_sgtl5000_dapm_widgets;
168 data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets);
169
170 ret = snd_soc_register_card(&data->card);
171 if (ret) {
172 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
173 goto clk_fail;
174 }
175
176 platform_set_drvdata(pdev, data);
177clk_fail:
178 clk_put(data->codec_clk);
179fail:
180 if (ssi_np)
181 of_node_put(ssi_np);
182 if (codec_np)
183 of_node_put(codec_np);
184
185 return ret;
186}
187
188static int __devexit imx_sgtl5000_remove(struct platform_device *pdev)
189{
190 struct imx_sgtl5000_data *data = platform_get_drvdata(pdev);
191
192 if (data->codec_clk) {
193 clk_disable_unprepare(data->codec_clk);
194 clk_put(data->codec_clk);
195 }
196 snd_soc_unregister_card(&data->card);
197
198 return 0;
199}
200
201static const struct of_device_id imx_sgtl5000_dt_ids[] = {
202 { .compatible = "fsl,imx-audio-sgtl5000", },
203 { /* sentinel */ }
204};
205MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids);
206
207static struct platform_driver imx_sgtl5000_driver = {
208 .driver = {
209 .name = "imx-sgtl5000",
210 .owner = THIS_MODULE,
211 .of_match_table = imx_sgtl5000_dt_ids,
212 },
213 .probe = imx_sgtl5000_probe,
214 .remove = __devexit_p(imx_sgtl5000_remove),
215};
216module_platform_driver(imx_sgtl5000_driver);
217
218MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
219MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver");
220MODULE_LICENSE("GPL v2");
221MODULE_ALIAS("platform:imx-sgtl5000");
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
new file mode 100644
index 000000000000..cf3ed0362c9c
--- /dev/null
+++ b/sound/soc/fsl/imx-ssi.c
@@ -0,0 +1,690 @@
1/*
2 * imx-ssi.c -- ALSA Soc Audio Layer
3 *
4 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
5 *
6 * This code is based on code copyrighted by Freescale,
7 * Liam Girdwood, Javier Martin and probably others.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 *
14 *
15 * The i.MX SSI core has some nasty limitations in AC97 mode. While most
16 * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
17 * one FIFO which combines all valid receive slots. We cannot even select
18 * which slots we want to receive. The WM9712 with which this driver
19 * was developed with always sends GPIO status data in slot 12 which
20 * we receive in our (PCM-) data stream. The only chance we have is to
21 * manually skip this data in the FIQ handler. With sampling rates different
22 * from 48000Hz not every frame has valid receive data, so the ratio
23 * between pcm data and GPIO status data changes. Our FIQ handler is not
24 * able to handle this, hence this driver only works with 48000Hz sampling
25 * rate.
26 * Reading and writing AC97 registers is another challenge. The core
27 * provides us status bits when the read register is updated with *another*
28 * value. When we read the same register two times (and the register still
29 * contains the same value) these status bits are not set. We work
30 * around this by not polling these bits but only wait a fixed delay.
31 *
32 */
33
34#include <linux/clk.h>
35#include <linux/delay.h>
36#include <linux/device.h>
37#include <linux/dma-mapping.h>
38#include <linux/init.h>
39#include <linux/interrupt.h>
40#include <linux/module.h>
41#include <linux/platform_device.h>
42#include <linux/slab.h>
43
44#include <sound/core.h>
45#include <sound/initval.h>
46#include <sound/pcm.h>
47#include <sound/pcm_params.h>
48#include <sound/soc.h>
49
50#include <mach/ssi.h>
51#include <mach/hardware.h>
52
53#include "imx-ssi.h"
54
55#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV)
56
57/*
58 * SSI Network Mode or TDM slots configuration.
59 * Should only be called when port is inactive (i.e. SSIEN = 0).
60 */
61static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
62 unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
63{
64 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
65 u32 sccr;
66
67 sccr = readl(ssi->base + SSI_STCCR);
68 sccr &= ~SSI_STCCR_DC_MASK;
69 sccr |= SSI_STCCR_DC(slots - 1);
70 writel(sccr, ssi->base + SSI_STCCR);
71
72 sccr = readl(ssi->base + SSI_SRCCR);
73 sccr &= ~SSI_STCCR_DC_MASK;
74 sccr |= SSI_STCCR_DC(slots - 1);
75 writel(sccr, ssi->base + SSI_SRCCR);
76
77 writel(tx_mask, ssi->base + SSI_STMSK);
78 writel(rx_mask, ssi->base + SSI_SRMSK);
79
80 return 0;
81}
82
83/*
84 * SSI DAI format configuration.
85 * Should only be called when port is inactive (i.e. SSIEN = 0).
86 */
87static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
88{
89 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
90 u32 strcr = 0, scr;
91
92 scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
93
94 /* DAI mode */
95 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
96 case SND_SOC_DAIFMT_I2S:
97 /* data on rising edge of bclk, frame low 1clk before data */
98 strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
99 scr |= SSI_SCR_NET;
100 if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) {
101 scr &= ~SSI_I2S_MODE_MASK;
102 scr |= SSI_SCR_I2S_MODE_SLAVE;
103 }
104 break;
105 case SND_SOC_DAIFMT_LEFT_J:
106 /* data on rising edge of bclk, frame high with data */
107 strcr |= SSI_STCR_TXBIT0;
108 break;
109 case SND_SOC_DAIFMT_DSP_B:
110 /* data on rising edge of bclk, frame high with data */
111 strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0;
112 break;
113 case SND_SOC_DAIFMT_DSP_A:
114 /* data on rising edge of bclk, frame high 1clk before data */
115 strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0 | SSI_STCR_TEFS;
116 break;
117 }
118
119 /* DAI clock inversion */
120 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
121 case SND_SOC_DAIFMT_IB_IF:
122 strcr |= SSI_STCR_TFSI;
123 strcr &= ~SSI_STCR_TSCKP;
124 break;
125 case SND_SOC_DAIFMT_IB_NF:
126 strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
127 break;
128 case SND_SOC_DAIFMT_NB_IF:
129 strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
130 break;
131 case SND_SOC_DAIFMT_NB_NF:
132 strcr &= ~SSI_STCR_TFSI;
133 strcr |= SSI_STCR_TSCKP;
134 break;
135 }
136
137 /* DAI clock master masks */
138 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
139 case SND_SOC_DAIFMT_CBM_CFM:
140 break;
141 default:
142 /* Master mode not implemented, needs handling of clocks. */
143 return -EINVAL;
144 }
145
146 strcr |= SSI_STCR_TFEN0;
147
148 if (ssi->flags & IMX_SSI_NET)
149 scr |= SSI_SCR_NET;
150 if (ssi->flags & IMX_SSI_SYN)
151 scr |= SSI_SCR_SYN;
152
153 writel(strcr, ssi->base + SSI_STCR);
154 writel(strcr, ssi->base + SSI_SRCR);
155 writel(scr, ssi->base + SSI_SCR);
156
157 return 0;
158}
159
160/*
161 * SSI system clock configuration.
162 * Should only be called when port is inactive (i.e. SSIEN = 0).
163 */
164static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
165 int clk_id, unsigned int freq, int dir)
166{
167 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
168 u32 scr;
169
170 scr = readl(ssi->base + SSI_SCR);
171
172 switch (clk_id) {
173 case IMX_SSP_SYS_CLK:
174 if (dir == SND_SOC_CLOCK_OUT)
175 scr |= SSI_SCR_SYS_CLK_EN;
176 else
177 scr &= ~SSI_SCR_SYS_CLK_EN;
178 break;
179 default:
180 return -EINVAL;
181 }
182
183 writel(scr, ssi->base + SSI_SCR);
184
185 return 0;
186}
187
188/*
189 * SSI Clock dividers
190 * Should only be called when port is inactive (i.e. SSIEN = 0).
191 */
192static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
193 int div_id, int div)
194{
195 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
196 u32 stccr, srccr;
197
198 stccr = readl(ssi->base + SSI_STCCR);
199 srccr = readl(ssi->base + SSI_SRCCR);
200
201 switch (div_id) {
202 case IMX_SSI_TX_DIV_2:
203 stccr &= ~SSI_STCCR_DIV2;
204 stccr |= div;
205 break;
206 case IMX_SSI_TX_DIV_PSR:
207 stccr &= ~SSI_STCCR_PSR;
208 stccr |= div;
209 break;
210 case IMX_SSI_TX_DIV_PM:
211 stccr &= ~0xff;
212 stccr |= SSI_STCCR_PM(div);
213 break;
214 case IMX_SSI_RX_DIV_2:
215 stccr &= ~SSI_STCCR_DIV2;
216 stccr |= div;
217 break;
218 case IMX_SSI_RX_DIV_PSR:
219 stccr &= ~SSI_STCCR_PSR;
220 stccr |= div;
221 break;
222 case IMX_SSI_RX_DIV_PM:
223 stccr &= ~0xff;
224 stccr |= SSI_STCCR_PM(div);
225 break;
226 default:
227 return -EINVAL;
228 }
229
230 writel(stccr, ssi->base + SSI_STCCR);
231 writel(srccr, ssi->base + SSI_SRCCR);
232
233 return 0;
234}
235
236static int imx_ssi_startup(struct snd_pcm_substream *substream,
237 struct snd_soc_dai *cpu_dai)
238{
239 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
240 struct imx_pcm_dma_params *dma_data;
241
242 /* Tx/Rx config */
243 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
244 dma_data = &ssi->dma_params_tx;
245 else
246 dma_data = &ssi->dma_params_rx;
247
248 snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
249
250 return 0;
251}
252
253/*
254 * Should only be called when port is inactive (i.e. SSIEN = 0),
255 * although can be called multiple times by upper layers.
256 */
257static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
258 struct snd_pcm_hw_params *params,
259 struct snd_soc_dai *cpu_dai)
260{
261 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
262 u32 reg, sccr;
263
264 /* Tx/Rx config */
265 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
266 reg = SSI_STCCR;
267 else
268 reg = SSI_SRCCR;
269
270 if (ssi->flags & IMX_SSI_SYN)
271 reg = SSI_STCCR;
272
273 sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK;
274
275 /* DAI data (word) size */
276 switch (params_format(params)) {
277 case SNDRV_PCM_FORMAT_S16_LE:
278 sccr |= SSI_SRCCR_WL(16);
279 break;
280 case SNDRV_PCM_FORMAT_S20_3LE:
281 sccr |= SSI_SRCCR_WL(20);
282 break;
283 case SNDRV_PCM_FORMAT_S24_LE:
284 sccr |= SSI_SRCCR_WL(24);
285 break;
286 }
287
288 writel(sccr, ssi->base + reg);
289
290 return 0;
291}
292
293static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
294 struct snd_soc_dai *dai)
295{
296 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai);
297 unsigned int sier_bits, sier;
298 unsigned int scr;
299
300 scr = readl(ssi->base + SSI_SCR);
301 sier = readl(ssi->base + SSI_SIER);
302
303 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
304 if (ssi->flags & IMX_SSI_DMA)
305 sier_bits = SSI_SIER_TDMAE;
306 else
307 sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN;
308 } else {
309 if (ssi->flags & IMX_SSI_DMA)
310 sier_bits = SSI_SIER_RDMAE;
311 else
312 sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN;
313 }
314
315 switch (cmd) {
316 case SNDRV_PCM_TRIGGER_START:
317 case SNDRV_PCM_TRIGGER_RESUME:
318 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
319 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
320 scr |= SSI_SCR_TE;
321 else
322 scr |= SSI_SCR_RE;
323 sier |= sier_bits;
324
325 if (++ssi->enabled == 1)
326 scr |= SSI_SCR_SSIEN;
327
328 break;
329
330 case SNDRV_PCM_TRIGGER_STOP:
331 case SNDRV_PCM_TRIGGER_SUSPEND:
332 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
333 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
334 scr &= ~SSI_SCR_TE;
335 else
336 scr &= ~SSI_SCR_RE;
337 sier &= ~sier_bits;
338
339 if (--ssi->enabled == 0)
340 scr &= ~SSI_SCR_SSIEN;
341
342 break;
343 default:
344 return -EINVAL;
345 }
346
347 if (!(ssi->flags & IMX_SSI_USE_AC97))
348 /* rx/tx are always enabled to access ac97 registers */
349 writel(scr, ssi->base + SSI_SCR);
350
351 writel(sier, ssi->base + SSI_SIER);
352
353 return 0;
354}
355
356static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
357 .startup = imx_ssi_startup,
358 .hw_params = imx_ssi_hw_params,
359 .set_fmt = imx_ssi_set_dai_fmt,
360 .set_clkdiv = imx_ssi_set_dai_clkdiv,
361 .set_sysclk = imx_ssi_set_dai_sysclk,
362 .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
363 .trigger = imx_ssi_trigger,
364};
365
366static int imx_ssi_dai_probe(struct snd_soc_dai *dai)
367{
368 struct imx_ssi *ssi = dev_get_drvdata(dai->dev);
369 uint32_t val;
370
371 snd_soc_dai_set_drvdata(dai, ssi);
372
373 val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.burstsize) |
374 SSI_SFCSR_RFWM0(ssi->dma_params_rx.burstsize);
375 writel(val, ssi->base + SSI_SFCSR);
376
377 return 0;
378}
379
380static struct snd_soc_dai_driver imx_ssi_dai = {
381 .probe = imx_ssi_dai_probe,
382 .playback = {
383 .channels_min = 1,
384 .channels_max = 2,
385 .rates = SNDRV_PCM_RATE_8000_96000,
386 .formats = SNDRV_PCM_FMTBIT_S16_LE,
387 },
388 .capture = {
389 .channels_min = 1,
390 .channels_max = 2,
391 .rates = SNDRV_PCM_RATE_8000_96000,
392 .formats = SNDRV_PCM_FMTBIT_S16_LE,
393 },
394 .ops = &imx_ssi_pcm_dai_ops,
395};
396
397static struct snd_soc_dai_driver imx_ac97_dai = {
398 .probe = imx_ssi_dai_probe,
399 .ac97_control = 1,
400 .playback = {
401 .stream_name = "AC97 Playback",
402 .channels_min = 2,
403 .channels_max = 2,
404 .rates = SNDRV_PCM_RATE_48000,
405 .formats = SNDRV_PCM_FMTBIT_S16_LE,
406 },
407 .capture = {
408 .stream_name = "AC97 Capture",
409 .channels_min = 2,
410 .channels_max = 2,
411 .rates = SNDRV_PCM_RATE_48000,
412 .formats = SNDRV_PCM_FMTBIT_S16_LE,
413 },
414 .ops = &imx_ssi_pcm_dai_ops,
415};
416
417static void setup_channel_to_ac97(struct imx_ssi *imx_ssi)
418{
419 void __iomem *base = imx_ssi->base;
420
421 writel(0x0, base + SSI_SCR);
422 writel(0x0, base + SSI_STCR);
423 writel(0x0, base + SSI_SRCR);
424
425 writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR);
426
427 writel(SSI_SFCSR_RFWM0(8) |
428 SSI_SFCSR_TFWM0(8) |
429 SSI_SFCSR_RFWM1(8) |
430 SSI_SFCSR_TFWM1(8), base + SSI_SFCSR);
431
432 writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR);
433 writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR);
434
435 writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR);
436 writel(SSI_SOR_WAIT(3), base + SSI_SOR);
437
438 writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN |
439 SSI_SCR_TE | SSI_SCR_RE,
440 base + SSI_SCR);
441
442 writel(SSI_SACNT_DEFAULT, base + SSI_SACNT);
443 writel(0xff, base + SSI_SACCDIS);
444 writel(0x300, base + SSI_SACCEN);
445}
446
447static struct imx_ssi *ac97_ssi;
448
449static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
450 unsigned short val)
451{
452 struct imx_ssi *imx_ssi = ac97_ssi;
453 void __iomem *base = imx_ssi->base;
454 unsigned int lreg;
455 unsigned int lval;
456
457 if (reg > 0x7f)
458 return;
459
460 pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
461
462 lreg = reg << 12;
463 writel(lreg, base + SSI_SACADD);
464
465 lval = val << 4;
466 writel(lval , base + SSI_SACDAT);
467
468 writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT);
469 udelay(100);
470}
471
472static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97,
473 unsigned short reg)
474{
475 struct imx_ssi *imx_ssi = ac97_ssi;
476 void __iomem *base = imx_ssi->base;
477
478 unsigned short val = -1;
479 unsigned int lreg;
480
481 lreg = (reg & 0x7f) << 12 ;
482 writel(lreg, base + SSI_SACADD);
483 writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT);
484
485 udelay(100);
486
487 val = (readl(base + SSI_SACDAT) >> 4) & 0xffff;
488
489 pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
490
491 return val;
492}
493
494static void imx_ssi_ac97_reset(struct snd_ac97 *ac97)
495{
496 struct imx_ssi *imx_ssi = ac97_ssi;
497
498 if (imx_ssi->ac97_reset)
499 imx_ssi->ac97_reset(ac97);
500}
501
502static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
503{
504 struct imx_ssi *imx_ssi = ac97_ssi;
505
506 if (imx_ssi->ac97_warm_reset)
507 imx_ssi->ac97_warm_reset(ac97);
508}
509
510struct snd_ac97_bus_ops soc_ac97_ops = {
511 .read = imx_ssi_ac97_read,
512 .write = imx_ssi_ac97_write,
513 .reset = imx_ssi_ac97_reset,
514 .warm_reset = imx_ssi_ac97_warm_reset
515};
516EXPORT_SYMBOL_GPL(soc_ac97_ops);
517
518static int imx_ssi_probe(struct platform_device *pdev)
519{
520 struct resource *res;
521 struct imx_ssi *ssi;
522 struct imx_ssi_platform_data *pdata = pdev->dev.platform_data;
523 int ret = 0;
524 struct snd_soc_dai_driver *dai;
525
526 ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
527 if (!ssi)
528 return -ENOMEM;
529 dev_set_drvdata(&pdev->dev, ssi);
530
531 if (pdata) {
532 ssi->ac97_reset = pdata->ac97_reset;
533 ssi->ac97_warm_reset = pdata->ac97_warm_reset;
534 ssi->flags = pdata->flags;
535 }
536
537 ssi->irq = platform_get_irq(pdev, 0);
538
539 ssi->clk = clk_get(&pdev->dev, NULL);
540 if (IS_ERR(ssi->clk)) {
541 ret = PTR_ERR(ssi->clk);
542 dev_err(&pdev->dev, "Cannot get the clock: %d\n",
543 ret);
544 goto failed_clk;
545 }
546 clk_enable(ssi->clk);
547
548 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
549 if (!res) {
550 ret = -ENODEV;
551 goto failed_get_resource;
552 }
553
554 if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) {
555 dev_err(&pdev->dev, "request_mem_region failed\n");
556 ret = -EBUSY;
557 goto failed_get_resource;
558 }
559
560 ssi->base = ioremap(res->start, resource_size(res));
561 if (!ssi->base) {
562 dev_err(&pdev->dev, "ioremap failed\n");
563 ret = -ENODEV;
564 goto failed_ioremap;
565 }
566
567 if (ssi->flags & IMX_SSI_USE_AC97) {
568 if (ac97_ssi) {
569 ret = -EBUSY;
570 goto failed_ac97;
571 }
572 ac97_ssi = ssi;
573 setup_channel_to_ac97(ssi);
574 dai = &imx_ac97_dai;
575 } else
576 dai = &imx_ssi_dai;
577
578 writel(0x0, ssi->base + SSI_SIER);
579
580 ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0;
581 ssi->dma_params_tx.dma_addr = res->start + SSI_STX0;
582
583 ssi->dma_params_tx.burstsize = 6;
584 ssi->dma_params_rx.burstsize = 4;
585
586 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
587 if (res)
588 ssi->dma_params_tx.dma = res->start;
589
590 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0");
591 if (res)
592 ssi->dma_params_rx.dma = res->start;
593
594 platform_set_drvdata(pdev, ssi);
595
596 ret = snd_soc_register_dai(&pdev->dev, dai);
597 if (ret) {
598 dev_err(&pdev->dev, "register DAI failed\n");
599 goto failed_register;
600 }
601
602 ssi->soc_platform_pdev_fiq = platform_device_alloc("imx-fiq-pcm-audio", pdev->id);
603 if (!ssi->soc_platform_pdev_fiq) {
604 ret = -ENOMEM;
605 goto failed_pdev_fiq_alloc;
606 }
607
608 platform_set_drvdata(ssi->soc_platform_pdev_fiq, ssi);
609 ret = platform_device_add(ssi->soc_platform_pdev_fiq);
610 if (ret) {
611 dev_err(&pdev->dev, "failed to add platform device\n");
612 goto failed_pdev_fiq_add;
613 }
614
615 ssi->soc_platform_pdev = platform_device_alloc("imx-pcm-audio", pdev->id);
616 if (!ssi->soc_platform_pdev) {
617 ret = -ENOMEM;
618 goto failed_pdev_alloc;
619 }
620
621 platform_set_drvdata(ssi->soc_platform_pdev, ssi);
622 ret = platform_device_add(ssi->soc_platform_pdev);
623 if (ret) {
624 dev_err(&pdev->dev, "failed to add platform device\n");
625 goto failed_pdev_add;
626 }
627
628 return 0;
629
630failed_pdev_add:
631 platform_device_put(ssi->soc_platform_pdev);
632failed_pdev_alloc:
633 platform_device_del(ssi->soc_platform_pdev_fiq);
634failed_pdev_fiq_add:
635 platform_device_put(ssi->soc_platform_pdev_fiq);
636failed_pdev_fiq_alloc:
637 snd_soc_unregister_dai(&pdev->dev);
638failed_register:
639failed_ac97:
640 iounmap(ssi->base);
641failed_ioremap:
642 release_mem_region(res->start, resource_size(res));
643failed_get_resource:
644 clk_disable(ssi->clk);
645 clk_put(ssi->clk);
646failed_clk:
647 kfree(ssi);
648
649 return ret;
650}
651
652static int __devexit imx_ssi_remove(struct platform_device *pdev)
653{
654 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
655 struct imx_ssi *ssi = platform_get_drvdata(pdev);
656
657 platform_device_unregister(ssi->soc_platform_pdev);
658 platform_device_unregister(ssi->soc_platform_pdev_fiq);
659
660 snd_soc_unregister_dai(&pdev->dev);
661
662 if (ssi->flags & IMX_SSI_USE_AC97)
663 ac97_ssi = NULL;
664
665 iounmap(ssi->base);
666 release_mem_region(res->start, resource_size(res));
667 clk_disable(ssi->clk);
668 clk_put(ssi->clk);
669 kfree(ssi);
670
671 return 0;
672}
673
674static struct platform_driver imx_ssi_driver = {
675 .probe = imx_ssi_probe,
676 .remove = __devexit_p(imx_ssi_remove),
677
678 .driver = {
679 .name = "imx-ssi",
680 .owner = THIS_MODULE,
681 },
682};
683
684module_platform_driver(imx_ssi_driver);
685
686/* Module information */
687MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>");
688MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface");
689MODULE_LICENSE("GPL");
690MODULE_ALIAS("platform:imx-ssi");
diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
new file mode 100644
index 000000000000..5744e86ca878
--- /dev/null
+++ b/sound/soc/fsl/imx-ssi.h
@@ -0,0 +1,216 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 */
6
7#ifndef _IMX_SSI_H
8#define _IMX_SSI_H
9
10#define SSI_STX0 0x00
11#define SSI_STX1 0x04
12#define SSI_SRX0 0x08
13#define SSI_SRX1 0x0c
14
15#define SSI_SCR 0x10
16#define SSI_SCR_CLK_IST (1 << 9)
17#define SSI_SCR_CLK_IST_SHIFT 9
18#define SSI_SCR_TCH_EN (1 << 8)
19#define SSI_SCR_SYS_CLK_EN (1 << 7)
20#define SSI_SCR_I2S_MODE_NORM (0 << 5)
21#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
22#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
23#define SSI_I2S_MODE_MASK (3 << 5)
24#define SSI_SCR_SYN (1 << 4)
25#define SSI_SCR_NET (1 << 3)
26#define SSI_SCR_RE (1 << 2)
27#define SSI_SCR_TE (1 << 1)
28#define SSI_SCR_SSIEN (1 << 0)
29
30#define SSI_SISR 0x14
31#define SSI_SISR_MASK ((1 << 19) - 1)
32#define SSI_SISR_CMDAU (1 << 18)
33#define SSI_SISR_CMDDU (1 << 17)
34#define SSI_SISR_RXT (1 << 16)
35#define SSI_SISR_RDR1 (1 << 15)
36#define SSI_SISR_RDR0 (1 << 14)
37#define SSI_SISR_TDE1 (1 << 13)
38#define SSI_SISR_TDE0 (1 << 12)
39#define SSI_SISR_ROE1 (1 << 11)
40#define SSI_SISR_ROE0 (1 << 10)
41#define SSI_SISR_TUE1 (1 << 9)
42#define SSI_SISR_TUE0 (1 << 8)
43#define SSI_SISR_TFS (1 << 7)
44#define SSI_SISR_RFS (1 << 6)
45#define SSI_SISR_TLS (1 << 5)
46#define SSI_SISR_RLS (1 << 4)
47#define SSI_SISR_RFF1 (1 << 3)
48#define SSI_SISR_RFF0 (1 << 2)
49#define SSI_SISR_TFE1 (1 << 1)
50#define SSI_SISR_TFE0 (1 << 0)
51
52#define SSI_SIER 0x18
53#define SSI_SIER_RDMAE (1 << 22)
54#define SSI_SIER_RIE (1 << 21)
55#define SSI_SIER_TDMAE (1 << 20)
56#define SSI_SIER_TIE (1 << 19)
57#define SSI_SIER_CMDAU_EN (1 << 18)
58#define SSI_SIER_CMDDU_EN (1 << 17)
59#define SSI_SIER_RXT_EN (1 << 16)
60#define SSI_SIER_RDR1_EN (1 << 15)
61#define SSI_SIER_RDR0_EN (1 << 14)
62#define SSI_SIER_TDE1_EN (1 << 13)
63#define SSI_SIER_TDE0_EN (1 << 12)
64#define SSI_SIER_ROE1_EN (1 << 11)
65#define SSI_SIER_ROE0_EN (1 << 10)
66#define SSI_SIER_TUE1_EN (1 << 9)
67#define SSI_SIER_TUE0_EN (1 << 8)
68#define SSI_SIER_TFS_EN (1 << 7)
69#define SSI_SIER_RFS_EN (1 << 6)
70#define SSI_SIER_TLS_EN (1 << 5)
71#define SSI_SIER_RLS_EN (1 << 4)
72#define SSI_SIER_RFF1_EN (1 << 3)
73#define SSI_SIER_RFF0_EN (1 << 2)
74#define SSI_SIER_TFE1_EN (1 << 1)
75#define SSI_SIER_TFE0_EN (1 << 0)
76
77#define SSI_STCR 0x1c
78#define SSI_STCR_TXBIT0 (1 << 9)
79#define SSI_STCR_TFEN1 (1 << 8)
80#define SSI_STCR_TFEN0 (1 << 7)
81#define SSI_FIFO_ENABLE_0_SHIFT 7
82#define SSI_STCR_TFDIR (1 << 6)
83#define SSI_STCR_TXDIR (1 << 5)
84#define SSI_STCR_TSHFD (1 << 4)
85#define SSI_STCR_TSCKP (1 << 3)
86#define SSI_STCR_TFSI (1 << 2)
87#define SSI_STCR_TFSL (1 << 1)
88#define SSI_STCR_TEFS (1 << 0)
89
90#define SSI_SRCR 0x20
91#define SSI_SRCR_RXBIT0 (1 << 9)
92#define SSI_SRCR_RFEN1 (1 << 8)
93#define SSI_SRCR_RFEN0 (1 << 7)
94#define SSI_FIFO_ENABLE_0_SHIFT 7
95#define SSI_SRCR_RFDIR (1 << 6)
96#define SSI_SRCR_RXDIR (1 << 5)
97#define SSI_SRCR_RSHFD (1 << 4)
98#define SSI_SRCR_RSCKP (1 << 3)
99#define SSI_SRCR_RFSI (1 << 2)
100#define SSI_SRCR_RFSL (1 << 1)
101#define SSI_SRCR_REFS (1 << 0)
102
103#define SSI_SRCCR 0x28
104#define SSI_SRCCR_DIV2 (1 << 18)
105#define SSI_SRCCR_PSR (1 << 17)
106#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
107#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
108#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
109#define SSI_SRCCR_WL_MASK (0xf << 13)
110#define SSI_SRCCR_DC_MASK (0x1f << 8)
111#define SSI_SRCCR_PM_MASK (0xff << 0)
112
113#define SSI_STCCR 0x24
114#define SSI_STCCR_DIV2 (1 << 18)
115#define SSI_STCCR_PSR (1 << 17)
116#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
117#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
118#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
119#define SSI_STCCR_WL_MASK (0xf << 13)
120#define SSI_STCCR_DC_MASK (0x1f << 8)
121#define SSI_STCCR_PM_MASK (0xff << 0)
122
123#define SSI_SFCSR 0x2c
124#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
125#define SSI_RX_FIFO_1_COUNT_SHIFT 28
126#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
127#define SSI_TX_FIFO_1_COUNT_SHIFT 24
128#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
129#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
130#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
131#define SSI_RX_FIFO_0_COUNT_SHIFT 12
132#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
133#define SSI_TX_FIFO_0_COUNT_SHIFT 8
134#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
135#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
136#define SSI_SFCSR_RFWM0_MASK (0xf << 4)
137#define SSI_SFCSR_TFWM0_MASK (0xf << 0)
138
139#define SSI_STR 0x30
140#define SSI_STR_TEST (1 << 15)
141#define SSI_STR_RCK2TCK (1 << 14)
142#define SSI_STR_RFS2TFS (1 << 13)
143#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
144#define SSI_STR_TXD2RXD (1 << 7)
145#define SSI_STR_TCK2RCK (1 << 6)
146#define SSI_STR_TFS2RFS (1 << 5)
147#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
148
149#define SSI_SOR 0x34
150#define SSI_SOR_CLKOFF (1 << 6)
151#define SSI_SOR_RX_CLR (1 << 5)
152#define SSI_SOR_TX_CLR (1 << 4)
153#define SSI_SOR_INIT (1 << 3)
154#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
155#define SSI_SOR_WAIT_MASK (0x3 << 1)
156#define SSI_SOR_SYNRST (1 << 0)
157
158#define SSI_SACNT 0x38
159#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
160#define SSI_SACNT_WR (1 << 4)
161#define SSI_SACNT_RD (1 << 3)
162#define SSI_SACNT_TIF (1 << 2)
163#define SSI_SACNT_FV (1 << 1)
164#define SSI_SACNT_AC97EN (1 << 0)
165
166#define SSI_SACADD 0x3c
167#define SSI_SACDAT 0x40
168#define SSI_SATAG 0x44
169#define SSI_STMSK 0x48
170#define SSI_SRMSK 0x4c
171#define SSI_SACCST 0x50
172#define SSI_SACCEN 0x54
173#define SSI_SACCDIS 0x58
174
175/* SSI clock sources */
176#define IMX_SSP_SYS_CLK 0
177
178/* SSI audio dividers */
179#define IMX_SSI_TX_DIV_2 0
180#define IMX_SSI_TX_DIV_PSR 1
181#define IMX_SSI_TX_DIV_PM 2
182#define IMX_SSI_RX_DIV_2 3
183#define IMX_SSI_RX_DIV_PSR 4
184#define IMX_SSI_RX_DIV_PM 5
185
186#define DRV_NAME "imx-ssi"
187
188#include <linux/dmaengine.h>
189#include <mach/dma.h>
190#include "imx-pcm.h"
191
192struct imx_ssi {
193 struct platform_device *ac97_dev;
194
195 struct snd_soc_dai *imx_ac97;
196 struct clk *clk;
197 void __iomem *base;
198 int irq;
199 int fiq_enable;
200 unsigned int offset;
201
202 unsigned int flags;
203
204 void (*ac97_reset) (struct snd_ac97 *ac97);
205 void (*ac97_warm_reset)(struct snd_ac97 *ac97);
206
207 struct imx_pcm_dma_params dma_params_rx;
208 struct imx_pcm_dma_params dma_params_tx;
209
210 int enabled;
211
212 struct platform_device *soc_platform_pdev;
213 struct platform_device *soc_platform_pdev_fiq;
214};
215
216#endif /* _IMX_SSI_H */
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index 3fea5a15ffe8..60bcba1bc30e 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -14,18 +14,16 @@
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/slab.h> 16#include <linux/slab.h>
17#include <linux/of_i2c.h>
18#include <sound/soc.h> 17#include <sound/soc.h>
19#include <asm/fsl_guts.h> 18#include <asm/fsl_guts.h>
20 19
21#include "fsl_dma.h" 20#include "fsl_dma.h"
22#include "fsl_ssi.h" 21#include "fsl_ssi.h"
22#include "fsl_utils.h"
23 23
24/* There's only one global utilities register */ 24/* There's only one global utilities register */
25static phys_addr_t guts_phys; 25static phys_addr_t guts_phys;
26 26
27#define DAI_NAME_SIZE 32
28
29/** 27/**
30 * mpc8610_hpcd_data: machine-specific ASoC device data 28 * mpc8610_hpcd_data: machine-specific ASoC device data
31 * 29 *
@@ -43,7 +41,6 @@ struct mpc8610_hpcd_data {
43 unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ 41 unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
44 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*/
45 char codec_dai_name[DAI_NAME_SIZE]; 43 char codec_dai_name[DAI_NAME_SIZE];
46 char codec_name[DAI_NAME_SIZE];
47 char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ 44 char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
48}; 45};
49 46
@@ -181,141 +178,6 @@ static struct snd_soc_ops mpc8610_hpcd_ops = {
181}; 178};
182 179
183/** 180/**
184 * get_node_by_phandle_name - get a node by its phandle name
185 *
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.
189 *
190 * If the property is not a phandle, or the node it points to is not compatible
191 * with the specific string, then NULL is returned.
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
218 *
219 * Return the value of the cell-index property of the parent of the given
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 be32_to_cpup(iprop);
238}
239
240/**
241 * codec_node_dev_name - determine the dev_name for a codec node
242 *
243 * This function determines the dev_name for an I2C node. This is the name
244 * that would be returned by dev_name() if this device_node were part of a
245 * 'struct device' It's ugly and hackish, but it works.
246 *
247 * The dev_name for such devices include the bus number and I2C address. For
248 * example, "cs4270.0-004f".
249 */
250static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
251{
252 const u32 *iprop;
253 int addr;
254 char temp[DAI_NAME_SIZE];
255 struct i2c_client *i2c;
256
257 of_modalias_node(np, temp, DAI_NAME_SIZE);
258
259 iprop = of_get_property(np, "reg", NULL);
260 if (!iprop)
261 return -EINVAL;
262
263 addr = be32_to_cpup(iprop);
264
265 /* We need the adapter number */
266 i2c = of_find_i2c_device_by_node(np);
267 if (!i2c)
268 return -ENODEV;
269
270 snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr);
271
272 return 0;
273}
274
275static int get_dma_channel(struct device_node *ssi_np,
276 const char *name,
277 struct snd_soc_dai_link *dai,
278 unsigned int *dma_channel_id,
279 unsigned int *dma_id)
280{
281 struct resource res;
282 struct device_node *dma_channel_np;
283 const u32 *iprop;
284 int ret;
285
286 dma_channel_np = get_node_by_phandle_name(ssi_np, name,
287 "fsl,ssi-dma-channel");
288 if (!dma_channel_np)
289 return -EINVAL;
290
291 /* Determine the dev_name for the device_node. This code mimics the
292 * behavior of of_device_make_bus_id(). We need this because ASoC uses
293 * the dev_name() of the device to match the platform (DMA) device with
294 * the CPU (SSI) device. It's all ugly and hackish, but it works (for
295 * now).
296 *
297 * dai->platform name should already point to an allocated buffer.
298 */
299 ret = of_address_to_resource(dma_channel_np, 0, &res);
300 if (ret)
301 return ret;
302 snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
303 (unsigned long long) res.start, dma_channel_np->name);
304
305 iprop = of_get_property(dma_channel_np, "cell-index", NULL);
306 if (!iprop) {
307 of_node_put(dma_channel_np);
308 return -EINVAL;
309 }
310
311 *dma_channel_id = be32_to_cpup(iprop);
312 *dma_id = get_parent_cell_index(dma_channel_np);
313 of_node_put(dma_channel_np);
314
315 return 0;
316}
317
318/**
319 * mpc8610_hpcd_probe: platform probe function for the machine driver 181 * mpc8610_hpcd_probe: platform probe function for the machine driver
320 * 182 *
321 * Although this is a machine driver, the SSI node is the "master" node with 183 * Although this is a machine driver, the SSI node is the "master" node with
@@ -352,16 +214,8 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
352 machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); 214 machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
353 machine_data->dai[0].ops = &mpc8610_hpcd_ops; 215 machine_data->dai[0].ops = &mpc8610_hpcd_ops;
354 216
355 /* Determine the codec name, it will be used as the codec DAI name */ 217 /* ASoC core can match codec with device node */
356 ret = codec_node_dev_name(codec_np, machine_data->codec_name, 218 machine_data->dai[0].codec_of_node = codec_np;
357 DAI_NAME_SIZE);
358 if (ret) {
359 dev_err(&pdev->dev, "invalid codec node %s\n",
360 codec_np->full_name);
361 ret = -EINVAL;
362 goto error;
363 }
364 machine_data->dai[0].codec_name = machine_data->codec_name;
365 219
366 /* The DAI name from the codec (snd_soc_dai_driver.name) */ 220 /* The DAI name from the codec (snd_soc_dai_driver.name) */
367 machine_data->dai[0].codec_dai_name = "cs4270-hifi"; 221 machine_data->dai[0].codec_dai_name = "cs4270-hifi";
@@ -458,9 +312,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
458 312
459 /* Find the playback DMA channel to use. */ 313 /* Find the playback DMA channel to use. */
460 machine_data->dai[0].platform_name = machine_data->platform_name[0]; 314 machine_data->dai[0].platform_name = machine_data->platform_name[0];
461 ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0], 315 ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma",
462 &machine_data->dma_channel_id[0], 316 &machine_data->dai[0],
463 &machine_data->dma_id[0]); 317 &machine_data->dma_channel_id[0],
318 &machine_data->dma_id[0]);
464 if (ret) { 319 if (ret) {
465 dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); 320 dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
466 goto error; 321 goto error;
@@ -468,9 +323,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
468 323
469 /* Find the capture DMA channel to use. */ 324 /* Find the capture DMA channel to use. */
470 machine_data->dai[1].platform_name = machine_data->platform_name[1]; 325 machine_data->dai[1].platform_name = machine_data->platform_name[1];
471 ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1], 326 ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma",
472 &machine_data->dma_channel_id[1], 327 &machine_data->dai[1],
473 &machine_data->dma_id[1]); 328 &machine_data->dma_channel_id[1],
329 &machine_data->dma_id[1]);
474 if (ret) { 330 if (ret) {
475 dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); 331 dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
476 goto error; 332 goto error;
diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c
new file mode 100644
index 000000000000..f6d04ad4bb39
--- /dev/null
+++ b/sound/soc/fsl/mx27vis-aic32x4.c
@@ -0,0 +1,245 @@
1/*
2 * mx27vis-aic32x4.c
3 *
4 * Copyright 2011 Vista Silicon S.L.
5 *
6 * Author: Javier Martin <javier.martin@vista-silicon.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 */
23
24#include <linux/module.h>
25#include <linux/moduleparam.h>
26#include <linux/device.h>
27#include <linux/i2c.h>
28#include <linux/gpio.h>
29#include <sound/core.h>
30#include <sound/pcm.h>
31#include <sound/soc.h>
32#include <sound/soc-dapm.h>
33#include <sound/tlv.h>
34#include <asm/mach-types.h>
35#include <mach/iomux-mx27.h>
36
37#include "../codecs/tlv320aic32x4.h"
38#include "imx-ssi.h"
39#include "imx-audmux.h"
40
41#define MX27VIS_AMP_GAIN 0
42#define MX27VIS_AMP_MUTE 1
43
44#define MX27VIS_PIN_G0 (GPIO_PORTF + 9)
45#define MX27VIS_PIN_G1 (GPIO_PORTF + 8)
46#define MX27VIS_PIN_SDL (GPIO_PORTE + 5)
47#define MX27VIS_PIN_SDR (GPIO_PORTF + 7)
48
49static int mx27vis_amp_gain;
50static int mx27vis_amp_mute;
51
52static const int mx27vis_amp_pins[] = {
53 MX27VIS_PIN_G0 | GPIO_GPIO | GPIO_OUT,
54 MX27VIS_PIN_G1 | GPIO_GPIO | GPIO_OUT,
55 MX27VIS_PIN_SDL | GPIO_GPIO | GPIO_OUT,
56 MX27VIS_PIN_SDR | GPIO_GPIO | GPIO_OUT,
57};
58
59static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
60 struct snd_pcm_hw_params *params)
61{
62 struct snd_soc_pcm_runtime *rtd = substream->private_data;
63 struct snd_soc_dai *codec_dai = rtd->codec_dai;
64 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
65 int ret;
66 u32 dai_format;
67
68 dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
69 SND_SOC_DAIFMT_CBM_CFM;
70
71 /* set codec DAI configuration */
72 snd_soc_dai_set_fmt(codec_dai, dai_format);
73
74 /* set cpu DAI configuration */
75 snd_soc_dai_set_fmt(cpu_dai, dai_format);
76
77 ret = snd_soc_dai_set_sysclk(codec_dai, 0,
78 25000000, SND_SOC_CLOCK_OUT);
79 if (ret) {
80 pr_err("%s: failed setting codec sysclk\n", __func__);
81 return ret;
82 }
83
84 ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
85 SND_SOC_CLOCK_IN);
86 if (ret) {
87 pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
88 return ret;
89 }
90
91 return 0;
92}
93
94static struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
95 .hw_params = mx27vis_aic32x4_hw_params,
96};
97
98static int mx27vis_amp_set(struct snd_kcontrol *kcontrol,
99 struct snd_ctl_elem_value *ucontrol)
100{
101 struct soc_mixer_control *mc =
102 (struct soc_mixer_control *)kcontrol->private_value;
103 int value = ucontrol->value.integer.value[0];
104 unsigned int reg = mc->reg;
105 int max = mc->max;
106
107 if (value > max)
108 return -EINVAL;
109
110 switch (reg) {
111 case MX27VIS_AMP_GAIN:
112 gpio_set_value(MX27VIS_PIN_G0, value & 1);
113 gpio_set_value(MX27VIS_PIN_G1, value >> 1);
114 mx27vis_amp_gain = value;
115 break;
116 case MX27VIS_AMP_MUTE:
117 gpio_set_value(MX27VIS_PIN_SDL, value & 1);
118 gpio_set_value(MX27VIS_PIN_SDR, value >> 1);
119 mx27vis_amp_mute = value;
120 break;
121 }
122 return 0;
123}
124
125static int mx27vis_amp_get(struct snd_kcontrol *kcontrol,
126 struct snd_ctl_elem_value *ucontrol)
127{
128 struct soc_mixer_control *mc =
129 (struct soc_mixer_control *)kcontrol->private_value;
130 unsigned int reg = mc->reg;
131
132 switch (reg) {
133 case MX27VIS_AMP_GAIN:
134 ucontrol->value.integer.value[0] = mx27vis_amp_gain;
135 break;
136 case MX27VIS_AMP_MUTE:
137 ucontrol->value.integer.value[0] = mx27vis_amp_mute;
138 break;
139 }
140 return 0;
141}
142
143/* From 6dB to 24dB in steps of 6dB */
144static const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0);
145
146static const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = {
147 SOC_DAPM_PIN_SWITCH("External Mic"),
148 SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0,
149 mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv),
150 SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0,
151 mx27vis_amp_get, mx27vis_amp_set),
152};
153
154static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
155 SND_SOC_DAPM_MIC("External Mic", NULL),
156};
157
158static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
159 {"Mic Bias", NULL, "External Mic"},
160 {"IN1_R", NULL, "Mic Bias"},
161 {"IN2_R", NULL, "Mic Bias"},
162 {"IN3_R", NULL, "Mic Bias"},
163 {"IN1_L", NULL, "Mic Bias"},
164 {"IN2_L", NULL, "Mic Bias"},
165 {"IN3_L", NULL, "Mic Bias"},
166};
167
168static struct snd_soc_dai_link mx27vis_aic32x4_dai = {
169 .name = "tlv320aic32x4",
170 .stream_name = "TLV320AIC32X4",
171 .codec_dai_name = "tlv320aic32x4-hifi",
172 .platform_name = "imx-pcm-audio.0",
173 .codec_name = "tlv320aic32x4.0-0018",
174 .cpu_dai_name = "imx-ssi.0",
175 .ops = &mx27vis_aic32x4_snd_ops,
176};
177
178static struct snd_soc_card mx27vis_aic32x4 = {
179 .name = "visstrim_m10-audio",
180 .owner = THIS_MODULE,
181 .dai_link = &mx27vis_aic32x4_dai,
182 .num_links = 1,
183 .controls = mx27vis_aic32x4_controls,
184 .num_controls = ARRAY_SIZE(mx27vis_aic32x4_controls),
185 .dapm_widgets = aic32x4_dapm_widgets,
186 .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
187 .dapm_routes = aic32x4_dapm_routes,
188 .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
189};
190
191static int __devinit mx27vis_aic32x4_probe(struct platform_device *pdev)
192{
193 int ret;
194
195 mx27vis_aic32x4.dev = &pdev->dev;
196 ret = snd_soc_register_card(&mx27vis_aic32x4);
197 if (ret) {
198 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
199 ret);
200 return ret;
201 }
202
203 /* Connect SSI0 as clock slave to SSI1 external pins */
204 imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
205 IMX_AUDMUX_V1_PCR_SYN |
206 IMX_AUDMUX_V1_PCR_TFSDIR |
207 IMX_AUDMUX_V1_PCR_TCLKDIR |
208 IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) |
209 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1)
210 );
211 imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1,
212 IMX_AUDMUX_V1_PCR_SYN |
213 IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
214 );
215
216 ret = mxc_gpio_setup_multiple_pins(mx27vis_amp_pins,
217 ARRAY_SIZE(mx27vis_amp_pins), "MX27VIS_AMP");
218 if (ret)
219 printk(KERN_ERR "ASoC: unable to setup gpios\n");
220
221 return ret;
222}
223
224static int __devexit mx27vis_aic32x4_remove(struct platform_device *pdev)
225{
226 snd_soc_unregister_card(&mx27vis_aic32x4);
227
228 return 0;
229}
230
231static struct platform_driver mx27vis_aic32x4_audio_driver = {
232 .driver = {
233 .name = "mx27vis",
234 .owner = THIS_MODULE,
235 },
236 .probe = mx27vis_aic32x4_probe,
237 .remove = __devexit_p(mx27vis_aic32x4_remove),
238};
239
240module_platform_driver(mx27vis_aic32x4_audio_driver);
241
242MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
243MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim");
244MODULE_LICENSE("GPL");
245MODULE_ALIAS("platform:mx27vis");
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index 982a1c944983..50adf4032bcc 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -14,12 +14,12 @@
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/slab.h> 16#include <linux/slab.h>
17#include <linux/of_i2c.h>
18#include <sound/soc.h> 17#include <sound/soc.h>
19#include <asm/fsl_guts.h> 18#include <asm/fsl_guts.h>
20 19
21#include "fsl_dma.h" 20#include "fsl_dma.h"
22#include "fsl_ssi.h" 21#include "fsl_ssi.h"
22#include "fsl_utils.h"
23 23
24/* P1022-specific PMUXCR and DMUXCR bit definitions */ 24/* P1022-specific PMUXCR and DMUXCR bit definitions */
25 25
@@ -57,8 +57,6 @@ static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts,
57/* There's only one global utilities register */ 57/* There's only one global utilities register */
58static phys_addr_t guts_phys; 58static phys_addr_t guts_phys;
59 59
60#define DAI_NAME_SIZE 32
61
62/** 60/**
63 * machine_data: machine-specific ASoC device data 61 * machine_data: machine-specific ASoC device data
64 * 62 *
@@ -75,7 +73,6 @@ struct machine_data {
75 unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ 73 unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */
76 unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ 74 unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
77 unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ 75 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 */ 76 char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
80}; 77};
81 78
@@ -191,136 +188,6 @@ static struct snd_soc_ops p1022_ds_ops = {
191}; 188};
192 189
193/** 190/**
194 * get_node_by_phandle_name - get a node by its phandle name
195 *
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.
199 *
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.
202 */
203static struct device_node *get_node_by_phandle_name(struct device_node *np,
204 const char *name, const char *compatible)
205{
206 np = of_parse_phandle(np, name, 0);
207 if (!np)
208 return NULL;
209
210 if (!of_device_is_compatible(np, compatible)) {
211 of_node_put(np);
212 return NULL;
213 }
214
215 return np;
216}
217
218/**
219 * get_parent_cell_index -- return the cell-index of the parent of a node
220 *
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.
224 */
225static int get_parent_cell_index(struct device_node *np)
226{
227 struct device_node *parent = of_get_parent(np);
228 const u32 *iprop;
229 int ret = -1;
230
231 if (!parent)
232 return -1;
233
234 iprop = of_get_property(parent, "cell-index", NULL);
235 if (iprop)
236 ret = be32_to_cpup(iprop);
237
238 of_node_put(parent);
239
240 return ret;
241}
242
243/**
244 * codec_node_dev_name - determine the dev_name for a codec node
245 *
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.
249 *
250 * The dev_name for such devices include the bus number and I2C address. For
251 * example, "cs4270-codec.0-004f".
252 */
253static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
254{
255 const u32 *iprop;
256 int addr;
257 char temp[DAI_NAME_SIZE];
258 struct i2c_client *i2c;
259
260 of_modalias_node(np, temp, DAI_NAME_SIZE);
261
262 iprop = of_get_property(np, "reg", NULL);
263 if (!iprop)
264 return -EINVAL;
265
266 addr = be32_to_cpup(iprop);
267
268 /* We need the adapter number */
269 i2c = of_find_i2c_device_by_node(np);
270 if (!i2c)
271 return -ENODEV;
272
273 snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr);
274
275 return 0;
276}
277
278static int get_dma_channel(struct device_node *ssi_np,
279 const char *name,
280 struct snd_soc_dai_link *dai,
281 unsigned int *dma_channel_id,
282 unsigned int *dma_id)
283{
284 struct resource res;
285 struct device_node *dma_channel_np;
286 const u32 *iprop;
287 int ret;
288
289 dma_channel_np = get_node_by_phandle_name(ssi_np, name,
290 "fsl,ssi-dma-channel");
291 if (!dma_channel_np)
292 return -EINVAL;
293
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
298 * now).
299 *
300 * dai->platform name should already point to an allocated buffer.
301 */
302 ret = of_address_to_resource(dma_channel_np, 0, &res);
303 if (ret) {
304 of_node_put(dma_channel_np);
305 return ret;
306 }
307 snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
308 (unsigned long long) res.start, dma_channel_np->name);
309
310 iprop = of_get_property(dma_channel_np, "cell-index", NULL);
311 if (!iprop) {
312 of_node_put(dma_channel_np);
313 return -EINVAL;
314 }
315
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);
319
320 return 0;
321}
322
323/**
324 * p1022_ds_probe: platform probe function for the machine driver 191 * p1022_ds_probe: platform probe function for the machine driver
325 * 192 *
326 * Although this is a machine driver, the SSI node is the "master" node with 193 * Although this is a machine driver, the SSI node is the "master" node with
@@ -357,15 +224,8 @@ static int p1022_ds_probe(struct platform_device *pdev)
357 mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); 224 mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
358 mdata->dai[0].ops = &p1022_ds_ops; 225 mdata->dai[0].ops = &p1022_ds_ops;
359 226
360 /* Determine the codec name, it will be used as the codec DAI name */ 227 /* ASoC core can match codec with device node */
361 ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE); 228 mdata->dai[0].codec_of_node = codec_np;
362 if (ret) {
363 dev_err(&pdev->dev, "invalid codec node %s\n",
364 codec_np->full_name);
365 ret = -EINVAL;
366 goto error;
367 }
368 mdata->dai[0].codec_name = mdata->codec_name;
369 229
370 /* We register two DAIs per SSI, one for playback and the other for 230 /* 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 231 * capture. We support codecs that have separate DAIs for both playback
@@ -462,9 +322,9 @@ static int p1022_ds_probe(struct platform_device *pdev)
462 322
463 /* Find the playback DMA channel to use. */ 323 /* Find the playback DMA channel to use. */
464 mdata->dai[0].platform_name = mdata->platform_name[0]; 324 mdata->dai[0].platform_name = mdata->platform_name[0];
465 ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], 325 ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
466 &mdata->dma_channel_id[0], 326 &mdata->dma_channel_id[0],
467 &mdata->dma_id[0]); 327 &mdata->dma_id[0]);
468 if (ret) { 328 if (ret) {
469 dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); 329 dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
470 goto error; 330 goto error;
@@ -472,9 +332,9 @@ static int p1022_ds_probe(struct platform_device *pdev)
472 332
473 /* Find the capture DMA channel to use. */ 333 /* Find the capture DMA channel to use. */
474 mdata->dai[1].platform_name = mdata->platform_name[1]; 334 mdata->dai[1].platform_name = mdata->platform_name[1];
475 ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], 335 ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
476 &mdata->dma_channel_id[1], 336 &mdata->dma_channel_id[1],
477 &mdata->dma_id[1]); 337 &mdata->dma_id[1]);
478 if (ret) { 338 if (ret) {
479 dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); 339 dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
480 goto error; 340 goto error;
diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c
new file mode 100644
index 000000000000..f8da6dd115ed
--- /dev/null
+++ b/sound/soc/fsl/phycore-ac97.c
@@ -0,0 +1,125 @@
1/*
2 * phycore-ac97.c -- SoC audio for imx_phycore in AC97 mode
3 *
4 * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/device.h>
16#include <linux/i2c.h>
17#include <sound/core.h>
18#include <sound/pcm.h>
19#include <sound/soc.h>
20#include <asm/mach-types.h>
21
22#include "imx-audmux.h"
23
24static struct snd_soc_card imx_phycore;
25
26static struct snd_soc_ops imx_phycore_hifi_ops = {
27};
28
29static struct snd_soc_dai_link imx_phycore_dai_ac97[] = {
30 {
31 .name = "HiFi",
32 .stream_name = "HiFi",
33 .codec_dai_name = "wm9712-hifi",
34 .codec_name = "wm9712-codec",
35 .cpu_dai_name = "imx-ssi.0",
36 .platform_name = "imx-fiq-pcm-audio.0",
37 .ops = &imx_phycore_hifi_ops,
38 },
39};
40
41static struct snd_soc_card imx_phycore = {
42 .name = "PhyCORE-ac97-audio",
43 .owner = THIS_MODULE,
44 .dai_link = imx_phycore_dai_ac97,
45 .num_links = ARRAY_SIZE(imx_phycore_dai_ac97),
46};
47
48static struct platform_device *imx_phycore_snd_ac97_device;
49static struct platform_device *imx_phycore_snd_device;
50
51static int __init imx_phycore_init(void)
52{
53 int ret;
54
55 if (machine_is_pca100()) {
56 imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
57 IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */
58 IMX_AUDMUX_V1_PCR_TFCSEL(3) |
59 IMX_AUDMUX_V1_PCR_TCLKDIR | /* clock is output */
60 IMX_AUDMUX_V1_PCR_RXDSEL(3));
61 imx_audmux_v1_configure_port(3,
62 IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */
63 IMX_AUDMUX_V1_PCR_TFCSEL(0) |
64 IMX_AUDMUX_V1_PCR_TFSDIR |
65 IMX_AUDMUX_V1_PCR_RXDSEL(0));
66 } else if (machine_is_pcm043()) {
67 imx_audmux_v2_configure_port(3,
68 IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */
69 IMX_AUDMUX_V2_PTCR_TFSEL(0) |
70 IMX_AUDMUX_V2_PTCR_TFSDIR,
71 IMX_AUDMUX_V2_PDCR_RXDSEL(0));
72 imx_audmux_v2_configure_port(0,
73 IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */
74 IMX_AUDMUX_V2_PTCR_TCSEL(3) |
75 IMX_AUDMUX_V2_PTCR_TCLKDIR, /* clock is output */
76 IMX_AUDMUX_V2_PDCR_RXDSEL(3));
77 } else {
78 /* return happy. We might run on a totally different machine */
79 return 0;
80 }
81
82 imx_phycore_snd_ac97_device = platform_device_alloc("soc-audio", -1);
83 if (!imx_phycore_snd_ac97_device)
84 return -ENOMEM;
85
86 platform_set_drvdata(imx_phycore_snd_ac97_device, &imx_phycore);
87 ret = platform_device_add(imx_phycore_snd_ac97_device);
88 if (ret)
89 goto fail1;
90
91 imx_phycore_snd_device = platform_device_alloc("wm9712-codec", -1);
92 if (!imx_phycore_snd_device) {
93 ret = -ENOMEM;
94 goto fail2;
95 }
96 ret = platform_device_add(imx_phycore_snd_device);
97
98 if (ret) {
99 printk(KERN_ERR "ASoC: Platform device allocation failed\n");
100 goto fail3;
101 }
102
103 return 0;
104
105fail3:
106 platform_device_put(imx_phycore_snd_device);
107fail2:
108 platform_device_del(imx_phycore_snd_ac97_device);
109fail1:
110 platform_device_put(imx_phycore_snd_ac97_device);
111 return ret;
112}
113
114static void __exit imx_phycore_exit(void)
115{
116 platform_device_unregister(imx_phycore_snd_device);
117 platform_device_unregister(imx_phycore_snd_ac97_device);
118}
119
120late_initcall(imx_phycore_init);
121module_exit(imx_phycore_exit);
122
123MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
124MODULE_DESCRIPTION("PhyCORE ALSA SoC driver");
125MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
new file mode 100644
index 000000000000..fe54a69073e5
--- /dev/null
+++ b/sound/soc/fsl/wm1133-ev1.c
@@ -0,0 +1,304 @@
1/*
2 * wm1133-ev1.c - Audio for WM1133-EV1 on i.MX31ADS
3 *
4 * Copyright (c) 2010 Wolfson Microelectronics plc
5 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
6 *
7 * Based on an earlier driver for the same hardware by Liam Girdwood.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 */
14
15#include <linux/platform_device.h>
16#include <linux/clk.h>
17#include <linux/module.h>
18#include <sound/core.h>
19#include <sound/jack.h>
20#include <sound/pcm.h>
21#include <sound/pcm_params.h>
22#include <sound/soc.h>
23
24#include "imx-ssi.h"
25#include "../codecs/wm8350.h"
26#include "imx-audmux.h"
27
28/* There is a silicon mic on the board optionally connected via a solder pad
29 * SP1. Define this to enable it.
30 */
31#undef USE_SIMIC
32
33struct _wm8350_audio {
34 unsigned int channels;
35 snd_pcm_format_t format;
36 unsigned int rate;
37 unsigned int sysclk;
38 unsigned int bclkdiv;
39 unsigned int clkdiv;
40 unsigned int lr_rate;
41};
42
43/* in order of power consumption per rate (lowest first) */
44static const struct _wm8350_audio wm8350_audio[] = {
45 /* 16bit mono modes */
46 {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000 >> 1,
47 WM8350_BCLK_DIV_48, WM8350_DACDIV_3, 16,},
48
49 /* 16 bit stereo modes */
50 {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
51 WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
52 {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
53 WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,},
54 {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
55 WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,},
56 {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
57 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
58 {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
59 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
60 {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
61 WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
62 {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
63 WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,},
64 {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
65 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
66 {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
67 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
68
69 /* 24bit stereo modes */
70 {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000,
71 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
72 {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000,
73 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
74 {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600,
75 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
76 {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200,
77 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
78};
79
80static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream,
81 struct snd_pcm_hw_params *params)
82{
83 struct snd_soc_pcm_runtime *rtd = substream->private_data;
84 struct snd_soc_dai *codec_dai = rtd->codec_dai;
85 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
86 int i, found = 0;
87 snd_pcm_format_t format = params_format(params);
88 unsigned int rate = params_rate(params);
89 unsigned int channels = params_channels(params);
90 u32 dai_format;
91
92 /* find the correct audio parameters */
93 for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) {
94 if (rate == wm8350_audio[i].rate &&
95 format == wm8350_audio[i].format &&
96 channels == wm8350_audio[i].channels) {
97 found = 1;
98 break;
99 }
100 }
101 if (!found)
102 return -EINVAL;
103
104 /* codec FLL input is 14.75 MHz from MCLK */
105 snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk);
106
107 dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
108 SND_SOC_DAIFMT_CBM_CFM;
109
110 /* set codec DAI configuration */
111 snd_soc_dai_set_fmt(codec_dai, dai_format);
112
113 /* set cpu DAI configuration */
114 snd_soc_dai_set_fmt(cpu_dai, dai_format);
115
116 /* TODO: The SSI driver should figure this out for us */
117 switch (channels) {
118 case 2:
119 snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
120 break;
121 case 1:
122 snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffe, 0xffffffe, 1, 0);
123 break;
124 default:
125 return -EINVAL;
126 }
127
128 /* set MCLK as the codec system clock for DAC and ADC */
129 snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_MCLK,
130 wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
131
132 /* set codec BCLK division for sample rate */
133 snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV,
134 wm8350_audio[i].bclkdiv);
135
136 /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */
137 snd_soc_dai_set_clkdiv(codec_dai,
138 WM8350_DACLR_CLKDIV, wm8350_audio[i].lr_rate);
139 snd_soc_dai_set_clkdiv(codec_dai,
140 WM8350_ADCLR_CLKDIV, wm8350_audio[i].lr_rate);
141
142 /* now configure DAC and ADC clocks */
143 snd_soc_dai_set_clkdiv(codec_dai,
144 WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv);
145
146 snd_soc_dai_set_clkdiv(codec_dai,
147 WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv);
148
149 return 0;
150}
151
152static struct snd_soc_ops wm1133_ev1_ops = {
153 .hw_params = wm1133_ev1_hw_params,
154};
155
156static const struct snd_soc_dapm_widget wm1133_ev1_widgets[] = {
157#ifdef USE_SIMIC
158 SND_SOC_DAPM_MIC("SiMIC", NULL),
159#endif
160 SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
161 SND_SOC_DAPM_MIC("Mic2 Jack", NULL),
162 SND_SOC_DAPM_LINE("Line In Jack", NULL),
163 SND_SOC_DAPM_LINE("Line Out Jack", NULL),
164 SND_SOC_DAPM_HP("Headphone Jack", NULL),
165};
166
167/* imx32ads soc_card audio map */
168static const struct snd_soc_dapm_route wm1133_ev1_map[] = {
169
170#ifdef USE_SIMIC
171 /* SiMIC --> IN1LN (with automatic bias) via SP1 */
172 { "IN1LN", NULL, "Mic Bias" },
173 { "Mic Bias", NULL, "SiMIC" },
174#endif
175
176 /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */
177 { "IN1LN", NULL, "Mic Bias" },
178 { "IN1LP", NULL, "Mic1 Jack" },
179 { "Mic Bias", NULL, "Mic1 Jack" },
180
181 /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */
182 { "IN1RN", NULL, "Mic Bias" },
183 { "IN1RP", NULL, "Mic2 Jack" },
184 { "Mic Bias", NULL, "Mic2 Jack" },
185
186 /* Line in Jack --> AUX (L+R) */
187 { "IN3R", NULL, "Line In Jack" },
188 { "IN3L", NULL, "Line In Jack" },
189
190 /* Out1 --> Headphone Jack */
191 { "Headphone Jack", NULL, "OUT1R" },
192 { "Headphone Jack", NULL, "OUT1L" },
193
194 /* Out1 --> Line Out Jack */
195 { "Line Out Jack", NULL, "OUT2R" },
196 { "Line Out Jack", NULL, "OUT2L" },
197};
198
199static struct snd_soc_jack hp_jack;
200
201static struct snd_soc_jack_pin hp_jack_pins[] = {
202 { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE },
203};
204
205static struct snd_soc_jack mic_jack;
206
207static struct snd_soc_jack_pin mic_jack_pins[] = {
208 { .pin = "Mic1 Jack", .mask = SND_JACK_MICROPHONE },
209 { .pin = "Mic2 Jack", .mask = SND_JACK_MICROPHONE },
210};
211
212static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd)
213{
214 struct snd_soc_codec *codec = rtd->codec;
215 struct snd_soc_dapm_context *dapm = &codec->dapm;
216
217 snd_soc_dapm_new_controls(dapm, wm1133_ev1_widgets,
218 ARRAY_SIZE(wm1133_ev1_widgets));
219
220 snd_soc_dapm_add_routes(dapm, wm1133_ev1_map,
221 ARRAY_SIZE(wm1133_ev1_map));
222
223 /* Headphone jack detection */
224 snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack);
225 snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
226 hp_jack_pins);
227 wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
228
229 /* Microphone jack detection */
230 snd_soc_jack_new(codec, "Microphone",
231 SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack);
232 snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins),
233 mic_jack_pins);
234 wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE,
235 SND_JACK_BTN_0);
236
237 snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
238
239 return 0;
240}
241
242
243static struct snd_soc_dai_link wm1133_ev1_dai = {
244 .name = "WM1133-EV1",
245 .stream_name = "Audio",
246 .cpu_dai_name = "imx-ssi.0",
247 .codec_dai_name = "wm8350-hifi",
248 .platform_name = "imx-fiq-pcm-audio.0",
249 .codec_name = "wm8350-codec.0-0x1a",
250 .init = wm1133_ev1_init,
251 .ops = &wm1133_ev1_ops,
252 .symmetric_rates = 1,
253};
254
255static struct snd_soc_card wm1133_ev1 = {
256 .name = "WM1133-EV1",
257 .owner = THIS_MODULE,
258 .dai_link = &wm1133_ev1_dai,
259 .num_links = 1,
260};
261
262static struct platform_device *wm1133_ev1_snd_device;
263
264static int __init wm1133_ev1_audio_init(void)
265{
266 int ret;
267 unsigned int ptcr, pdcr;
268
269 /* SSI0 mastered by port 5 */
270 ptcr = IMX_AUDMUX_V2_PTCR_SYN |
271 IMX_AUDMUX_V2_PTCR_TFSDIR |
272 IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) |
273 IMX_AUDMUX_V2_PTCR_TCLKDIR |
274 IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
275 pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
276 imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr);
277
278 ptcr = IMX_AUDMUX_V2_PTCR_SYN;
279 pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0);
280 imx_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr);
281
282 wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1);
283 if (!wm1133_ev1_snd_device)
284 return -ENOMEM;
285
286 platform_set_drvdata(wm1133_ev1_snd_device, &wm1133_ev1);
287 ret = platform_device_add(wm1133_ev1_snd_device);
288
289 if (ret)
290 platform_device_put(wm1133_ev1_snd_device);
291
292 return ret;
293}
294module_init(wm1133_ev1_audio_init);
295
296static void __exit wm1133_ev1_audio_exit(void)
297{
298 platform_device_unregister(wm1133_ev1_snd_device);
299}
300module_exit(wm1133_ev1_audio_exit);
301
302MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
303MODULE_DESCRIPTION("Audio for WM1133-EV1 on i.MX31ADS");
304MODULE_LICENSE("GPL");