blob: 804fa14a9fc9af8b87478febc0d9f0cdf0c681c3 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/clk.h>
16#include <linux/err.h>
17#include <linux/slab.h>
18#include <linux/gpio.h>
19#include <linux/delay.h>
20#include <linux/io.h>
21#include <asm/uaccess.h>
22#include <asm/io.h>
23#include <mach/clk.h>
24#include <mach/qdsp6v2/audio_dev_ctl.h>
25#include <sound/apr_audio.h>
26#include <sound/q6afe.h>
27#include "snddev_ecodec.h"
28
29#define ECODEC_SAMPLE_RATE 8000
30
31/* Context for each external codec device */
32struct snddev_ecodec_state {
33 struct snddev_ecodec_data *data;
34 u32 sample_rate;
35};
36
37/* Global state for the driver */
38struct snddev_ecodec_drv_state {
39 struct mutex dev_lock;
40 int ref_cnt; /* ensure one rx device at a time */
41 struct clk *ecodec_clk;
42};
43
44static struct snddev_ecodec_drv_state snddev_ecodec_drv;
45
46struct aux_pcm_state {
47 unsigned int dout;
48 unsigned int din;
49 unsigned int syncout;
50 unsigned int clkin_a;
51};
52
53static struct aux_pcm_state the_aux_pcm_state;
54
55static int aux_pcm_gpios_request(void)
56{
57 int rc = 0;
58
59 pr_debug("%s\n", __func__);
60 rc = gpio_request(the_aux_pcm_state.dout, "AUX PCM DOUT");
61 if (rc < 0) {
62 pr_err("%s: GPIO request for AUX PCM DOUT failed\n", __func__);
63 return rc;
64 }
65
66 rc = gpio_request(the_aux_pcm_state.din, "AUX PCM DIN");
67 if (rc < 0) {
68 pr_err("%s: GPIO request for AUX PCM DIN failed\n", __func__);
69 gpio_free(the_aux_pcm_state.dout);
70 return rc;
71 }
72
73 rc = gpio_request(the_aux_pcm_state.syncout, "AUX PCM SYNC OUT");
74 if (rc < 0) {
75 pr_err("%s: GPIO request for AUX PCM SYNC OUT failed\n",
76 __func__);
77 gpio_free(the_aux_pcm_state.dout);
78 gpio_free(the_aux_pcm_state.din);
79 return rc;
80 }
81
82 rc = gpio_request(the_aux_pcm_state.clkin_a, "AUX PCM CLKIN A");
83 if (rc < 0) {
84 pr_err("%s: GPIO request for AUX PCM CLKIN A failed\n",
85 __func__);
86 gpio_free(the_aux_pcm_state.dout);
87 gpio_free(the_aux_pcm_state.din);
88 gpio_free(the_aux_pcm_state.syncout);
89 return rc;
90 }
91
92 return rc;
93}
94
95static void aux_pcm_gpios_free(void)
96{
97 pr_debug("%s\n", __func__);
98 gpio_free(the_aux_pcm_state.dout);
99 gpio_free(the_aux_pcm_state.din);
100 gpio_free(the_aux_pcm_state.syncout);
101 gpio_free(the_aux_pcm_state.clkin_a);
102}
103
104static int get_aux_pcm_gpios(struct platform_device *pdev)
105{
106 int rc = 0;
107 struct resource *res;
108
109 /* Claim all of the GPIOs. */
110 res = platform_get_resource_byname(pdev, IORESOURCE_IO, "aux_pcm_dout");
111 if (!res) {
112 pr_err("%s: failed to get gpio AUX PCM DOUT\n", __func__);
113 return -ENODEV;
114 }
115
116 the_aux_pcm_state.dout = res->start;
117
118 res = platform_get_resource_byname(pdev, IORESOURCE_IO, "aux_pcm_din");
119 if (!res) {
120 pr_err("%s: failed to get gpio AUX PCM DIN\n", __func__);
121 return -ENODEV;
122 }
123
124 the_aux_pcm_state.din = res->start;
125
126 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
127 "aux_pcm_syncout");
128 if (!res) {
129 pr_err("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__);
130 return -ENODEV;
131 }
132
133 the_aux_pcm_state.syncout = res->start;
134
135 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
136 "aux_pcm_clkin_a");
137 if (!res) {
138 pr_err("%s: failed to get gpio AUX PCM CLKIN A\n", __func__);
139 return -ENODEV;
140 }
141
142 the_aux_pcm_state.clkin_a = res->start;
143
144 return rc;
145}
146
147static int aux_pcm_probe(struct platform_device *pdev)
148{
149 int rc = 0;
150
151 rc = get_aux_pcm_gpios(pdev);
152 if (rc < 0) {
153 pr_err("%s: GPIO configuration failed\n", __func__);
154 return -ENODEV;
155 }
156 return rc;
157}
158
159static struct platform_driver aux_pcm_driver = {
160 .probe = aux_pcm_probe,
161 .driver = { .name = "msm_aux_pcm"}
162};
163
164static int snddev_ecodec_open(struct msm_snddev_info *dev_info)
165{
166 int rc;
167 struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
168 union afe_port_config afe_config;
169
170 pr_debug("%s\n", __func__);
171
172 mutex_lock(&drv->dev_lock);
173
174 if (dev_info->opened) {
175 pr_err("%s: ERROR: %s already opened\n", __func__,
176 dev_info->name);
177 mutex_unlock(&drv->dev_lock);
178 return -EBUSY;
179 }
180
181 if (drv->ref_cnt != 0) {
182 pr_debug("%s: opened %s\n", __func__, dev_info->name);
183 drv->ref_cnt++;
184 mutex_unlock(&drv->dev_lock);
185 return 0;
186 }
187
188 pr_info("%s: opening %s\n", __func__, dev_info->name);
189
190 rc = aux_pcm_gpios_request();
191 if (rc < 0) {
192 pr_err("%s: GPIO request failed\n", __func__);
193 return rc;
194 }
195
196 clk_reset(drv->ecodec_clk, CLK_RESET_ASSERT);
197
198 afe_config.pcm.mode = AFE_PCM_CFG_MODE_PCM;
199 afe_config.pcm.sync = AFE_PCM_CFG_SYNC_INT;
200 afe_config.pcm.frame = AFE_PCM_CFG_FRM_256BPF;
201 afe_config.pcm.quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD;
202 afe_config.pcm.slot = 0;
203 afe_config.pcm.data = AFE_PCM_CFG_CDATAOE_MASTER;
204
205 rc = afe_open(PCM_RX, &afe_config, ECODEC_SAMPLE_RATE);
206 if (rc < 0) {
207 pr_err("%s: afe open failed for PCM_RX\n", __func__);
208 goto err_rx_afe;
209 }
210
211 rc = afe_open(PCM_TX, &afe_config, ECODEC_SAMPLE_RATE);
212 if (rc < 0) {
213 pr_err("%s: afe open failed for PCM_TX\n", __func__);
214 goto err_tx_afe;
215 }
216
217 rc = clk_set_rate(drv->ecodec_clk, 2048000);
218 if (rc < 0) {
219 pr_err("%s: clk_set_rate failed\n", __func__);
220 goto err_clk;
221 }
222
Karthik Reddy Katta1944dc32012-06-05 14:35:28 +0530223 clk_prepare_enable(drv->ecodec_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224
225 clk_reset(drv->ecodec_clk, CLK_RESET_DEASSERT);
226
227 drv->ref_cnt++;
228 mutex_unlock(&drv->dev_lock);
229
230 return 0;
231
232err_clk:
233 afe_close(PCM_TX);
234err_tx_afe:
235 afe_close(PCM_RX);
236err_rx_afe:
237 aux_pcm_gpios_free();
238 mutex_unlock(&drv->dev_lock);
239 return -ENODEV;
240}
241
242int snddev_ecodec_close(struct msm_snddev_info *dev_info)
243{
244 struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
245
246 pr_debug("%s: closing %s\n", __func__, dev_info->name);
247
248 mutex_lock(&drv->dev_lock);
249
250 if (!dev_info->opened) {
251 pr_err("%s: ERROR: %s is not opened\n", __func__,
252 dev_info->name);
253 mutex_unlock(&drv->dev_lock);
254 return -EPERM;
255 }
256
257 drv->ref_cnt--;
258
259 if (drv->ref_cnt == 0) {
260
261 pr_info("%s: closing all devices\n", __func__);
262
Karthik Reddy Katta1944dc32012-06-05 14:35:28 +0530263 clk_disable_unprepare(drv->ecodec_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264 aux_pcm_gpios_free();
265
266 afe_close(PCM_RX);
267 afe_close(PCM_TX);
268 }
269
270 mutex_unlock(&drv->dev_lock);
271
272 return 0;
273}
274
275int snddev_ecodec_set_freq(struct msm_snddev_info *dev_info, u32 rate)
276{
277 int rc = 0;
278
279 if (!dev_info) {
280 rc = -EINVAL;
281 goto error;
282 }
283 return ECODEC_SAMPLE_RATE;
284
285error:
286 return rc;
287}
288
289static int snddev_ecodec_probe(struct platform_device *pdev)
290{
291 int rc = 0;
292 struct snddev_ecodec_data *pdata;
293 struct msm_snddev_info *dev_info;
294 struct snddev_ecodec_state *ecodec;
295
296 if (!pdev || !pdev->dev.platform_data) {
297 printk(KERN_ALERT "Invalid caller\n");
298 rc = -1;
299 goto error;
300 }
301 pdata = pdev->dev.platform_data;
302
303 ecodec = kzalloc(sizeof(struct snddev_ecodec_state), GFP_KERNEL);
304 if (!ecodec) {
305 rc = -ENOMEM;
306 goto error;
307 }
308
309 dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
310 if (!dev_info) {
311 kfree(ecodec);
312 rc = -ENOMEM;
313 goto error;
314 }
315
316 dev_info->name = pdata->name;
317 dev_info->copp_id = pdata->copp_id;
318 dev_info->private_data = (void *)ecodec;
319 dev_info->dev_ops.open = snddev_ecodec_open;
320 dev_info->dev_ops.close = snddev_ecodec_close;
321 dev_info->dev_ops.set_freq = snddev_ecodec_set_freq;
322 dev_info->dev_ops.enable_sidetone = NULL;
323 dev_info->capability = pdata->capability;
324 dev_info->opened = 0;
325
326 msm_snddev_register(dev_info);
327
328 ecodec->data = pdata;
329 ecodec->sample_rate = ECODEC_SAMPLE_RATE; /* Default to 8KHz */
330error:
331 return rc;
332}
333
334struct platform_driver snddev_ecodec_driver = {
335 .probe = snddev_ecodec_probe,
336 .driver = {.name = "msm_snddev_ecodec"}
337};
338
339int __init snddev_ecodec_init(void)
340{
341 int rc = 0;
342 struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
343
344 mutex_init(&drv->dev_lock);
345 drv->ref_cnt = 0;
346
Satish Babu Patakokilaebbd1e22012-05-01 13:51:02 +0530347 drv->ecodec_clk = clk_get_sys(NULL, "pcm_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 if (IS_ERR(drv->ecodec_clk)) {
349 pr_err("%s: could not get pcm_clk\n", __func__);
350 return PTR_ERR(drv->ecodec_clk);
351 }
352
353 rc = platform_driver_register(&aux_pcm_driver);
354 if (IS_ERR_VALUE(rc)) {
355 pr_err("%s: platform_driver_register for aux pcm failed\n",
356 __func__);
357 goto error_aux_pcm_platform_driver;
358 }
359
360 rc = platform_driver_register(&snddev_ecodec_driver);
361 if (IS_ERR_VALUE(rc)) {
362 pr_err("%s: platform_driver_register for ecodec failed\n",
363 __func__);
364 goto error_ecodec_platform_driver;
365 }
366
367 return 0;
368
369error_ecodec_platform_driver:
370 platform_driver_unregister(&aux_pcm_driver);
371error_aux_pcm_platform_driver:
372 clk_put(drv->ecodec_clk);
373
374 pr_err("%s: encounter error\n", __func__);
375 return -ENODEV;
376}
377
378device_initcall(snddev_ecodec_init);
379
380MODULE_DESCRIPTION("ECodec Sound Device driver");
381MODULE_VERSION("1.0");
382MODULE_LICENSE("GPL v2");