1/* 2 * Copyright (C) 2005-2006 Micronas USA Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License (Version 2) as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/moduleparam.h> 17#include <linux/spinlock.h> 18#include <linux/delay.h> 19#include <linux/sched.h> 20#include <linux/vmalloc.h> 21#include <linux/time.h> 22#include <linux/mm.h> 23#include <linux/i2c.h> 24#include <linux/mutex.h> 25#include <linux/uaccess.h> 26#include <linux/slab.h> 27#include <sound/core.h> 28#include <sound/pcm.h> 29#include <sound/initval.h> 30 31#include "go7007-priv.h" 32 33static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 34static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 35static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 36 37module_param_array(index, int, NULL, 0444); 38module_param_array(id, charp, NULL, 0444); 39module_param_array(enable, bool, NULL, 0444); 40MODULE_PARM_DESC(index, "Index value for the go7007 audio driver"); 41MODULE_PARM_DESC(id, "ID string for the go7007 audio driver"); 42MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver"); 43 44struct go7007_snd { 45 struct snd_card *card; 46 struct snd_pcm *pcm; 47 struct snd_pcm_substream *substream; 48 spinlock_t lock; 49 int w_idx; 50 int hw_ptr; 51 int avail; 52 int capturing; 53}; 54 55static struct snd_pcm_hardware go7007_snd_capture_hw = { 56 .info = (SNDRV_PCM_INFO_MMAP | 57 SNDRV_PCM_INFO_INTERLEAVED | 58 SNDRV_PCM_INFO_BLOCK_TRANSFER | 59 SNDRV_PCM_INFO_MMAP_VALID), 60 .formats = SNDRV_PCM_FMTBIT_S16_LE, 61 .rates = SNDRV_PCM_RATE_48000, 62 .rate_min = 48000, 63 .rate_max = 48000, 64 .channels_min = 2, 65 .channels_max = 2, 66 .buffer_bytes_max = (128*1024), 67 .period_bytes_min = 4096, 68 .period_bytes_max = (128*1024), 69 .periods_min = 1, 70 .periods_max = 32, 71}; 72 73static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) 74{ 75 struct go7007_snd *gosnd = go->snd_context; 76 struct snd_pcm_runtime *runtime = gosnd->substream->runtime; 77 int frames = bytes_to_frames(runtime, length); 78 79 spin_lock(&gosnd->lock); 80 gosnd->hw_ptr += frames; 81 if (gosnd->hw_ptr >= runtime->buffer_size) 82 gosnd->hw_ptr -= runtime->buffer_size; 83 gosnd->avail += frames; 84 spin_unlock(&gosnd->lock); 85 if (gosnd->w_idx + length > runtime->dma_bytes) { 86 int cpy = runtime->dma_bytes - gosnd->w_idx; 87 88 memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy); 89 length -= cpy; 90 buf += cpy; 91 gosnd->w_idx = 0; 92 } 93 memcpy(runtime->dma_area + gosnd->w_idx, buf, length); 94 gosnd->w_idx += length; 95 spin_lock(&gosnd->lock); 96 if (gosnd->avail < runtime->period_size) { 97 spin_unlock(&gosnd->lock); 98 return; 99 } 100 gosnd->avail -= runtime->period_size; 101 spin_unlock(&gosnd->lock); 102 if (gosnd->capturing) 103 snd_pcm_period_elapsed(gosnd->substream); 104} 105 106static int go7007_snd_hw_params(struct snd_pcm_substream *substream, 107 struct snd_pcm_hw_params *hw_params) 108{ 109 struct go7007 *go = snd_pcm_substream_chip(substream); 110 unsigned int bytes; 111 112 bytes = params_buffer_bytes(hw_params); 113 if (substream->runtime->dma_bytes > 0) 114 vfree(substream->runtime->dma_area); 115 substream->runtime->dma_bytes = 0; 116 substream->runtime->dma_area = vmalloc(bytes); 117 if (substream->runtime->dma_area == NULL) 118 return -ENOMEM; 119 substream->runtime->dma_bytes = bytes; 120 go->audio_deliver = parse_audio_stream_data; 121 return 0; 122} 123 124static int go7007_snd_hw_free(struct snd_pcm_substream *substream) 125{ 126 struct go7007 *go = snd_pcm_substream_chip(substream); 127 128 go->audio_deliver = NULL; 129 if (substream->runtime->dma_bytes > 0) 130 vfree(substream->runtime->dma_area); 131 substream->runtime->dma_bytes = 0; 132 return 0; 133} 134 135static int go7007_snd_capture_open(struct snd_pcm_substream *substream) 136{ 137 struct go7007 *go = snd_pcm_substream_chip(substream); 138 struct go7007_snd *gosnd = go->snd_context; 139 unsigned long flags; 140 int r; 141 142 spin_lock_irqsave(&gosnd->lock, flags); 143 if (gosnd->substream == NULL) { 144 gosnd->substream = substream; 145 substream->runtime->hw = go7007_snd_capture_hw; 146 r = 0; 147 } else 148 r = -EBUSY; 149 spin_unlock_irqrestore(&gosnd->lock, flags); 150 return r; 151} 152 153static int go7007_snd_capture_close(struct snd_pcm_substream *substream) 154{ 155 struct go7007 *go = snd_pcm_substream_chip(substream); 156 struct go7007_snd *gosnd = go->snd_context; 157 158 gosnd->substream = NULL; 159 return 0; 160} 161 162static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream) 163{ 164 return 0; 165} 166 167static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 168{ 169 struct go7007 *go = snd_pcm_substream_chip(substream); 170 struct go7007_snd *gosnd = go->snd_context; 171 172 switch (cmd) { 173 case SNDRV_PCM_TRIGGER_START: 174 /* Just set a flag to indicate we should signal ALSA when 175 * sound comes in */ 176 gosnd->capturing = 1; 177 return 0; 178 case SNDRV_PCM_TRIGGER_STOP: 179 gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; 180 gosnd->capturing = 0; 181 return 0; 182 default: 183 return -EINVAL; 184 } 185} 186 187static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream) 188{ 189 struct go7007 *go = snd_pcm_substream_chip(substream); 190 struct go7007_snd *gosnd = go->snd_context; 191 192 return gosnd->hw_ptr; 193} 194 195static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream, 196 unsigned long offset) 197{ 198 return vmalloc_to_page(substream->runtime->dma_area + offset); 199} 200 201static struct snd_pcm_ops go7007_snd_capture_ops = { 202 .open = go7007_snd_capture_open, 203 .close = go7007_snd_capture_close, 204 .ioctl = snd_pcm_lib_ioctl, 205 .hw_params = go7007_snd_hw_params, 206 .hw_free = go7007_snd_hw_free, 207 .prepare = go7007_snd_pcm_prepare, 208 .trigger = go7007_snd_pcm_trigger, 209 .pointer = go7007_snd_pcm_pointer, 210 .page = go7007_snd_pcm_page, 211}; 212 213static int go7007_snd_free(struct snd_device *device) 214{ 215 struct go7007 *go = device->device_data; 216 217 kfree(go->snd_context); 218 go->snd_context = NULL; 219 return 0; 220} 221 222static struct snd_device_ops go7007_snd_device_ops = { 223 .dev_free = go7007_snd_free, 224}; 225 226int go7007_snd_init(struct go7007 *go) 227{ 228 static int dev; 229 struct go7007_snd *gosnd; 230 int ret = 0; 231 232 if (dev >= SNDRV_CARDS) 233 return -ENODEV; 234 if (!enable[dev]) { 235 dev++; 236 return -ENOENT; 237 } 238 gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL); 239 if (gosnd == NULL) 240 return -ENOMEM; 241 spin_lock_init(&gosnd->lock); 242 gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; 243 gosnd->capturing = 0; 244 ret = snd_card_new(go->dev, index[dev], id[dev], THIS_MODULE, 0, 245 &gosnd->card); 246 if (ret < 0) { 247 kfree(gosnd); 248 return ret; 249 } 250 ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go, 251 &go7007_snd_device_ops); 252 if (ret < 0) { 253 kfree(gosnd); 254 return ret; 255 } 256 ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm); 257 if (ret < 0) { 258 snd_card_free(gosnd->card); 259 kfree(gosnd); 260 return ret; 261 } 262 strlcpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver)); 263 strlcpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver)); 264 strlcpy(gosnd->card->longname, gosnd->card->shortname, 265 sizeof(gosnd->card->longname)); 266 267 gosnd->pcm->private_data = go; 268 snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE, 269 &go7007_snd_capture_ops); 270 271 ret = snd_card_register(gosnd->card); 272 if (ret < 0) { 273 snd_card_free(gosnd->card); 274 kfree(gosnd); 275 return ret; 276 } 277 278 gosnd->substream = NULL; 279 go->snd_context = gosnd; 280 v4l2_device_get(&go->v4l2_dev); 281 ++dev; 282 283 return 0; 284} 285EXPORT_SYMBOL(go7007_snd_init); 286 287int go7007_snd_remove(struct go7007 *go) 288{ 289 struct go7007_snd *gosnd = go->snd_context; 290 291 snd_card_disconnect(gosnd->card); 292 snd_card_free_when_closed(gosnd->card); 293 v4l2_device_put(&go->v4l2_dev); 294 return 0; 295} 296EXPORT_SYMBOL(go7007_snd_remove); 297 298MODULE_LICENSE("GPL v2"); 299