blob: 297ddfa474aeba62b4b6788543d7c48cb490c337 [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;
165 val |= (enable << 3);
166 ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
167 if (!ret)
168 ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK);
169 return ret;
170}
171EXPORT_SYMBOL_GPL(pm8xxx_spk_enable);
172
173static int pm8xxx_spk_config(void)
174{
175 u16 addr;
176 int ret = 0;
177
178 if (spk_defined() == false) {
179 pr_err("Invalid spk handle or no spk_chip\n");
180 return -ENODEV;
181 }
182
183 addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
184 ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK & 0);
185 if (!ret)
186 ret = pm8xxx_spk_gain(PM8XXX_SPK_GAIN);
187 return ret;
188}
189
190static int __devinit pm8xxx_spk_probe(struct platform_device *pdev)
191{
192 const struct pm8xxx_spk_platform_data *pdata = pdev->dev.platform_data;
193 int ret = 0;
194
195 if (!pdata) {
196 pr_err("missing platform data\n");
197 return -EINVAL;
198 }
199
200 the_spk_chip = kzalloc(sizeof(struct pm8xxx_spk_chip), GFP_KERNEL);
201 if (the_spk_chip == NULL) {
202 pr_err("kzalloc() failed.\n");
203 return -ENOMEM;
204 }
205
206 mutex_init(&the_spk_chip->spk_mutex);
207
208 the_spk_chip->dev = &pdev->dev;
209 the_spk_chip->version = pm8xxx_get_version(the_spk_chip->dev->parent);
210 switch (pm8xxx_get_version(the_spk_chip->dev->parent)) {
211 case PM8XXX_VERSION_8038:
212 break;
213 default:
214 ret = -ENODEV;
215 goto err_handle;
216 }
217
218 memcpy(&(the_spk_chip->pdata), pdata,
219 sizeof(struct pm8xxx_spk_platform_data));
220
221 the_spk_chip->base = pdev->resource[0].start;
222 the_spk_chip->end = pdev->resource[0].end;
223
224 if (the_spk_chip->pdata.spk_add_enable) {
225 int val;
226 val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
227 if (val < 0) {
228 ret = val;
229 goto err_handle;
230 }
231 val |= (the_spk_chip->pdata.spk_add_enable & PM8XXX_ADD_EN);
232 ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
233 if (ret < 0)
234 goto err_handle;
235 }
236 return pm8xxx_spk_config();
237err_handle:
238 pr_err("pm8xxx_spk_probe failed."
239 "Audio unavailable on speaker.\n");
240 mutex_destroy(&the_spk_chip->spk_mutex);
241 kfree(the_spk_chip);
242 return ret;
243}
244
245static int __devexit pm8xxx_spk_remove(struct platform_device *pdev)
246{
247 if (spk_defined() == false) {
248 pr_err("Invalid spk handle or no spk_chip\n");
249 return -ENODEV;
250 }
251 mutex_destroy(&the_spk_chip->spk_mutex);
252 kfree(the_spk_chip);
253 return 0;
254}
255
256static struct platform_driver pm8xxx_spk_driver = {
257 .probe = pm8xxx_spk_probe,
258 .remove = __devexit_p(pm8xxx_spk_remove),
259 .driver = {
260 .name = PM8XXX_SPK_DEV_NAME,
261 .owner = THIS_MODULE,
262 },
263};
264
265static int __init pm8xxx_spk_init(void)
266{
267 return platform_driver_register(&pm8xxx_spk_driver);
268}
269subsys_initcall(pm8xxx_spk_init);
270
271static void __exit pm8xxx_spk_exit(void)
272{
273 platform_driver_unregister(&pm8xxx_spk_driver);
274}
275module_exit(pm8xxx_spk_exit);
276
277MODULE_LICENSE("GPL v2");
278MODULE_DESCRIPTION("PM8XXX SPK driver");
279MODULE_ALIAS("platform:" PM8XXX_SPK_DEV_NAME);