blob: 2de70f44c8f830a45cf0d7647b24f2f890887822 [file] [log] [blame]
Asish Bhattacharya656b7a22012-03-15 00:27:42 -07001/* Copyright (c) 2012, 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#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/module.h>
16#include <linux/err.h>
17#include <linux/platform_device.h>
18#include <linux/slab.h>
19#include <linux/stddef.h>
20#include <linux/debugfs.h>
21#include <linux/mfd/pm8xxx/core.h>
22#include <linux/mfd/pm8xxx/spk.h>
23
24#define PM8XXX_SPK_CTL1_REG_OFF 0
25#define PM8XXX_SPK_TEST_REG_1_OFF 1
26#define PM8XXX_SPK_TEST_REG_2_OFF 2
27
28#define PM8XXX_SPK_BANK_SEL 4
29#define PM8XXX_SPK_BANK_WRITE 0x80
30#define PM8XXX_SPK_BANK_VAL_MASK 0xF
31
32#define BOOST_6DB_GAIN_EN_MASK 0x8
33#define VSEL_LD0_1P1 0x0
34#define VSEL_LD0_1P2 0x2
35#define VSEL_LD0_1P0 0x4
36
37#define PWM_EN_MASK 0xF
38#define PM8XXX_SPK_TEST_REG_1_BANKS 8
39#define PM8XXX_SPK_TEST_REG_2_BANKS 2
40
41#define PM8XXX_SPK_GAIN 0x5
42#define PM8XXX_ADD_EN 0x1
43
44struct pm8xxx_spk_chip {
45 struct list_head link;
46 struct pm8xxx_spk_platform_data pdata;
47 struct device *dev;
48 enum pm8xxx_version version;
49 struct mutex spk_mutex;
50 u16 base;
51 u16 end;
52};
53
54static struct pm8xxx_spk_chip *the_spk_chip;
55
56static inline bool spk_defined(void)
57{
58 if (the_spk_chip == NULL || IS_ERR(the_spk_chip))
59 return false;
60 return true;
61}
62
63static int pm8xxx_spk_bank_write(u16 reg, u16 bank, u8 val)
64{
65 int rc = 0;
66 u8 bank_val = PM8XXX_SPK_BANK_WRITE | (bank << PM8XXX_SPK_BANK_SEL);
67
68 bank_val |= (val & PM8XXX_SPK_BANK_VAL_MASK);
69 mutex_lock(&the_spk_chip->spk_mutex);
70 rc = pm8xxx_writeb(the_spk_chip->dev->parent, reg, bank_val);
71 if (rc)
72 pr_err("pm8xxx_writeb(): rc=%d\n", rc);
73 mutex_unlock(&the_spk_chip->spk_mutex);
74 return rc;
75}
76
77
78static int pm8xxx_spk_read(u16 addr)
79{
80 int rc = 0;
81 u8 val = 0;
82
83 mutex_lock(&the_spk_chip->spk_mutex);
84 rc = pm8xxx_readb(the_spk_chip->dev->parent,
85 the_spk_chip->base + addr, &val);
86 if (rc) {
87 pr_err("pm8xxx_spk_readb() failed: rc=%d\n", rc);
88 val = rc;
89 }
90 mutex_unlock(&the_spk_chip->spk_mutex);
91
92 return val;
93}
94
95static int pm8xxx_spk_write(u16 addr, u8 val)
96{
97 int rc = 0;
98
99 mutex_lock(&the_spk_chip->spk_mutex);
100 rc = pm8xxx_writeb(the_spk_chip->dev->parent,
101 the_spk_chip->base + addr, val);
102 if (rc)
103 pr_err("pm8xxx_writeb() failed: rc=%d\n", rc);
104 mutex_unlock(&the_spk_chip->spk_mutex);
105 return rc;
106}
107
108int pm8xxx_spk_mute(bool mute)
109{
110 u8 val = 0;
111 int ret = 0;
112 if (spk_defined() == false) {
113 pr_err("Invalid spk handle or no spk_chip\n");
114 return -ENODEV;
115 }
116
117 val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
118 if (val < 0)
119 return val;
120 val |= mute << 2;
121 ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
122 return ret;
123}
124EXPORT_SYMBOL_GPL(pm8xxx_spk_mute);
125
126int pm8xxx_spk_gain(u8 gain)
127{
128 u8 val;
129 int ret = 0;
130
131 if (spk_defined() == false) {
132 pr_err("Invalid spk handle or no spk_chip\n");
133 return -ENODEV;
134 }
135
136 val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
137 if (val < 0)
138 return val;
139 val |= (gain << 4);
140 ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
141 if (!ret) {
142 pm8xxx_spk_bank_write(the_spk_chip->base
143 + PM8XXX_SPK_TEST_REG_1_OFF,
144 0, BOOST_6DB_GAIN_EN_MASK | VSEL_LD0_1P2);
145 }
146 return ret;
147}
148EXPORT_SYMBOL_GPL(pm8xxx_spk_gain);
149
150int pm8xxx_spk_enable(int enable)
151{
152 int val = 0;
153 u16 addr;
154 int ret = 0;
155
156 if (spk_defined() == false) {
157 pr_err("Invalid spk handle or no spk_chip\n");
158 return -ENODEV;
159 }
160
161 addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
162 val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
163 if (val < 0)
164 return val;
Asish Bhattacharya7225d642012-07-20 19:56:37 +0530165 if (enable)
166 val |= (1 << 3);
167 else
168 val &= ~(1 << 3);
Asish Bhattacharya656b7a22012-03-15 00:27:42 -0700169 ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
170 if (!ret)
171 ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK);
172 return ret;
173}
174EXPORT_SYMBOL_GPL(pm8xxx_spk_enable);
175
176static int pm8xxx_spk_config(void)
177{
178 u16 addr;
179 int ret = 0;
180
181 if (spk_defined() == false) {
182 pr_err("Invalid spk handle or no spk_chip\n");
183 return -ENODEV;
184 }
185
186 addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
187 ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK & 0);
188 if (!ret)
189 ret = pm8xxx_spk_gain(PM8XXX_SPK_GAIN);
190 return ret;
191}
192
193static int __devinit pm8xxx_spk_probe(struct platform_device *pdev)
194{
195 const struct pm8xxx_spk_platform_data *pdata = pdev->dev.platform_data;
196 int ret = 0;
197
198 if (!pdata) {
199 pr_err("missing platform data\n");
200 return -EINVAL;
201 }
202
203 the_spk_chip = kzalloc(sizeof(struct pm8xxx_spk_chip), GFP_KERNEL);
204 if (the_spk_chip == NULL) {
205 pr_err("kzalloc() failed.\n");
206 return -ENOMEM;
207 }
208
209 mutex_init(&the_spk_chip->spk_mutex);
210
211 the_spk_chip->dev = &pdev->dev;
212 the_spk_chip->version = pm8xxx_get_version(the_spk_chip->dev->parent);
213 switch (pm8xxx_get_version(the_spk_chip->dev->parent)) {
214 case PM8XXX_VERSION_8038:
215 break;
216 default:
217 ret = -ENODEV;
218 goto err_handle;
219 }
220
221 memcpy(&(the_spk_chip->pdata), pdata,
222 sizeof(struct pm8xxx_spk_platform_data));
223
224 the_spk_chip->base = pdev->resource[0].start;
225 the_spk_chip->end = pdev->resource[0].end;
226
227 if (the_spk_chip->pdata.spk_add_enable) {
228 int val;
229 val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
230 if (val < 0) {
231 ret = val;
232 goto err_handle;
233 }
234 val |= (the_spk_chip->pdata.spk_add_enable & PM8XXX_ADD_EN);
235 ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
236 if (ret < 0)
237 goto err_handle;
238 }
239 return pm8xxx_spk_config();
240err_handle:
241 pr_err("pm8xxx_spk_probe failed."
242 "Audio unavailable on speaker.\n");
243 mutex_destroy(&the_spk_chip->spk_mutex);
244 kfree(the_spk_chip);
245 return ret;
246}
247
248static int __devexit pm8xxx_spk_remove(struct platform_device *pdev)
249{
250 if (spk_defined() == false) {
251 pr_err("Invalid spk handle or no spk_chip\n");
252 return -ENODEV;
253 }
254 mutex_destroy(&the_spk_chip->spk_mutex);
255 kfree(the_spk_chip);
256 return 0;
257}
258
259static struct platform_driver pm8xxx_spk_driver = {
260 .probe = pm8xxx_spk_probe,
261 .remove = __devexit_p(pm8xxx_spk_remove),
262 .driver = {
263 .name = PM8XXX_SPK_DEV_NAME,
264 .owner = THIS_MODULE,
265 },
266};
267
268static int __init pm8xxx_spk_init(void)
269{
270 return platform_driver_register(&pm8xxx_spk_driver);
271}
272subsys_initcall(pm8xxx_spk_init);
273
274static void __exit pm8xxx_spk_exit(void)
275{
276 platform_driver_unregister(&pm8xxx_spk_driver);
277}
278module_exit(pm8xxx_spk_exit);
279
280MODULE_LICENSE("GPL v2");
281MODULE_DESCRIPTION("PM8XXX SPK driver");
282MODULE_ALIAS("platform:" PM8XXX_SPK_DEV_NAME);