1 #include <linux/kernel.h>
2 #include <linux/usb.h>
3 #include <linux/init.h>
4 #include <linux/sound.h>
5 #include <linux/spinlock.h>
6 #include <linux/soundcard.h>
7 #include <linux/vmalloc.h>
8 #include <linux/proc_fs.h>
9 #include <linux/module.h>
10 #include <linux/gfp.h>
11 #include <sound/core.h>
12 #include <sound/pcm.h>
13 #include <sound/pcm_params.h>
14 #include <sound/info.h>
15 #include <sound/initval.h>
16 #include <sound/control.h>
17 #include <media/v4l2-common.h>
18 #include "pd-common.h"
19 #include "vendorcmds.h"
20 
21 static void complete_handler_audio(struct urb *urb);
22 #define AUDIO_EP	(0x83)
23 #define AUDIO_BUF_SIZE	(512)
24 #define PERIOD_SIZE	(1024 * 8)
25 #define PERIOD_MIN	(4)
26 #define PERIOD_MAX 	PERIOD_MIN
27 
28 static struct snd_pcm_hardware snd_pd_hw_capture = {
29 	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
30 		SNDRV_PCM_INFO_MMAP           |
31 		SNDRV_PCM_INFO_INTERLEAVED |
32 		SNDRV_PCM_INFO_MMAP_VALID,
33 
34 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
35 	.rates = SNDRV_PCM_RATE_48000,
36 
37 	.rate_min = 48000,
38 	.rate_max = 48000,
39 	.channels_min = 2,
40 	.channels_max = 2,
41 	.buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
42 	.period_bytes_min = PERIOD_SIZE,
43 	.period_bytes_max = PERIOD_SIZE,
44 	.periods_min = PERIOD_MIN,
45 	.periods_max = PERIOD_MAX,
46 	/*
47 	.buffer_bytes_max = 62720 * 8,
48 	.period_bytes_min = 64,
49 	.period_bytes_max = 12544,
50 	.periods_min = 2,
51 	.periods_max = 98
52 	*/
53 };
54 
snd_pd_capture_open(struct snd_pcm_substream * substream)55 static int snd_pd_capture_open(struct snd_pcm_substream *substream)
56 {
57 	struct poseidon *p = snd_pcm_substream_chip(substream);
58 	struct poseidon_audio *pa = &p->audio;
59 	struct snd_pcm_runtime *runtime = substream->runtime;
60 
61 	if (!p)
62 		return -ENODEV;
63 	pa->users++;
64 	pa->card_close 		= 0;
65 	pa->capture_pcm_substream	= substream;
66 	runtime->private_data		= p;
67 
68 	runtime->hw = snd_pd_hw_capture;
69 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
70 	usb_autopm_get_interface(p->interface);
71 	kref_get(&p->kref);
72 	return 0;
73 }
74 
snd_pd_pcm_close(struct snd_pcm_substream * substream)75 static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
76 {
77 	struct poseidon *p = snd_pcm_substream_chip(substream);
78 	struct poseidon_audio *pa = &p->audio;
79 
80 	pa->users--;
81 	pa->card_close 		= 1;
82 	usb_autopm_put_interface(p->interface);
83 	kref_put(&p->kref, poseidon_delete);
84 	return 0;
85 }
86 
snd_pd_hw_capture_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)87 static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
88 					struct snd_pcm_hw_params *hw_params)
89 {
90 	struct snd_pcm_runtime *runtime = substream->runtime;
91 	unsigned int size;
92 
93 	size = params_buffer_bytes(hw_params);
94 	if (runtime->dma_area) {
95 		if (runtime->dma_bytes > size)
96 			return 0;
97 		vfree(runtime->dma_area);
98 	}
99 	runtime->dma_area = vmalloc(size);
100 	if (!runtime->dma_area)
101 		return -ENOMEM;
102 	else
103 		runtime->dma_bytes = size;
104 	return 0;
105 }
106 
audio_buf_free(struct poseidon * p)107 static int audio_buf_free(struct poseidon *p)
108 {
109 	struct poseidon_audio *pa = &p->audio;
110 	int i;
111 
112 	for (i = 0; i < AUDIO_BUFS; i++)
113 		if (pa->urb_array[i])
114 			usb_kill_urb(pa->urb_array[i]);
115 	free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
116 	logpm();
117 	return 0;
118 }
119 
snd_pd_hw_capture_free(struct snd_pcm_substream * substream)120 static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
121 {
122 	struct poseidon *p = snd_pcm_substream_chip(substream);
123 
124 	logpm();
125 	audio_buf_free(p);
126 	return 0;
127 }
128 
snd_pd_prepare(struct snd_pcm_substream * substream)129 static int snd_pd_prepare(struct snd_pcm_substream *substream)
130 {
131 	return 0;
132 }
133 
134 #define AUDIO_TRAILER_SIZE	(16)
handle_audio_data(struct urb * urb,int * period_elapsed)135 static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
136 {
137 	struct poseidon_audio *pa = urb->context;
138 	struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
139 
140 	int stride	= runtime->frame_bits >> 3;
141 	int len		= urb->actual_length / stride;
142 	unsigned char *cp	= urb->transfer_buffer;
143 	unsigned int oldptr	= pa->rcv_position;
144 
145 	if (urb->actual_length == AUDIO_BUF_SIZE - 4)
146 		len -= (AUDIO_TRAILER_SIZE / stride);
147 
148 	/* do the copy */
149 	if (oldptr + len >= runtime->buffer_size) {
150 		unsigned int cnt = runtime->buffer_size - oldptr;
151 
152 		memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
153 		memcpy(runtime->dma_area, (cp + cnt * stride),
154 					(len * stride - cnt * stride));
155 	} else
156 		memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
157 
158 	/* update the statas */
159 	snd_pcm_stream_lock(pa->capture_pcm_substream);
160 	pa->rcv_position	+= len;
161 	if (pa->rcv_position >= runtime->buffer_size)
162 		pa->rcv_position -= runtime->buffer_size;
163 
164 	pa->copied_position += (len);
165 	if (pa->copied_position >= runtime->period_size) {
166 		pa->copied_position -= runtime->period_size;
167 		*period_elapsed = 1;
168 	}
169 	snd_pcm_stream_unlock(pa->capture_pcm_substream);
170 }
171 
complete_handler_audio(struct urb * urb)172 static void complete_handler_audio(struct urb *urb)
173 {
174 	struct poseidon_audio *pa = urb->context;
175 	struct snd_pcm_substream *substream = pa->capture_pcm_substream;
176 	int    period_elapsed = 0;
177 	int    ret;
178 
179 	if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
180 		return;
181 
182 	if (urb->status != 0) {
183 		/*if (urb->status == -ESHUTDOWN)*/
184 			return;
185 	}
186 
187 	if (substream) {
188 		if (urb->actual_length) {
189 			handle_audio_data(urb, &period_elapsed);
190 			if (period_elapsed)
191 				snd_pcm_period_elapsed(substream);
192 		}
193 	}
194 
195 	ret = usb_submit_urb(urb, GFP_ATOMIC);
196 	if (ret < 0)
197 		log("audio urb failed (errcod = %i)", ret);
198 	return;
199 }
200 
fire_audio_urb(struct poseidon * p)201 static int fire_audio_urb(struct poseidon *p)
202 {
203 	int i, ret = 0;
204 	struct poseidon_audio *pa = &p->audio;
205 
206 	alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
207 			p->udev, AUDIO_EP,
208 			AUDIO_BUF_SIZE, GFP_ATOMIC,
209 			complete_handler_audio, pa);
210 
211 	for (i = 0; i < AUDIO_BUFS; i++) {
212 		ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
213 		if (ret)
214 			log("urb err : %d", ret);
215 	}
216 	log();
217 	return ret;
218 }
219 
snd_pd_capture_trigger(struct snd_pcm_substream * substream,int cmd)220 static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
221 {
222 	struct poseidon *p = snd_pcm_substream_chip(substream);
223 	struct poseidon_audio *pa = &p->audio;
224 
225 	if (debug_mode)
226 		log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
227 
228 	switch (cmd) {
229 	case SNDRV_PCM_TRIGGER_RESUME:
230 	case SNDRV_PCM_TRIGGER_START:
231 		if (pa->capture_stream == STREAM_ON)
232 			return 0;
233 
234 		pa->rcv_position = pa->copied_position = 0;
235 		pa->capture_stream = STREAM_ON;
236 
237 		if (in_hibernation(p))
238 			return 0;
239 		fire_audio_urb(p);
240 		return 0;
241 
242 	case SNDRV_PCM_TRIGGER_SUSPEND:
243 		pa->capture_stream = STREAM_SUSPEND;
244 		return 0;
245 	case SNDRV_PCM_TRIGGER_STOP:
246 		pa->capture_stream = STREAM_OFF;
247 		return 0;
248 	default:
249 		return -EINVAL;
250 	}
251 }
252 
253 static snd_pcm_uframes_t
snd_pd_capture_pointer(struct snd_pcm_substream * substream)254 snd_pd_capture_pointer(struct snd_pcm_substream *substream)
255 {
256 	struct poseidon *p = snd_pcm_substream_chip(substream);
257 	struct poseidon_audio *pa = &p->audio;
258 	return pa->rcv_position;
259 }
260 
snd_pcm_pd_get_page(struct snd_pcm_substream * subs,unsigned long offset)261 static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
262 					     unsigned long offset)
263 {
264 	void *pageptr = subs->runtime->dma_area + offset;
265 	return vmalloc_to_page(pageptr);
266 }
267 
268 static struct snd_pcm_ops pcm_capture_ops = {
269 	.open      = snd_pd_capture_open,
270 	.close     = snd_pd_pcm_close,
271 	.ioctl     = snd_pcm_lib_ioctl,
272 	.hw_params = snd_pd_hw_capture_params,
273 	.hw_free   = snd_pd_hw_capture_free,
274 	.prepare   = snd_pd_prepare,
275 	.trigger   = snd_pd_capture_trigger,
276 	.pointer   = snd_pd_capture_pointer,
277 	.page      = snd_pcm_pd_get_page,
278 };
279 
280 #ifdef CONFIG_PM
pm_alsa_suspend(struct poseidon * p)281 int pm_alsa_suspend(struct poseidon *p)
282 {
283 	logpm(p);
284 	audio_buf_free(p);
285 	return 0;
286 }
287 
pm_alsa_resume(struct poseidon * p)288 int pm_alsa_resume(struct poseidon *p)
289 {
290 	logpm(p);
291 	fire_audio_urb(p);
292 	return 0;
293 }
294 #endif
295 
poseidon_audio_init(struct poseidon * p)296 int poseidon_audio_init(struct poseidon *p)
297 {
298 	struct poseidon_audio *pa = &p->audio;
299 	struct snd_card *card;
300 	struct snd_pcm *pcm;
301 	int ret;
302 
303 	ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
304 	if (ret != 0)
305 		return ret;
306 
307 	ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
308 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
309 	pcm->info_flags   = 0;
310 	pcm->private_data = p;
311 	strcpy(pcm->name, "poseidon audio capture");
312 
313 	strcpy(card->driver, "ALSA driver");
314 	strcpy(card->shortname, "poseidon Audio");
315 	strcpy(card->longname, "poseidon ALSA Audio");
316 
317 	if (snd_card_register(card)) {
318 		snd_card_free(card);
319 		return -ENOMEM;
320 	}
321 	pa->card = card;
322 	return 0;
323 }
324 
poseidon_audio_free(struct poseidon * p)325 int poseidon_audio_free(struct poseidon *p)
326 {
327 	struct poseidon_audio *pa = &p->audio;
328 
329 	if (pa->card)
330 		snd_card_free(pa->card);
331 	return 0;
332 }
333