blob: 78919b9b1954325c581ef9f95cf757e1de50bdfe [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* drivers/i2c/chips/tpa2018d1.c
2 *
3 * TI TPA2018D1 Speaker Amplifier
4 *
5 * Copyright (C) 2009 HTC Corporation
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18/* TODO: content validation in TPA2018_SET_CONFIG */
19
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/device.h>
23#include <linux/fs.h>
24#include <linux/i2c.h>
25#include <linux/miscdevice.h>
26#include <linux/gpio.h>
27#include <asm/uaccess.h>
28#include <linux/delay.h>
29#include <linux/mutex.h>
30#include <linux/slab.h>
31#include <linux/tpa2018d1.h>
32
33#include "board-mahimahi-tpa2018d1.h"
34
35static struct i2c_client *this_client;
36static struct tpa2018d1_platform_data *pdata;
37static int is_on;
38static char spk_amp_cfg[8];
39static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */
40 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21
41};
42static const char spk_amp_off[] = {0x01, 0xa2};
43
44static DEFINE_MUTEX(spk_amp_lock);
45static int tpa2018d1_opened;
46static char *config_data;
47static int tpa2018d1_num_modes;
48
49#define DEBUG 0
50
51static int tpa2018_i2c_write(const char *txData, int length)
52{
53 struct i2c_msg msg[] = {
54 {
55 .addr = this_client->addr,
56 .flags = 0,
57 .len = length,
58 .buf = txData,
59 },
60 };
61
62 if (i2c_transfer(this_client->adapter, msg, 1) < 0) {
63 pr_err("%s: I2C transfer error\n", __func__);
64 return -EIO;
65 } else
66 return 0;
67}
68
69static int tpa2018_i2c_read(char *rxData, int length)
70{
71 struct i2c_msg msgs[] = {
72 {
73 .addr = this_client->addr,
74 .flags = I2C_M_RD,
75 .len = length,
76 .buf = rxData,
77 },
78 };
79
80 if (i2c_transfer(this_client->adapter, msgs, 1) < 0) {
81 pr_err("%s: I2C transfer error\n", __func__);
82 return -EIO;
83 }
84
85#if DEBUG
86 do {
87 int i = 0;
88 for (i = 0; i < length; i++)
89 pr_info("%s: rx[%d] = %2x\n",
90 __func__, i, rxData[i]);
91 } while(0);
92#endif
93
94 return 0;
95}
96
97static int tpa2018d1_open(struct inode *inode, struct file *file)
98{
99 int rc = 0;
100
101 mutex_lock(&spk_amp_lock);
102
103 if (tpa2018d1_opened) {
104 pr_err("%s: busy\n", __func__);
105 rc = -EBUSY;
106 goto done;
107 }
108
109 tpa2018d1_opened = 1;
110done:
111 mutex_unlock(&spk_amp_lock);
112 return rc;
113}
114
115static int tpa2018d1_release(struct inode *inode, struct file *file)
116{
117 mutex_lock(&spk_amp_lock);
118 tpa2018d1_opened = 0;
119 mutex_unlock(&spk_amp_lock);
120
121 return 0;
122}
123
124static int tpa2018d1_read_config(void __user *argp)
125{
126 int rc = 0;
127 unsigned char reg_idx = 0x01;
128 unsigned char tmp[7];
129
130 if (!is_on) {
131 gpio_set_value(pdata->gpio_tpa2018_spk_en, 1);
132 msleep(5); /* According to TPA2018D1 Spec */
133 }
134
135 rc = tpa2018_i2c_write(&reg_idx, sizeof(reg_idx));
136 if (rc < 0)
137 goto err;
138
139 rc = tpa2018_i2c_read(tmp, sizeof(tmp));
140 if (rc < 0)
141 goto err;
142
143 if (copy_to_user(argp, &tmp, sizeof(tmp)))
144 rc = -EFAULT;
145
146err:
147 if (!is_on)
148 gpio_set_value(pdata->gpio_tpa2018_spk_en, 0);
149 return rc;
150}
151
152static int tpa2018d1_ioctl(struct inode *inode, struct file *file,
153 unsigned int cmd, unsigned long arg)
154{
155 void __user *argp = (void __user *)arg;
156 int rc = 0;
157 int mode = -1;
158 int offset = 0;
159 struct tpa2018d1_config_data cfg;
160
161 mutex_lock(&spk_amp_lock);
162
163 switch (cmd) {
164 case TPA2018_SET_CONFIG:
165 if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg)))
166 rc = -EFAULT;
167 break;
168
169 case TPA2018_READ_CONFIG:
170 rc = tpa2018d1_read_config(argp);
171 break;
172
173 case TPA2018_SET_MODE:
174 if (copy_from_user(&mode, argp, sizeof(mode))) {
175 rc = -EFAULT;
176 break;
177 }
178 if (mode >= tpa2018d1_num_modes || mode < 0) {
179 pr_err("%s: unsupported tpa2018d1 mode %d\n",
180 __func__, mode);
181 rc = -EINVAL;
182 break;
183 }
184 if (!config_data) {
185 pr_err("%s: no config data!\n", __func__);
186 rc = -EIO;
187 break;
188 }
189 memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN,
190 TPA2018D1_CMD_LEN);
191 break;
192
193 case TPA2018_SET_PARAM:
194 if (copy_from_user(&cfg, argp, sizeof(cfg))) {
195 pr_err("%s: copy from user failed.\n", __func__);
196 rc = -EFAULT;
197 break;
198 }
199 tpa2018d1_num_modes = cfg.mode_num;
200 if (tpa2018d1_num_modes > TPA2018_NUM_MODES) {
201 pr_err("%s: invalid number of modes %d\n", __func__,
202 tpa2018d1_num_modes);
203 rc = -EINVAL;
204 break;
205 }
206 if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) {
207 pr_err("%s: invalid data length %d, expecting %d\n",
208 __func__, cfg.data_len,
209 tpa2018d1_num_modes * TPA2018D1_CMD_LEN);
210 rc = -EINVAL;
211 break;
212 }
213 /* Free the old data */
214 if (config_data)
215 kfree(config_data);
216 config_data = kmalloc(cfg.data_len, GFP_KERNEL);
217 if (!config_data) {
218 pr_err("%s: out of memory\n", __func__);
219 rc = -ENOMEM;
220 break;
221 }
222 if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) {
223 pr_err("%s: copy data from user failed.\n", __func__);
224 kfree(config_data);
225 config_data = NULL;
226 rc = -EFAULT;
227 break;
228 }
229 /* replace default setting with playback setting */
230 if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) {
231 offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN;
232 memcpy(spk_amp_cfg, config_data + offset,
233 TPA2018D1_CMD_LEN);
234 }
235 break;
236
237 default:
238 pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd));
239 rc = -EINVAL;
240 break;
241 }
242 mutex_unlock(&spk_amp_lock);
243 return rc;
244}
245
246static struct file_operations tpa2018d1_fops = {
247 .owner = THIS_MODULE,
248 .open = tpa2018d1_open,
249 .release = tpa2018d1_release,
250 .ioctl = tpa2018d1_ioctl,
251};
252
253static struct miscdevice tpa2018d1_device = {
254 .minor = MISC_DYNAMIC_MINOR,
255 .name = "tpa2018d1",
256 .fops = &tpa2018d1_fops,
257};
258
259void tpa2018d1_set_speaker_amp(int on)
260{
261 if (!pdata) {
262 pr_err("%s: no platform data!\n", __func__);
263 return;
264 }
265 mutex_lock(&spk_amp_lock);
266 if (on && !is_on) {
267 gpio_set_value(pdata->gpio_tpa2018_spk_en, 1);
268 msleep(5); /* According to TPA2018D1 Spec */
269
270 if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) {
271 is_on = 1;
272 pr_info("%s: ON\n", __func__);
273 }
274 } else if (!on && is_on) {
275 if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) {
276 is_on = 0;
277 msleep(2);
278 gpio_set_value(pdata->gpio_tpa2018_spk_en, 0);
279 pr_info("%s: OFF\n", __func__);
280 }
281 }
282 mutex_unlock(&spk_amp_lock);
283}
284
285static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id)
286{
287 int ret = 0;
288
289 pdata = client->dev.platform_data;
290
291 if (!pdata) {
292 ret = -EINVAL;
293 pr_err("%s: platform data is NULL\n", __func__);
294 goto err_no_pdata;
295 }
296
297 this_client = client;
298
299 ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018");
300 if (ret < 0) {
301 pr_err("%s: gpio request aud_spk_en pin failed\n", __func__);
302 goto err_free_gpio;
303 }
304
305 ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1);
306 if (ret < 0) {
307 pr_err("%s: request aud_spk_en gpio direction failed\n",
308 __func__);
309 goto err_free_gpio;
310 }
311
312 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
313 pr_err("%s: i2c check functionality error\n", __func__);
314 ret = -ENODEV;
315 goto err_free_gpio;
316 }
317
318 gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */
319
320 ret = misc_register(&tpa2018d1_device);
321 if (ret) {
322 pr_err("%s: tpa2018d1_device register failed\n", __func__);
323 goto err_free_gpio;
324 }
325 memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on));
326 return 0;
327
328err_free_gpio:
329 gpio_free(pdata->gpio_tpa2018_spk_en);
330err_no_pdata:
331 return ret;
332}
333
334static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg)
335{
336 return 0;
337}
338
339static int tpa2018d1_resume(struct i2c_client *client)
340{
341 return 0;
342}
343
344static const struct i2c_device_id tpa2018d1_id[] = {
345 { TPA2018D1_I2C_NAME, 0 },
346 { }
347};
348
349static struct i2c_driver tpa2018d1_driver = {
350 .probe = tpa2018d1_probe,
351 .suspend = tpa2018d1_suspend,
352 .resume = tpa2018d1_resume,
353 .id_table = tpa2018d1_id,
354 .driver = {
355 .name = TPA2018D1_I2C_NAME,
356 },
357};
358
359static int __init tpa2018d1_init(void)
360{
361 pr_info("%s\n", __func__);
362 return i2c_add_driver(&tpa2018d1_driver);
363}
364
365module_init(tpa2018d1_init);
366
367MODULE_DESCRIPTION("tpa2018d1 speaker amp driver");
368MODULE_LICENSE("GPL");