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 
33 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
34 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
35 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
36 
37 module_param_array(index, int, NULL, 0444);
38 module_param_array(id, charp, NULL, 0444);
39 module_param_array(enable, bool, NULL, 0444);
40 MODULE_PARM_DESC(index, "Index value for the go7007 audio driver");
41 MODULE_PARM_DESC(id, "ID string for the go7007 audio driver");
42 MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver");
43 
44 struct 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 
55 static 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 
73 static 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 
107 static 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 
125 static 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 
136 static 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 
154 static 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 
163 static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream)
164 {
165 	return 0;
166 }
167 
168 static 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 		/* Just set a flag to indicate we should signal ALSA when
176 		 * sound comes in */
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 
188 static 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 
196 static 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 
202 static 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 
214 static 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 
223 static struct snd_device_ops go7007_snd_device_ops = {
224 	.dev_free	= go7007_snd_free,
225 };
226 
227 int 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 }
286 EXPORT_SYMBOL(go7007_snd_init);
287 
288 int 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 }
297 EXPORT_SYMBOL(go7007_snd_remove);
298 
299 MODULE_LICENSE("GPL v2");
300