/arch/arm/mach-fsm/qdsp6/pcm_out.c

https://bitbucket.org/sammyz/iscream_thunderc-2.6.35-rebase · C · 242 lines · 199 code · 28 blank · 15 comment · 32 complexity · 8681c9cc15d28d3200a483a53d0d6517 MD5 · raw file

  1. /* arch/arm/mach-msm/qdsp6/pcm_out.c
  2. *
  3. * Copyright (C) 2009 Google, Inc.
  4. * Author: Brian Swetland <swetland@google.com>
  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. #include <linux/fs.h>
  17. #include <linux/module.h>
  18. #include <linux/miscdevice.h>
  19. #include <linux/mutex.h>
  20. #include <linux/sched.h>
  21. #include <linux/wait.h>
  22. #include <linux/uaccess.h>
  23. #include <linux/msm_audio.h>
  24. #include <mach/msm_qdsp6_audio.h>
  25. #include <mach/debug_mm.h>
  26. void audio_client_dump(struct audio_client *ac);
  27. #define BUFSZ (3072)
  28. struct pcm {
  29. struct mutex lock;
  30. struct audio_client *ac;
  31. uint32_t sample_rate;
  32. uint32_t channel_count;
  33. size_t buffer_size;
  34. };
  35. static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  36. {
  37. struct pcm *pcm = file->private_data;
  38. int rc = 0;
  39. if (cmd == AUDIO_GET_STATS) {
  40. struct msm_audio_stats stats;
  41. memset(&stats, 0, sizeof(stats));
  42. if (copy_to_user((void*) arg, &stats, sizeof(stats)))
  43. return -EFAULT;
  44. return 0;
  45. }
  46. mutex_lock(&pcm->lock);
  47. switch (cmd) {
  48. case AUDIO_SET_VOLUME: {
  49. int vol;
  50. if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
  51. rc = -EFAULT;
  52. break;
  53. }
  54. rc = q6audio_set_stream_volume(pcm->ac, vol);
  55. break;
  56. }
  57. case AUDIO_START: {
  58. uint32_t acdb_id;
  59. if (arg == 0) {
  60. acdb_id = 0;
  61. } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
  62. pr_info("[%s:%s] copy acdb_id from user failed\n",
  63. __MM_FILE__, __func__);
  64. rc = -EFAULT;
  65. break;
  66. }
  67. if (pcm->ac) {
  68. rc = -EBUSY;
  69. } else {
  70. pcm->ac = q6audio_open_pcm(pcm->buffer_size,
  71. pcm->sample_rate,
  72. pcm->channel_count,
  73. AUDIO_FLAG_WRITE, acdb_id);
  74. if (!pcm->ac)
  75. rc = -ENOMEM;
  76. }
  77. break;
  78. }
  79. case AUDIO_STOP:
  80. break;
  81. case AUDIO_FLUSH:
  82. break;
  83. case AUDIO_SET_CONFIG: {
  84. struct msm_audio_config config;
  85. if (pcm->ac) {
  86. rc = -EBUSY;
  87. break;
  88. }
  89. if (copy_from_user(&config, (void*) arg, sizeof(config))) {
  90. rc = -EFAULT;
  91. break;
  92. }
  93. if (config.channel_count < 1 || config.channel_count > 2) {
  94. rc = -EINVAL;
  95. break;
  96. }
  97. if (config.sample_rate < 8000 || config.sample_rate > 48000) {
  98. rc = -EINVAL;
  99. break;
  100. }
  101. if (config.buffer_size < 128 || config.buffer_size > 8192) {
  102. rc = -EINVAL;
  103. break;
  104. }
  105. pcm->sample_rate = config.sample_rate;
  106. pcm->channel_count = config.channel_count;
  107. pcm->buffer_size = config.buffer_size;
  108. break;
  109. }
  110. case AUDIO_GET_CONFIG: {
  111. struct msm_audio_config config;
  112. config.buffer_size = pcm->buffer_size;
  113. config.buffer_count = 2;
  114. config.sample_rate = pcm->sample_rate;
  115. config.channel_count = pcm->channel_count;
  116. config.unused[0] = 0;
  117. config.unused[1] = 0;
  118. config.unused[2] = 0;
  119. if (copy_to_user((void*) arg, &config, sizeof(config))) {
  120. rc = -EFAULT;
  121. }
  122. break;
  123. }
  124. case AUDIO_SET_EQ: {
  125. struct msm_audio_eq_stream_config eq_config;
  126. if (copy_from_user(&eq_config, (void *) arg,
  127. sizeof(eq_config))) {
  128. rc = -EFAULT;
  129. break;
  130. }
  131. rc = q6audio_set_stream_eq_pcm(pcm->ac, (void *) &eq_config);
  132. break;
  133. }
  134. default:
  135. rc = -EINVAL;
  136. }
  137. mutex_unlock(&pcm->lock);
  138. return rc;
  139. }
  140. static int pcm_open(struct inode *inode, struct file *file)
  141. {
  142. struct pcm *pcm;
  143. pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
  144. pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
  145. if (!pcm)
  146. return -ENOMEM;
  147. mutex_init(&pcm->lock);
  148. pcm->channel_count = 2;
  149. pcm->sample_rate = 44100;
  150. pcm->buffer_size = BUFSZ;
  151. file->private_data = pcm;
  152. return 0;
  153. }
  154. static ssize_t pcm_write(struct file *file, const char __user *buf,
  155. size_t count, loff_t *pos)
  156. {
  157. struct pcm *pcm = file->private_data;
  158. struct audio_client *ac;
  159. struct audio_buffer *ab;
  160. const char __user *start = buf;
  161. int xfer;
  162. if (!pcm->ac)
  163. pcm_ioctl(file, AUDIO_START, 0);
  164. ac = pcm->ac;
  165. if (!ac)
  166. return -ENODEV;
  167. while (count > 0) {
  168. ab = ac->buf + ac->cpu_buf;
  169. if (ab->used)
  170. if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) {
  171. audio_client_dump(ac);
  172. pr_err("[%s:%s] timeout. dsp dead?\n",
  173. __MM_FILE__, __func__);
  174. q6audio_dsp_not_responding();
  175. }
  176. xfer = count;
  177. if (xfer > ab->size)
  178. xfer = ab->size;
  179. if (copy_from_user(ab->data, buf, xfer))
  180. return -EFAULT;
  181. buf += xfer;
  182. count -= xfer;
  183. ab->used = 1;
  184. ab->actual_size = xfer;
  185. q6audio_write(ac, ab);
  186. ac->cpu_buf ^= 1;
  187. }
  188. return buf - start;
  189. }
  190. static int pcm_release(struct inode *inode, struct file *file)
  191. {
  192. struct pcm *pcm = file->private_data;
  193. if (pcm->ac)
  194. q6audio_close(pcm->ac);
  195. kfree(pcm);
  196. pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
  197. return 0;
  198. }
  199. static struct file_operations pcm_fops = {
  200. .owner = THIS_MODULE,
  201. .open = pcm_open,
  202. .write = pcm_write,
  203. .release = pcm_release,
  204. .unlocked_ioctl = pcm_ioctl,
  205. };
  206. struct miscdevice pcm_misc = {
  207. .minor = MISC_DYNAMIC_MINOR,
  208. .name = "msm_pcm_out",
  209. .fops = &pcm_fops,
  210. };
  211. static int __init pcm_init(void) {
  212. return misc_register(&pcm_misc);
  213. }
  214. device_initcall(pcm_init);