diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/at91/eti_b1_wm8731.c | 141 |
1 files changed, 124 insertions, 17 deletions
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index 089cdc9e7265..8179df3bb2f3 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c | |||
@@ -18,9 +18,6 @@ | |||
18 | * Free Software Foundation; either version 2 of the License, or (at your | 18 | * Free Software Foundation; either version 2 of the License, or (at your |
19 | * option) any later version. | 19 | * option) any later version. |
20 | * | 20 | * |
21 | * Revision history | ||
22 | * 30th Nov 2005 Initial version. | ||
23 | * | ||
24 | */ | 21 | */ |
25 | 22 | ||
26 | #include <linux/module.h> | 23 | #include <linux/module.h> |
@@ -43,9 +40,10 @@ | |||
43 | 40 | ||
44 | #include "../codecs/wm8731.h" | 41 | #include "../codecs/wm8731.h" |
45 | #include "at91-pcm.h" | 42 | #include "at91-pcm.h" |
43 | #include "at91-i2s.h" | ||
46 | 44 | ||
47 | #if 0 | 45 | #if 0 |
48 | #define DBG(x...) printk(KERN_INFO "eti_b1_wm8731:" x) | 46 | #define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x) |
49 | #else | 47 | #else |
50 | #define DBG(x...) | 48 | #define DBG(x...) |
51 | #endif | 49 | #endif |
@@ -57,12 +55,29 @@ | |||
57 | #define AT91_PIO_RK1 (1 << (AT91_PIN_PB10 - PIN_BASE) % 32) | 55 | #define AT91_PIO_RK1 (1 << (AT91_PIN_PB10 - PIN_BASE) % 32) |
58 | #define AT91_PIO_RF1 (1 << (AT91_PIN_PB11 - PIN_BASE) % 32) | 56 | #define AT91_PIO_RF1 (1 << (AT91_PIN_PB11 - PIN_BASE) % 32) |
59 | 57 | ||
60 | |||
61 | static struct clk *pck1_clk; | 58 | static struct clk *pck1_clk; |
62 | static struct clk *pllb_clk; | 59 | static struct clk *pllb_clk; |
63 | 60 | ||
61 | |||
64 | static int eti_b1_startup(struct snd_pcm_substream *substream) | 62 | static int eti_b1_startup(struct snd_pcm_substream *substream) |
65 | { | 63 | { |
64 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
65 | struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
66 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
67 | int ret; | ||
68 | |||
69 | /* cpu clock is the AT91 master clock sent to the SSC */ | ||
70 | ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, AT91_SYSCLK_MCK, | ||
71 | 60000000, SND_SOC_CLOCK_IN); | ||
72 | if (ret < 0) | ||
73 | return ret; | ||
74 | |||
75 | /* codec system clock is supplied by PCK1, set to 12MHz */ | ||
76 | ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, | ||
77 | 12000000, SND_SOC_CLOCK_IN); | ||
78 | if (ret < 0) | ||
79 | return ret; | ||
80 | |||
66 | /* Start PCK1 clock. */ | 81 | /* Start PCK1 clock. */ |
67 | clk_enable(pck1_clk); | 82 | clk_enable(pck1_clk); |
68 | DBG("pck1 started\n"); | 83 | DBG("pck1 started\n"); |
@@ -77,8 +92,105 @@ static void eti_b1_shutdown(struct snd_pcm_substream *substream) | |||
77 | DBG("pck1 stopped\n"); | 92 | DBG("pck1 stopped\n"); |
78 | } | 93 | } |
79 | 94 | ||
95 | static int eti_b1_hw_params(struct snd_pcm_substream *substream, | ||
96 | struct snd_pcm_hw_params *params) | ||
97 | { | ||
98 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
99 | struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
100 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
101 | int ret; | ||
102 | |||
103 | #ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE | ||
104 | unsigned int rate; | ||
105 | int cmr_div, period; | ||
106 | |||
107 | /* set codec DAI configuration */ | ||
108 | ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
109 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
110 | if (ret < 0) | ||
111 | return ret; | ||
112 | |||
113 | /* set cpu DAI configuration */ | ||
114 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
115 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
116 | if (ret < 0) | ||
117 | return ret; | ||
118 | |||
119 | /* | ||
120 | * The SSC clock dividers depend on the sample rate. The CMR.DIV | ||
121 | * field divides the system master clock MCK to drive the SSC TK | ||
122 | * signal which provides the codec BCLK. The TCMR.PERIOD and | ||
123 | * RCMR.PERIOD fields further divide the BCLK signal to drive | ||
124 | * the SSC TF and RF signals which provide the codec DACLRC and | ||
125 | * ADCLRC clocks. | ||
126 | * | ||
127 | * The dividers were determined through trial and error, where a | ||
128 | * CMR.DIV value is chosen such that the resulting BCLK value is | ||
129 | * divisible, or almost divisible, by (2 * sample rate), and then | ||
130 | * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. | ||
131 | */ | ||
132 | rate = params_rate(params); | ||
133 | |||
134 | switch (rate) { | ||
135 | case 8000: | ||
136 | cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */ | ||
137 | period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */ | ||
138 | break; | ||
139 | case 32000: | ||
140 | cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */ | ||
141 | period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */ | ||
142 | break; | ||
143 | case 48000: | ||
144 | cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */ | ||
145 | period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */ | ||
146 | break; | ||
147 | default: | ||
148 | printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate); | ||
149 | return -EINVAL; | ||
150 | } | ||
151 | |||
152 | /* set the MCK divider for BCLK */ | ||
153 | ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div); | ||
154 | if (ret < 0) | ||
155 | return ret; | ||
156 | |||
157 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
158 | /* set the BCLK divider for DACLRC */ | ||
159 | ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, | ||
160 | AT91SSC_TCMR_PERIOD, period); | ||
161 | } else { | ||
162 | /* set the BCLK divider for ADCLRC */ | ||
163 | ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, | ||
164 | AT91SSC_RCMR_PERIOD, period); | ||
165 | } | ||
166 | if (ret < 0) | ||
167 | return ret; | ||
168 | |||
169 | #else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ | ||
170 | /* | ||
171 | * Codec in Master Mode. | ||
172 | */ | ||
173 | |||
174 | /* set codec DAI configuration */ | ||
175 | ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
176 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
177 | if (ret < 0) | ||
178 | return ret; | ||
179 | |||
180 | /* set cpu DAI configuration */ | ||
181 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
182 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
183 | if (ret < 0) | ||
184 | return ret; | ||
185 | |||
186 | #endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
80 | static struct snd_soc_ops eti_b1_ops = { | 191 | static struct snd_soc_ops eti_b1_ops = { |
81 | .startup = eti_b1_startup, | 192 | .startup = eti_b1_startup, |
193 | .hw_params = eti_b1_hw_params, | ||
82 | .shutdown = eti_b1_shutdown, | 194 | .shutdown = eti_b1_shutdown, |
83 | }; | 195 | }; |
84 | 196 | ||
@@ -134,29 +246,19 @@ static int eti_b1_wm8731_init(struct snd_soc_codec *codec) | |||
134 | return 0; | 246 | return 0; |
135 | } | 247 | } |
136 | 248 | ||
137 | unsigned int eti_b1_config_sysclk(struct snd_soc_pcm_runtime *rtd, | ||
138 | struct snd_soc_clock_info *info) | ||
139 | { | ||
140 | if(info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { | ||
141 | return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 12000000); | ||
142 | } | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static struct snd_soc_dai_link eti_b1_dai = { | 249 | static struct snd_soc_dai_link eti_b1_dai = { |
147 | .name = "WM8731", | 250 | .name = "WM8731", |
148 | .stream_name = "WM8731", | 251 | .stream_name = "WM8731", |
149 | .cpu_dai = &at91_i2s_dai[1], | 252 | .cpu_dai = &at91_i2s_dai[1], |
150 | .codec_dai = &wm8731_dai, | 253 | .codec_dai = &wm8731_dai, |
151 | .init = eti_b1_wm8731_init, | 254 | .init = eti_b1_wm8731_init, |
152 | .config_sysclk = eti_b1_config_sysclk, | 255 | .ops = &eti_b1_ops, |
153 | }; | 256 | }; |
154 | 257 | ||
155 | static struct snd_soc_machine snd_soc_machine_eti_b1 = { | 258 | static struct snd_soc_machine snd_soc_machine_eti_b1 = { |
156 | .name = "ETI_B1", | 259 | .name = "ETI_B1", |
157 | .dai_link = &eti_b1_dai, | 260 | .dai_link = &eti_b1_dai, |
158 | .num_links = 1, | 261 | .num_links = 1, |
159 | .ops = &eti_b1_ops, | ||
160 | }; | 262 | }; |
161 | 263 | ||
162 | static struct wm8731_setup_data eti_b1_wm8731_setup = { | 264 | static struct wm8731_setup_data eti_b1_wm8731_setup = { |
@@ -210,7 +312,7 @@ static int __init eti_b1_init(void) | |||
210 | } | 312 | } |
211 | 313 | ||
212 | ssc_pio_lines = AT91_PIO_TF1 | AT91_PIO_TK1 | AT91_PIO_TD1 | 314 | ssc_pio_lines = AT91_PIO_TF1 | AT91_PIO_TK1 | AT91_PIO_TD1 |
213 | | AT91_PIO_RD1 /* | AT91_PIO_RK1 | AT91_PIO_RF1 */; | 315 | | AT91_PIO_RD1 /* | AT91_PIO_RK1 */ | AT91_PIO_RF1; |
214 | 316 | ||
215 | /* Reset all PIO registers and assign lines to peripheral A */ | 317 | /* Reset all PIO registers and assign lines to peripheral A */ |
216 | at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines); | 318 | at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines); |
@@ -237,6 +339,11 @@ static int __init eti_b1_init(void) | |||
237 | /* assign the GPIO pin to PCK1 */ | 339 | /* assign the GPIO pin to PCK1 */ |
238 | at91_set_B_periph(AT91_PIN_PA24, 0); | 340 | at91_set_B_periph(AT91_PIN_PA24, 0); |
239 | 341 | ||
342 | #ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE | ||
343 | printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n"); | ||
344 | #else | ||
345 | printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n"); | ||
346 | #endif | ||
240 | return ret; | 347 | return ret; |
241 | 348 | ||
242 | fail_io_unmap: | 349 | fail_io_unmap: |