1
2
3
4
5
6
7
8
9
10
11
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 const 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 unsigned long flags;
79
80 spin_lock_irqsave(&gosnd->lock, flags);
81 gosnd->hw_ptr += frames;
82 if (gosnd->hw_ptr >= runtime->buffer_size)
83 gosnd->hw_ptr -= runtime->buffer_size;
84 gosnd->avail += frames;
85 spin_unlock_irqrestore(&gosnd->lock, flags);
86 if (gosnd->w_idx + length > runtime->dma_bytes) {
87 int cpy = runtime->dma_bytes - gosnd->w_idx;
88
89 memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy);
90 length -= cpy;
91 buf += cpy;
92 gosnd->w_idx = 0;
93 }
94 memcpy(runtime->dma_area + gosnd->w_idx, buf, length);
95 gosnd->w_idx += length;
96 spin_lock_irqsave(&gosnd->lock, flags);
97 if (gosnd->avail < runtime->period_size) {
98 spin_unlock_irqrestore(&gosnd->lock, flags);
99 return;
100 }
101 gosnd->avail -= runtime->period_size;
102 spin_unlock_irqrestore(&gosnd->lock, flags);
103 if (gosnd->capturing)
104 snd_pcm_period_elapsed(gosnd->substream);
105}
106
107static int go7007_snd_hw_params(struct snd_pcm_substream *substream,
108 struct snd_pcm_hw_params *hw_params)
109{
110 struct go7007 *go = snd_pcm_substream_chip(substream);
111 unsigned int bytes;
112
113 bytes = params_buffer_bytes(hw_params);
114 if (substream->runtime->dma_bytes > 0)
115 vfree(substream->runtime->dma_area);
116 substream->runtime->dma_bytes = 0;
117 substream->runtime->dma_area = vmalloc(bytes);
118 if (substream->runtime->dma_area == NULL)
119 return -ENOMEM;
120 substream->runtime->dma_bytes = bytes;
121 go->audio_deliver = parse_audio_stream_data;
122 return 0;
123}
124
125static int go7007_snd_hw_free(struct snd_pcm_substream *substream)
126{
127 struct go7007 *go = snd_pcm_substream_chip(substream);
128
129 go->audio_deliver = NULL;
130 if (substream->runtime->dma_bytes > 0)
131 vfree(substream->runtime->dma_area);
132 substream->runtime->dma_bytes = 0;
133 return 0;
134}
135
136static int go7007_snd_capture_open(struct snd_pcm_substream *substream)
137{
138 struct go7007 *go = snd_pcm_substream_chip(substream);
139 struct go7007_snd *gosnd = go->snd_context;
140 unsigned long flags;
141 int r;
142
143 spin_lock_irqsave(&gosnd->lock, flags);
144 if (gosnd->substream == NULL) {
145 gosnd->substream = substream;
146 substream->runtime->hw = go7007_snd_capture_hw;
147 r = 0;
148 } else
149 r = -EBUSY;
150 spin_unlock_irqrestore(&gosnd->lock, flags);
151 return r;
152}
153
154static int go7007_snd_capture_close(struct snd_pcm_substream *substream)
155{
156 struct go7007 *go = snd_pcm_substream_chip(substream);
157 struct go7007_snd *gosnd = go->snd_context;
158
159 gosnd->substream = NULL;
160 return 0;
161}
162
163static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream)
164{
165 return 0;
166}
167
168static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
169{
170 struct go7007 *go = snd_pcm_substream_chip(substream);
171 struct go7007_snd *gosnd = go->snd_context;
172
173 switch (cmd) {
174 case SNDRV_PCM_TRIGGER_START:
175
176
177 gosnd->capturing = 1;
178 return 0;
179 case SNDRV_PCM_TRIGGER_STOP:
180 gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
181 gosnd->capturing = 0;
182 return 0;
183 default:
184 return -EINVAL;
185 }
186}
187
188static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream)
189{
190 struct go7007 *go = snd_pcm_substream_chip(substream);
191 struct go7007_snd *gosnd = go->snd_context;
192
193 return gosnd->hw_ptr;
194}
195
196static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream,
197 unsigned long offset)
198{
199 return vmalloc_to_page(substream->runtime->dma_area + offset);
200}
201
202static const struct snd_pcm_ops go7007_snd_capture_ops = {
203 .open = go7007_snd_capture_open,
204 .close = go7007_snd_capture_close,
205 .ioctl = snd_pcm_lib_ioctl,
206 .hw_params = go7007_snd_hw_params,
207 .hw_free = go7007_snd_hw_free,
208 .prepare = go7007_snd_pcm_prepare,
209 .trigger = go7007_snd_pcm_trigger,
210 .pointer = go7007_snd_pcm_pointer,
211 .page = go7007_snd_pcm_page,
212};
213
214static int go7007_snd_free(struct snd_device *device)
215{
216 struct go7007 *go = device->device_data;
217
218 kfree(go->snd_context);
219 go->snd_context = NULL;
220 return 0;
221}
222
223static struct snd_device_ops go7007_snd_device_ops = {
224 .dev_free = go7007_snd_free,
225};
226
227int go7007_snd_init(struct go7007 *go)
228{
229 static int dev;
230 struct go7007_snd *gosnd;
231 int ret;
232
233 if (dev >= SNDRV_CARDS)
234 return -ENODEV;
235 if (!enable[dev]) {
236 dev++;
237 return -ENOENT;
238 }
239 gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL);
240 if (gosnd == NULL)
241 return -ENOMEM;
242 spin_lock_init(&gosnd->lock);
243 gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
244 gosnd->capturing = 0;
245 ret = snd_card_new(go->dev, index[dev], id[dev], THIS_MODULE, 0,
246 &gosnd->card);
247 if (ret < 0) {
248 kfree(gosnd);
249 return ret;
250 }
251 ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go,
252 &go7007_snd_device_ops);
253 if (ret < 0) {
254 kfree(gosnd);
255 return ret;
256 }
257 ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm);
258 if (ret < 0) {
259 snd_card_free(gosnd->card);
260 kfree(gosnd);
261 return ret;
262 }
263 strlcpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver));
264 strlcpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver));
265 strlcpy(gosnd->card->longname, gosnd->card->shortname,
266 sizeof(gosnd->card->longname));
267
268 gosnd->pcm->private_data = go;
269 snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE,
270 &go7007_snd_capture_ops);
271
272 ret = snd_card_register(gosnd->card);
273 if (ret < 0) {
274 snd_card_free(gosnd->card);
275 kfree(gosnd);
276 return ret;
277 }
278
279 gosnd->substream = NULL;
280 go->snd_context = gosnd;
281 v4l2_device_get(&go->v4l2_dev);
282 ++dev;
283
284 return 0;
285}
286EXPORT_SYMBOL(go7007_snd_init);
287
288int go7007_snd_remove(struct go7007 *go)
289{
290 struct go7007_snd *gosnd = go->snd_context;
291
292 snd_card_disconnect(gosnd->card);
293 snd_card_free_when_closed(gosnd->card);
294 v4l2_device_put(&go->v4l2_dev);
295 return 0;
296}
297EXPORT_SYMBOL(go7007_snd_remove);
298
299MODULE_LICENSE("GPL v2");
300