blob: 06318bc7f78aff4cb8ca2e5cab53ebbb074844bc [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2009-2011, 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/mutex.h>
15#include <linux/platform_device.h>
16#include <linux/io.h>
17#include <linux/err.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070018#include <linux/gpio.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019#include <mach/qdsp5v2/aux_pcm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/delay.h>
21#include <mach/debug_mm.h>
22
23/*----------------------------------------------------------------------------
24 * Preprocessor Definitions and Constants
25 * -------------------------------------------------------------------------*/
26
27/* define offset of registers here, may put them into platform data */
28#define AUX_CODEC_CTL_OFFSET 0x00
29#define PCM_PATH_CTL_OFFSET 0x04
30#define AUX_CODEC_CTL_OUT_OFFSET 0x08
31
32/* define some bit values in PCM_PATH_CTL register */
33#define PCM_PATH_CTL__ADSP_CTL_EN_BMSK 0x8
34
35/* mask and shift */
36#define AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK 0x800
37#define AUX_CODEC_CTL_PCM_SYNC_LONG_BMSK 0x400
38#define AUX_CODEC_CTL_PCM_SYNC_SHORT_BMSK 0x200
39#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_SRC_BMSK 0x80
40#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_MODE_BMSK 0x40
41#define AUX_CODEC_CTL_I2S_RX_MODE_BMSK 0x20
42#define AUX_CODEC_CTL_I2S_CLK_MODE_BMSK 0x10
43#define AUX_CODEC_CTL_AUX_PCM_MODE_BMSK 0x0b
44#define AUX_CODEC_CTL_AUX_CODEC_MODE_BMSK 0x02
45
46/* AUX PCM MODE */
47#define MASTER_PRIM_PCM_SHORT 0
48#define MASTER_AUX_PCM_LONG 1
49#define SLAVE_PRIM_PCM_SHORT 2
50
51struct aux_pcm_state {
52 void __iomem *aux_pcm_base; /* configure aux pcm through Scorpion */
53 int dout;
54 int din;
55 int syncout;
56 int clkin_a;
57};
58
59static struct aux_pcm_state the_aux_pcm_state;
60
61static void __iomem *get_base_addr(struct aux_pcm_state *aux_pcm)
62{
63 return aux_pcm->aux_pcm_base;
64}
65
66/* Set who control aux pcm : adsp or MSM */
67void aux_codec_adsp_codec_ctl_en(bool msm_adsp_en)
68{
69 void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
70 uint32_t val;
71
72 if (!IS_ERR(baddr)) {
73 val = readl(baddr + AUX_CODEC_CTL_OFFSET);
74 if (msm_adsp_en) { /* adsp */
75 writel(
76 ((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
77 AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__ADSP_V),
78 baddr + AUX_CODEC_CTL_OFFSET);
79 } else { /* MSM */
80 writel(
81 ((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
82 AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__MSM_V),
83 baddr + AUX_CODEC_CTL_OFFSET);
84 }
85 }
86 mb();
87}
88
89/* Set who control aux pcm path: adsp or MSM */
90void aux_codec_pcm_path_ctl_en(bool msm_adsp_en)
91{
92 void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
93 uint32_t val;
94
95 if (!IS_ERR(baddr)) {
96 val = readl(baddr + PCM_PATH_CTL_OFFSET);
97 if (msm_adsp_en) { /* adsp */
98 writel(
99 ((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
100 PCM_PATH_CTL__ADSP_CTL_EN__ADSP_V),
101 baddr + PCM_PATH_CTL_OFFSET);
102 } else { /* MSM */
103 writel(
104 ((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
105 PCM_PATH_CTL__ADSP_CTL_EN__MSM_V),
106 baddr + PCM_PATH_CTL_OFFSET);
107 }
108 }
109 mb();
110 return;
111}
112EXPORT_SYMBOL(aux_codec_pcm_path_ctl_en);
113
114int aux_pcm_gpios_request(void)
115{
116 int rc = 0;
117
118 MM_DBG("aux_pcm_gpios_request\n");
119 rc = gpio_request(the_aux_pcm_state.dout, "AUX PCM DOUT");
120 if (rc) {
121 MM_ERR("GPIO request for AUX PCM DOUT failed\n");
122 return rc;
123 }
124
125 rc = gpio_request(the_aux_pcm_state.din, "AUX PCM DIN");
126 if (rc) {
127 MM_ERR("GPIO request for AUX PCM DIN failed\n");
128 gpio_free(the_aux_pcm_state.dout);
129 return rc;
130 }
131
132 rc = gpio_request(the_aux_pcm_state.syncout, "AUX PCM SYNC OUT");
133 if (rc) {
134 MM_ERR("GPIO request for AUX PCM SYNC OUT failed\n");
135 gpio_free(the_aux_pcm_state.dout);
136 gpio_free(the_aux_pcm_state.din);
137 return rc;
138 }
139
140 rc = gpio_request(the_aux_pcm_state.clkin_a, "AUX PCM CLKIN A");
141 if (rc) {
142 MM_ERR("GPIO request for AUX PCM CLKIN A failed\n");
143 gpio_free(the_aux_pcm_state.dout);
144 gpio_free(the_aux_pcm_state.din);
145 gpio_free(the_aux_pcm_state.syncout);
146 return rc;
147 }
148
149 return rc;
150}
151EXPORT_SYMBOL(aux_pcm_gpios_request);
152
153
154void aux_pcm_gpios_free(void)
155{
156 MM_DBG(" aux_pcm_gpios_free \n");
157
158 /*
159 * Feed silence frames before close to prevent buzzing sound in BT at
160 * call end. This fix is applicable only to Marimba BT.
161 */
162 gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 0, GPIO_CFG_OUTPUT,
163 GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
164 gpio_set_value(the_aux_pcm_state.dout, 0);
165 msleep(20);
166 gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 1, GPIO_CFG_OUTPUT,
167 GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
168
169 gpio_free(the_aux_pcm_state.dout);
170 gpio_free(the_aux_pcm_state.din);
171 gpio_free(the_aux_pcm_state.syncout);
172 gpio_free(the_aux_pcm_state.clkin_a);
173}
174EXPORT_SYMBOL(aux_pcm_gpios_free);
175
176
177static int get_aux_pcm_gpios(struct platform_device *pdev)
178{
179 int rc = 0;
180 struct resource *res;
181
182 /* Claim all of the GPIOs. */
183 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
184 "aux_pcm_dout");
185 if (!res) {
186 MM_ERR("%s: failed to get gpio AUX PCM DOUT\n", __func__);
187 return -ENODEV;
188 }
189
190 the_aux_pcm_state.dout = res->start;
191
192 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
193 "aux_pcm_din");
194 if (!res) {
195 MM_ERR("%s: failed to get gpio AUX PCM DIN\n", __func__);
196 return -ENODEV;
197 }
198
199 the_aux_pcm_state.din = res->start;
200
201 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
202 "aux_pcm_syncout");
203 if (!res) {
204 MM_ERR("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__);
205 return -ENODEV;
206 }
207
208 the_aux_pcm_state.syncout = res->start;
209
210 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
211 "aux_pcm_clkin_a");
212 if (!res) {
213 MM_ERR("%s: failed to get gpio AUX PCM CLKIN A\n", __func__);
214 return -ENODEV;
215 }
216
217 the_aux_pcm_state.clkin_a = res->start;
218
219 return rc;
220}
221static int aux_pcm_probe(struct platform_device *pdev)
222{
223 int rc = 0;
224 struct resource *mem_src;
225
226 MM_DBG("aux_pcm_probe \n");
227 mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM,
228 "aux_codec_reg_addr");
229 if (!mem_src) {
230 rc = -ENODEV;
231 goto done;
232 }
233
234 the_aux_pcm_state.aux_pcm_base = ioremap(mem_src->start,
235 (mem_src->end - mem_src->start) + 1);
236 if (!the_aux_pcm_state.aux_pcm_base) {
237 rc = -ENOMEM;
238 goto done;
239 }
240 rc = get_aux_pcm_gpios(pdev);
241 if (rc) {
242 MM_ERR("GPIO configuration failed\n");
243 rc = -ENODEV;
244 }
245
246done: return rc;
247
248}
249
250static int aux_pcm_remove(struct platform_device *pdev)
251{
252 iounmap(the_aux_pcm_state.aux_pcm_base);
253 return 0;
254}
255
256static struct platform_driver aux_pcm_driver = {
257 .probe = aux_pcm_probe,
258 .remove = aux_pcm_remove,
259 .driver = {
260 .name = "msm_aux_pcm",
261 .owner = THIS_MODULE,
262 },
263};
264
265static int __init aux_pcm_init(void)
266{
267
268 return platform_driver_register(&aux_pcm_driver);
269}
270
271static void __exit aux_pcm_exit(void)
272{
273 platform_driver_unregister(&aux_pcm_driver);
274}
275
276module_init(aux_pcm_init);
277module_exit(aux_pcm_exit);
278
279MODULE_DESCRIPTION("MSM AUX PCM driver");
280MODULE_LICENSE("GPL v2");