htcleo: This adds an SMD channel on /dev/smd1 which can be used with pppd.
[htc-linux.git] / arch / arm / mach-msm / qdsp6 / mp3.c
blob92e8f0f02949a203a91f56cd44012dfc31cef14f
1 /* arch/arm/mach-msm/qdsp6/mp3.c
3 * Copyright (C) 2009 Google, Inc.
4 * Copyright (C) 2009 HTC Corporation
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.
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.
17 #include <linux/fs.h>
18 #include <linux/module.h>
19 #include <linux/miscdevice.h>
20 #include <linux/mutex.h>
21 #include <linux/sched.h>
22 #include <linux/wait.h>
23 #include <linux/uaccess.h>
25 #include <linux/msm_audio.h>
27 #include <mach/msm_qdsp6_audio.h>
28 #include "dal_audio.h"
30 #define BUFSZ (8192)
31 #define DMASZ (BUFSZ * 2)
33 struct mp3 {
34 struct mutex lock;
35 struct audio_client *ac;
36 uint32_t sample_rate;
37 uint32_t channel_count;
40 static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
42 struct mp3 *mp3 = file->private_data;
43 struct cad_audio_eq_cfg eq_cfg;
44 int rc = 0;
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;
54 mutex_lock(&mp3->lock);
55 switch (cmd) {
56 case AUDIO_SET_VOLUME: {
57 int vol;
58 if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
59 rc = -EFAULT;
60 break;
62 rc = q6audio_set_stream_volume(mp3->ac, vol);
63 break;
65 case AUDIO_SET_EQ: {
66 if (copy_from_user(&eq_cfg, (void *)arg, sizeof(struct cad_audio_eq_cfg))) {
67 rc = -EFAULT;
68 break;
70 rc = q6audio_set_stream_eq(mp3->ac, &eq_cfg);
71 break;
73 case AUDIO_START: {
74 uint32_t acdb_id;
75 if (arg == 0) {
76 acdb_id = 0;
77 } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
78 pr_info("pcm_out: copy acdb_id from user failed\n");
79 rc = -EFAULT;
80 break;
82 if (mp3->ac) {
83 rc = -EBUSY;
84 } else {
85 mp3->ac = q6audio_open_mp3(BUFSZ,
86 mp3->sample_rate, mp3->channel_count, acdb_id);
87 if (!mp3->ac)
88 rc = -ENOMEM;
90 break;
92 case AUDIO_STOP:
93 break;
94 case AUDIO_FLUSH:
95 break;
96 case AUDIO_SET_CONFIG: {
97 struct msm_audio_config config;
98 if (mp3->ac) {
99 rc = -EBUSY;
100 break;
102 if (copy_from_user(&config, (void*) arg, sizeof(config))) {
103 rc = -EFAULT;
104 break;
106 if (config.channel_count < 1 || config.channel_count > 2) {
107 rc = -EINVAL;
108 break;
110 mp3->sample_rate = config.sample_rate;
111 mp3->channel_count = config.channel_count;
112 break;
114 case AUDIO_GET_CONFIG: {
115 struct msm_audio_config config;
116 config.buffer_size = BUFSZ;
117 config.buffer_count = 2;
118 config.sample_rate = mp3->sample_rate;
119 config.channel_count = mp3->channel_count;
120 config.unused[0] = 0;
121 config.unused[1] = 0;
122 config.unused[2] = 0;
123 if (copy_to_user((void*) arg, &config, sizeof(config))) {
124 rc = -EFAULT;
126 break;
128 default:
129 rc = -EINVAL;
131 mutex_unlock(&mp3->lock);
132 return rc;
135 static int mp3_open(struct inode *inode, struct file *file)
137 int rc = 0;
139 struct mp3 *mp3;
140 mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL);
142 if (!mp3)
143 return -ENOMEM;
145 mutex_init(&mp3->lock);
146 mp3->channel_count = 2;
147 mp3->sample_rate = 44100;
149 file->private_data = mp3;
150 return rc;
153 static ssize_t mp3_write(struct file *file, const char __user *buf,
154 size_t count, loff_t *pos)
156 struct mp3 *mp3 = file->private_data;
157 struct audio_client *ac;
158 struct audio_buffer *ab;
159 const char __user *start = buf;
160 int xfer;
162 if (!mp3->ac)
163 mp3_ioctl(file, AUDIO_START, 0);
165 ac = mp3->ac;
166 if (!ac)
167 return -ENODEV;
169 while (count > 0) {
170 ab = ac->buf + ac->cpu_buf;
172 if (ab->used)
173 wait_event(ac->wait, (ab->used == 0));
175 xfer = count;
176 if (xfer > ab->size)
177 xfer = ab->size;
179 if (copy_from_user(ab->data, buf, xfer))
180 return -EFAULT;
182 buf += xfer;
183 count -= xfer;
185 ab->used = xfer;
186 q6audio_write(ac, ab);
187 ac->cpu_buf ^= 1;
190 return buf - start;
193 static int mp3_fsync(struct file *f, struct dentry *dentry, int datasync)
195 struct mp3 *mp3 = f->private_data;
196 if (mp3->ac)
197 return q6audio_async(mp3->ac);
198 return -ENODEV;
201 static int mp3_release(struct inode *inode, struct file *file)
203 struct mp3 *mp3 = file->private_data;
204 if (mp3->ac)
205 q6audio_mp3_close(mp3->ac);
206 kfree(mp3);
207 return 0;
210 static struct file_operations mp3_fops = {
211 .owner = THIS_MODULE,
212 .open = mp3_open,
213 .write = mp3_write,
214 .fsync = mp3_fsync,
215 .release = mp3_release,
216 .unlocked_ioctl = mp3_ioctl,
219 struct miscdevice mp3_misc = {
220 .minor = MISC_DYNAMIC_MINOR,
221 .name = "msm_mp3",
222 .fops = &mp3_fops,
225 static int __init mp3_init(void) {
226 return misc_register(&mp3_misc);
229 device_initcall(mp3_init);