blob: 8469507c8e30b2173e29c2cba9cdc4328619fdc8 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13
14#include <linux/clk.h>
15#include <linux/err.h>
16#include <linux/gpio.h>
17#include <linux/mfd/pmic8058.h>
18#include <linux/mfd/pmic8901.h>
19#include <linux/platform_device.h>
20#include <mach/board.h>
21#include <mach/mpp.h>
22#include <sound/core.h>
23#include <sound/soc.h>
24#include <sound/soc-dapm.h>
25#include <sound/pcm.h>
26#include <sound/dai.h>
27#include "msm8660-pcm.h"
28#include "../codecs/timpani.h"
29
30#define PM8058_GPIO_BASE NR_MSM_GPIOS
31#define PM8901_GPIO_BASE (PM8058_GPIO_BASE + \
32 PM8058_GPIOS + PM8058_MPPS)
33#define PM8901_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio + PM8901_GPIO_BASE)
34#define GPIO_EXPANDER_GPIO_BASE \
35 (PM8901_GPIO_BASE + PM8901_MPPS)
36
37static struct clk *rx_osr_clk;
38static struct clk *rx_bit_clk;
39static struct clk *tx_osr_clk;
40static struct clk *tx_bit_clk;
41
42static int rx_hw_param_status;
43static int tx_hw_param_status;
44/* Platform specific logic */
45
46static int timpani_rx_route_enable(void)
47{
48 int ret = 0;
49 pr_debug("%s\n", __func__);
50 ret = gpio_request(109, "I2S_Clock");
51 if (ret != 0) {
52 pr_err("%s: I2s clk gpio 109 request"
53 "failed\n", __func__);
54 return ret;
55 }
56 return ret;
57}
58
59static int timpani_rx_route_disable(void)
60{
61 int ret = 0;
62 pr_debug("%s\n", __func__);
63 gpio_free(109);
64 return ret;
65}
66
67
68#define GPIO_CLASS_D1_EN (GPIO_EXPANDER_GPIO_BASE + 0)
69#define PM8901_MPP_3 (2) /* PM8901 MPP starts from 0 */
70static void config_class_d1_gpio(int enable)
71{
72 int rc;
73
74 if (enable) {
75 rc = gpio_request(GPIO_CLASS_D1_EN, "CLASSD1_EN");
76 if (rc) {
77 pr_err("%s: spkr pamp gpio %d request"
78 "failed\n", __func__, GPIO_CLASS_D1_EN);
79 return;
80 }
81 gpio_direction_output(GPIO_CLASS_D1_EN, 1);
82 gpio_set_value_cansleep(GPIO_CLASS_D1_EN, 1);
83 } else {
84 gpio_set_value_cansleep(GPIO_CLASS_D1_EN, 0);
85 gpio_free(GPIO_CLASS_D1_EN);
86 }
87}
88
89static void config_class_d0_gpio(int enable)
90{
91 int rc;
92
93 if (enable) {
94 rc = pm8901_mpp_config_digital_out(PM8901_MPP_3,
95 PM8901_MPP_DIG_LEVEL_MSMIO, 1);
96
97 if (rc) {
98 pr_err("%s: CLASS_D0_EN failed\n", __func__);
99 return;
100 }
101
102 rc = gpio_request(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3),
103 "CLASSD0_EN");
104
105 if (rc) {
106 pr_err("%s: spkr pamp gpio pm8901 mpp3 request"
107 "failed\n", __func__);
108 pm8901_mpp_config_digital_out(PM8901_MPP_3,
109 PM8901_MPP_DIG_LEVEL_MSMIO, 0);
110 return;
111 }
112
113 gpio_direction_output(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 1);
114 gpio_set_value_cansleep(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 1);
115
116 } else {
117 pm8901_mpp_config_digital_out(PM8901_MPP_3,
118 PM8901_MPP_DIG_LEVEL_MSMIO, 0);
119 gpio_set_value_cansleep(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 0);
120 gpio_free(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3));
121 }
122}
123
124static void timpani_poweramp_on(void)
125{
126
127 pr_debug("%s: enable stereo spkr amp\n", __func__);
128 timpani_rx_route_enable();
129 config_class_d0_gpio(1);
130 config_class_d1_gpio(1);
131}
132
133static void timpani_poweramp_off(void)
134{
135
136 pr_debug("%s: disable stereo spkr amp\n", __func__);
137 timpani_rx_route_disable();
138 config_class_d0_gpio(0);
139 config_class_d1_gpio(0);
140}
141
142static int msm8660_hw_params(struct snd_pcm_substream *substream,
143 struct snd_pcm_hw_params *params)
144{
145 int rate = params_rate(params);
146
147 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
148 if (rx_hw_param_status)
149 return 0;
150 clk_set_rate(rx_osr_clk, rate * 256);
151 rx_hw_param_status++;
152 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
153 if (tx_hw_param_status)
154 return 0;
155 clk_set_rate(tx_osr_clk, rate * 256);
156 tx_hw_param_status++;
157 }
158 return 0;
159}
160
161static int msm8660_startup(struct snd_pcm_substream *substream)
162{
163 int ret = 0;
164 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
165 rx_osr_clk = clk_get(NULL, "i2s_spkr_osr_clk");
166 if (IS_ERR(rx_osr_clk)) {
167 pr_debug("Failed to get i2s_spkr_osr_clk\n");
168 return PTR_ERR(rx_osr_clk);
169 }
170 /* Master clock OSR 256 */
171 /* Initially set to Lowest sample rate Needed */
172 clk_set_rate(rx_osr_clk, 8000 * 256);
173 ret = clk_enable(rx_osr_clk);
174 if (ret != 0) {
175 pr_debug("Unable to enable i2s_spkr_osr_clk\n");
176 clk_put(rx_osr_clk);
177 return ret;
178 }
179 rx_bit_clk = clk_get(NULL, "i2s_spkr_bit_clk");
180 if (IS_ERR(rx_bit_clk)) {
181 pr_debug("Failed to get i2s_spkr_bit_clk\n");
182 clk_disable(rx_osr_clk);
183 clk_put(rx_osr_clk);
184 return PTR_ERR(rx_bit_clk);
185 }
186 clk_set_rate(rx_bit_clk, 8);
187 ret = clk_enable(rx_bit_clk);
188 if (ret != 0) {
189 pr_debug("Unable to enable i2s_spkr_bit_clk\n");
190 clk_put(rx_bit_clk);
191 clk_disable(rx_osr_clk);
192 clk_put(rx_osr_clk);
193 return ret;
194 }
195 timpani_poweramp_on();
196 msleep(30);
197 /* End of platform specific logic */
198 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
199 tx_osr_clk = clk_get(NULL, "i2s_mic_osr_clk");
200 if (IS_ERR(tx_osr_clk)) {
201 pr_debug("Failed to get i2s_mic_osr_clk\n");
202 return PTR_ERR(tx_osr_clk);
203 }
204 /* Master clock OSR 256 */
205 clk_set_rate(tx_osr_clk, 8000 * 256);
206 ret = clk_enable(tx_osr_clk);
207 if (ret != 0) {
208 pr_debug("Unable to enable i2s_mic_osr_clk\n");
209 clk_put(tx_osr_clk);
210 return ret;
211 }
212 tx_bit_clk = clk_get(NULL, "i2s_mic_bit_clk");
213 if (IS_ERR(tx_bit_clk)) {
214 pr_debug("Failed to get i2s_mic_bit_clk\n");
215 clk_disable(tx_osr_clk);
216 clk_put(tx_osr_clk);
217 return PTR_ERR(tx_bit_clk);
218 }
219 clk_set_rate(tx_bit_clk, 8);
220 ret = clk_enable(tx_bit_clk);
221 if (ret != 0) {
222 pr_debug("Unable to enable i2s_mic_bit_clk\n");
223 clk_put(tx_bit_clk);
224 clk_disable(tx_osr_clk);
225 clk_put(tx_osr_clk);
226 return ret;
227 }
228 msm_snddev_enable_dmic_power();
229 msleep(30);
230 }
231 return ret;
232}
233
234/*
235 * TODO: rx/tx_hw_param_status should be a counter in the below code
236 * when driver starts supporting mutisession else setting it to 0
237 * will stop audio in all sessions.
238 */
239static void msm8660_shutdown(struct snd_pcm_substream *substream)
240{
241 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
242 rx_hw_param_status = 0;
243 timpani_poweramp_off();
244 msleep(30);
245 if (rx_bit_clk) {
246 clk_disable(rx_bit_clk);
247 clk_put(rx_bit_clk);
248 rx_bit_clk = NULL;
249 }
250 if (rx_osr_clk) {
251 clk_disable(rx_osr_clk);
252 clk_put(rx_osr_clk);
253 rx_osr_clk = NULL;
254 }
255 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
256 tx_hw_param_status = 0;
257 msm_snddev_disable_dmic_power();
258 msleep(30);
259 if (tx_bit_clk) {
260 clk_disable(tx_bit_clk);
261 clk_put(tx_bit_clk);
262 tx_bit_clk = NULL;
263 }
264 if (tx_osr_clk) {
265 clk_disable(tx_osr_clk);
266 clk_put(tx_osr_clk);
267 tx_osr_clk = NULL;
268 }
269 }
270}
271
272static struct snd_soc_ops machine_ops = {
273 .startup = msm8660_startup,
274 .shutdown = msm8660_shutdown,
275 .hw_params = msm8660_hw_params,
276};
277
278/* Digital audio interface glue - connects codec <---> CPU */
279static struct snd_soc_dai_link msm8660_dai[] = {
280 {
281 .name = "Audio Rx",
282 .stream_name = "Audio Rx",
283 .cpu_dai = &msm_cpu_dai[0],
284 .codec_dai = &timpani_codec_dai[0],
285 .ops = &machine_ops,
286 },
287 {
288 .name = "Audio Tx",
289 .stream_name = "Audio Tx",
290 .cpu_dai = &msm_cpu_dai[5],
291 .codec_dai = &timpani_codec_dai[1],
292 .ops = &machine_ops,
293 }
294};
295
296struct snd_soc_card snd_soc_card_msm8660 = {
297 .name = "msm8660-pcm-audio",
298 .dai_link = msm8660_dai,
299 .num_links = ARRAY_SIZE(msm8660_dai),
300 .platform = &msm8660_soc_platform,
301};
302
303/* msm_audio audio subsystem */
304static struct snd_soc_device msm_snd_devdata = {
305 .card = &snd_soc_card_msm8660,
306 .codec_dev = &soc_codec_dev_timpani,
307};
308
309static struct platform_device *msm_snd_device;
310
311
312static int __init msm_audio_init(void)
313{
314 int ret;
315
316 msm_snd_device = platform_device_alloc("soc-audio", 0);
317 if (!msm_snd_device) {
318 pr_err("Platform device allocation failed\n");
319 return -ENOMEM;
320 }
321
322 platform_set_drvdata(msm_snd_device, &msm_snd_devdata);
323
324 msm_snd_devdata.dev = &msm_snd_device->dev;
325 ret = platform_device_add(msm_snd_device);
326 if (ret) {
327 platform_device_put(msm_snd_device);
328 return ret;
329 }
330
331 return ret;
332}
333module_init(msm_audio_init);
334
335static void __exit msm_audio_exit(void)
336{
337 platform_device_unregister(msm_snd_device);
338}
339module_exit(msm_audio_exit);
340
341MODULE_DESCRIPTION("ALSA SoC MSM8660");
342MODULE_LICENSE("GPL v2");