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
21static 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
28static 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
55static 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
75static 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
87static 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
107static 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
120static 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
129static int snd_pd_prepare(struct snd_pcm_substream *substream)
130{
131	return 0;
132}
133
134#define AUDIO_TRAILER_SIZE	(16)
135static 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
172static 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
201static 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
220static 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
253static snd_pcm_uframes_t
254snd_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
261static 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
268static 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
281int pm_alsa_suspend(struct poseidon *p)
282{
283	logpm(p);
284	audio_buf_free(p);
285	return 0;
286}
287
288int pm_alsa_resume(struct poseidon *p)
289{
290	logpm(p);
291	fire_audio_urb(p);
292	return 0;
293}
294#endif
295
296int 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_new(&p->interface->dev, -1, "Telegent",
304			   THIS_MODULE, 0, &card);
305	if (ret != 0)
306		return ret;
307
308	ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
309	if (ret < 0) {
310		snd_card_free(card);
311		return ret;
312	}
313	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
314	pcm->info_flags   = 0;
315	pcm->private_data = p;
316	strcpy(pcm->name, "poseidon audio capture");
317
318	strcpy(card->driver, "ALSA driver");
319	strcpy(card->shortname, "poseidon Audio");
320	strcpy(card->longname, "poseidon ALSA Audio");
321
322	if (snd_card_register(card)) {
323		snd_card_free(card);
324		return -ENOMEM;
325	}
326	pa->card = card;
327	return 0;
328}
329
330int poseidon_audio_free(struct poseidon *p)
331{
332	struct poseidon_audio *pa = &p->audio;
333
334	if (pa->card)
335		snd_card_free(pa->card);
336	return 0;
337}
338