diff options
Diffstat (limited to 'sound/soc/atmel/sam9g20_wm8731.c')
-rw-r--r-- | sound/soc/atmel/sam9g20_wm8731.c | 138 |
1 files changed, 25 insertions, 113 deletions
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 173a239a541c..130b12118d4f 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c | |||
@@ -56,30 +56,14 @@ | |||
56 | 56 | ||
57 | #define MCLK_RATE 12000000 | 57 | #define MCLK_RATE 12000000 |
58 | 58 | ||
59 | static struct clk *mclk; | 59 | /* |
60 | 60 | * As shipped the board does not have inputs. However, it is relatively | |
61 | static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) | 61 | * straightforward to modify the board to hook them up so support is left |
62 | { | 62 | * in the driver. |
63 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | 63 | */ |
64 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | 64 | #undef ENABLE_MIC_INPUT |
65 | int ret; | ||
66 | |||
67 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | ||
68 | MCLK_RATE, SND_SOC_CLOCK_IN); | ||
69 | if (ret < 0) { | ||
70 | clk_disable(mclk); | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream) | ||
78 | { | ||
79 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | ||
80 | 65 | ||
81 | dev_dbg(rtd->socdev->dev, "shutdown"); | 66 | static struct clk *mclk; |
82 | } | ||
83 | 67 | ||
84 | static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, | 68 | static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, |
85 | struct snd_pcm_hw_params *params) | 69 | struct snd_pcm_hw_params *params) |
@@ -87,102 +71,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, | |||
87 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 71 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
88 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | 72 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; |
89 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 73 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
90 | struct atmel_ssc_info *ssc_p = cpu_dai->private_data; | ||
91 | struct ssc_device *ssc = ssc_p->ssc; | ||
92 | int ret; | 74 | int ret; |
93 | 75 | ||
94 | unsigned int rate; | ||
95 | int cmr_div, period; | ||
96 | |||
97 | if (ssc == NULL) { | ||
98 | printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n"); | ||
99 | return -EINVAL; | ||
100 | } | ||
101 | |||
102 | /* set codec DAI configuration */ | 76 | /* set codec DAI configuration */ |
103 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | 77 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | |
104 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | 78 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
105 | if (ret < 0) | 79 | if (ret < 0) |
106 | return ret; | 80 | return ret; |
107 | 81 | ||
108 | /* set cpu DAI configuration */ | 82 | /* set cpu DAI configuration */ |
109 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | 83 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | |
110 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | 84 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
111 | if (ret < 0) | ||
112 | return ret; | ||
113 | |||
114 | /* | ||
115 | * The SSC clock dividers depend on the sample rate. The CMR.DIV | ||
116 | * field divides the system master clock MCK to drive the SSC TK | ||
117 | * signal which provides the codec BCLK. The TCMR.PERIOD and | ||
118 | * RCMR.PERIOD fields further divide the BCLK signal to drive | ||
119 | * the SSC TF and RF signals which provide the codec DACLRC and | ||
120 | * ADCLRC clocks. | ||
121 | * | ||
122 | * The dividers were determined through trial and error, where a | ||
123 | * CMR.DIV value is chosen such that the resulting BCLK value is | ||
124 | * divisible, or almost divisible, by (2 * sample rate), and then | ||
125 | * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. | ||
126 | */ | ||
127 | rate = params_rate(params); | ||
128 | |||
129 | switch (rate) { | ||
130 | case 8000: | ||
131 | cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */ | ||
132 | period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */ | ||
133 | break; | ||
134 | case 11025: | ||
135 | cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */ | ||
136 | period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */ | ||
137 | break; | ||
138 | case 16000: | ||
139 | cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */ | ||
140 | period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */ | ||
141 | break; | ||
142 | case 22050: | ||
143 | cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */ | ||
144 | period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */ | ||
145 | break; | ||
146 | case 32000: | ||
147 | cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */ | ||
148 | period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */ | ||
149 | break; | ||
150 | case 44100: | ||
151 | cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */ | ||
152 | period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */ | ||
153 | break; | ||
154 | case 48000: | ||
155 | cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */ | ||
156 | period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */ | ||
157 | break; | ||
158 | case 88200: | ||
159 | cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */ | ||
160 | period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */ | ||
161 | break; | ||
162 | case 96000: | ||
163 | cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */ | ||
164 | period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */ | ||
165 | break; | ||
166 | default: | ||
167 | printk(KERN_WARNING "unsupported rate %d" | ||
168 | " on at91sam9g20ek board\n", rate); | ||
169 | return -EINVAL; | ||
170 | } | ||
171 | |||
172 | /* set the MCK divider for BCLK */ | ||
173 | ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div); | ||
174 | if (ret < 0) | ||
175 | return ret; | ||
176 | |||
177 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
178 | /* set the BCLK divider for DACLRC */ | ||
179 | ret = snd_soc_dai_set_clkdiv(cpu_dai, | ||
180 | ATMEL_SSC_TCMR_PERIOD, period); | ||
181 | } else { | ||
182 | /* set the BCLK divider for ADCLRC */ | ||
183 | ret = snd_soc_dai_set_clkdiv(cpu_dai, | ||
184 | ATMEL_SSC_RCMR_PERIOD, period); | ||
185 | } | ||
186 | if (ret < 0) | 85 | if (ret < 0) |
187 | return ret; | 86 | return ret; |
188 | 87 | ||
@@ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, | |||
190 | } | 89 | } |
191 | 90 | ||
192 | static struct snd_soc_ops at91sam9g20ek_ops = { | 91 | static struct snd_soc_ops at91sam9g20ek_ops = { |
193 | .startup = at91sam9g20ek_startup, | ||
194 | .hw_params = at91sam9g20ek_hw_params, | 92 | .hw_params = at91sam9g20ek_hw_params, |
195 | .shutdown = at91sam9g20ek_shutdown, | ||
196 | }; | 93 | }; |
197 | 94 | ||
198 | static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, | 95 | static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, |
@@ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
241 | */ | 138 | */ |
242 | static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) | 139 | static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) |
243 | { | 140 | { |
141 | struct snd_soc_dai *codec_dai = &codec->dai[0]; | ||
142 | int ret; | ||
143 | |||
244 | printk(KERN_DEBUG | 144 | printk(KERN_DEBUG |
245 | "at91sam9g20ek_wm8731 " | 145 | "at91sam9g20ek_wm8731 " |
246 | ": at91sam9g20ek_wm8731_init() called\n"); | 146 | ": at91sam9g20ek_wm8731_init() called\n"); |
247 | 147 | ||
148 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | ||
149 | MCLK_RATE, SND_SOC_CLOCK_IN); | ||
150 | if (ret < 0) { | ||
151 | printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); | ||
152 | return ret; | ||
153 | } | ||
154 | |||
248 | /* Add specific widgets */ | 155 | /* Add specific widgets */ |
249 | snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets, | 156 | snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets, |
250 | ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); | 157 | ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); |
@@ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) | |||
255 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); | 162 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); |
256 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); | 163 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); |
257 | 164 | ||
258 | /* always connected */ | 165 | #ifdef ENABLE_MIC_INPUT |
259 | snd_soc_dapm_enable_pin(codec, "Int Mic"); | 166 | snd_soc_dapm_enable_pin(codec, "Int Mic"); |
167 | #else | ||
168 | snd_soc_dapm_nc_pin(codec, "Int Mic"); | ||
169 | #endif | ||
170 | |||
171 | /* always connected */ | ||
260 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | 172 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); |
261 | 173 | ||
262 | snd_soc_dapm_sync(codec); | 174 | snd_soc_dapm_sync(codec); |