blob: 16f6204febb7cf9d4c0c074b5a2c0d26547aca62 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/qdsp6/mp3.c
2 *
3 * Copyright (C) 2009 Google, Inc.
4 * Copyright (C) 2009 HTC Corporation
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/slab.h>
18#include <linux/fs.h>
19#include <linux/module.h>
20#include <linux/miscdevice.h>
21#include <linux/mutex.h>
22#include <linux/sched.h>
23#include <linux/wait.h>
24#include <linux/uaccess.h>
25
26#include <linux/msm_audio.h>
27
28#include <mach/msm_qdsp6_audio.h>
29#include <mach/debug_mm.h>
30
31#define BUFSZ (8192)
32#define DMASZ (BUFSZ * 2)
33
34struct mp3 {
35 struct mutex lock;
36 struct audio_client *ac;
37 uint32_t sample_rate;
38 uint32_t channel_count;
39};
40
41static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
42{
43 struct mp3 *mp3 = file->private_data;
44 int rc = 0;
45
46 if (cmd == AUDIO_GET_STATS) {
47 struct msm_audio_stats stats;
48 memset(&stats, 0, sizeof(stats));
49 if (copy_to_user((void*) arg, &stats, sizeof(stats)))
50 return -EFAULT;
51 return 0;
52 }
53
54 mutex_lock(&mp3->lock);
55 switch (cmd) {
56 case AUDIO_SET_VOLUME: {
57 int vol;
58 pr_debug("[%s:%s] SET_VOLUME = %d\n", __MM_FILE__,
59 __func__, vol);
60 if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
61 rc = -EFAULT;
62 break;
63 }
64 rc = q6audio_set_stream_volume(mp3->ac, vol);
65 break;
66 }
67 case AUDIO_START: {
68 uint32_t acdb_id;
69 pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
70 if (arg == 0) {
71 acdb_id = 0;
72 } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
73 pr_info("[%s:%s] copy acdb_id from user failed\n",
74 __MM_FILE__, __func__);
75 rc = -EFAULT;
76 break;
77 }
78 if (mp3->ac) {
79 pr_err("[%s:%s] active session already existing\n",
80 __MM_FILE__, __func__);
81 rc = -EBUSY;
82 } else {
83 mp3->ac = q6audio_open_mp3(BUFSZ,
84 mp3->sample_rate, mp3->channel_count, acdb_id);
85 if (!mp3->ac) {
86 pr_err("[%s:%s] mp3 open session failed\n",
87 __MM_FILE__, __func__);
88 rc = -ENOMEM;
89 }
90 }
91 break;
92 }
93 case AUDIO_STOP:
94 pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
95 break;
96 case AUDIO_FLUSH:
97 break;
98 case AUDIO_SET_CONFIG: {
99 struct msm_audio_config config;
100 if (mp3->ac) {
101 rc = -EBUSY;
102 pr_err("[%s:%s] active session already existing\n",
103 __MM_FILE__, __func__);
104 break;
105 }
106 if (copy_from_user(&config, (void*) arg, sizeof(config))) {
107 rc = -EFAULT;
108 break;
109 }
110 pr_debug("[%s:%s] SET_CONFIG: buffsize = %d, samplerate = %d, \
111 channelcount = %d\n", __MM_FILE__, __func__,
112 config.buffer_size, config.sample_rate,
113 config.channel_count);
114 if (config.channel_count < 1 || config.channel_count > 2) {
115 rc = -EINVAL;
116 pr_err("[%s:%s] invalid channelcount\n", __MM_FILE__,
117 __func__);
118 break;
119 }
120 mp3->sample_rate = config.sample_rate;
121 mp3->channel_count = config.channel_count;
122 break;
123 }
124 case AUDIO_GET_CONFIG: {
125 struct msm_audio_config config;
126 config.buffer_size = BUFSZ;
127 config.buffer_count = 2;
128 config.sample_rate = mp3->sample_rate;
129 config.channel_count = mp3->channel_count;
130 config.unused[0] = 0;
131 config.unused[1] = 0;
132 config.unused[2] = 0;
133 if (copy_to_user((void*) arg, &config, sizeof(config))) {
134 rc = -EFAULT;
135 }
136 pr_debug("[%s:%s] GET_CONFIG: buffsize = %d, samplerate = %d, \
137 channelcount = %d\n", __MM_FILE__, __func__,
138 config.buffer_size, config.sample_rate,
139 config.channel_count);
140 break;
141 }
142 default:
143 rc = -EINVAL;
144 }
145 mutex_unlock(&mp3->lock);
146 pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc);
147 return rc;
148}
149
150static int mp3_open(struct inode *inode, struct file *file)
151{
152 int rc = 0;
153
154 struct mp3 *mp3;
155 pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
156 mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL);
157
158 if (!mp3)
159 return -ENOMEM;
160
161 mutex_init(&mp3->lock);
162 mp3->channel_count = 2;
163 mp3->sample_rate = 44100;
164
165 file->private_data = mp3;
166 return rc;
167}
168
169static ssize_t mp3_write(struct file *file, const char __user *buf,
170 size_t count, loff_t *pos)
171{
172 struct mp3 *mp3 = file->private_data;
173 struct audio_client *ac;
174 struct audio_buffer *ab;
175 const char __user *start = buf;
176 int xfer;
177
178 pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
179 if (!mp3->ac)
180 mp3_ioctl(file, AUDIO_START, 0);
181
182 ac = mp3->ac;
183 if (!ac)
184 return -ENODEV;
185
186 while (count > 0) {
187 ab = ac->buf + ac->cpu_buf;
188
189 if (ab->used)
190 wait_event(ac->wait, (ab->used == 0));
191
192 pr_debug("[%s:%s] ab->data = %p, ac->cpu_buf = %d\n",
193 __MM_FILE__, __func__, ab->data, ac->cpu_buf);
194 xfer = count;
195 if (xfer > ab->size)
196 xfer = ab->size;
197
198 if (copy_from_user(ab->data, buf, xfer))
199 return -EFAULT;
200
201 buf += xfer;
202 count -= xfer;
203
204 ab->used = xfer;
205 q6audio_write(ac, ab);
206 ac->cpu_buf ^= 1;
207 }
208
209 return buf - start;
210}
211
212static int mp3_fsync(struct file *f, int datasync)
213{
214 struct mp3 *mp3 = f->private_data;
215 if (mp3->ac)
216 return q6audio_async(mp3->ac);
217 return -ENODEV;
218}
219
220static int mp3_release(struct inode *inode, struct file *file)
221{
222 struct mp3 *mp3 = file->private_data;
223 if (mp3->ac)
224 q6audio_mp3_close(mp3->ac);
225 kfree(mp3);
226 pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
227 return 0;
228}
229
230static struct file_operations mp3_fops = {
231 .owner = THIS_MODULE,
232 .open = mp3_open,
233 .write = mp3_write,
234 .fsync = mp3_fsync,
235 .release = mp3_release,
236 .unlocked_ioctl = mp3_ioctl,
237};
238
239struct miscdevice mp3_misc = {
240 .minor = MISC_DYNAMIC_MINOR,
241 .name = "msm_mp3",
242 .fops = &mp3_fops,
243};
244
245static int __init mp3_init(void) {
246 return misc_register(&mp3_misc);
247}
248
249device_initcall(mp3_init);