]>
Commit | Line | Data |
---|---|---|
704a84cc EG |
1 | /* |
2 | * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar | |
3 | * | |
4 | * Based on the audio support from the tw6869 driver: | |
5 | * Copyright 2015 www.starterkit.ru <info@starterkit.ru> | |
6 | * | |
7 | * Based on: | |
8 | * Driver for Intersil|Techwell TW6869 based DVR cards | |
9 | * (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China] | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify it | |
12 | * under the terms of version 2 of the GNU General Public License | |
13 | * as published by the Free Software Foundation. | |
14 | */ | |
15 | ||
16 | #include <linux/types.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/module.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/kmod.h> | |
21 | #include <linux/mutex.h> | |
22 | #include <linux/pci.h> | |
23 | #include <linux/delay.h> | |
24 | ||
25 | #include <sound/core.h> | |
26 | #include <sound/initval.h> | |
27 | #include <sound/pcm.h> | |
28 | #include <sound/control.h> | |
29 | #include "tw686x.h" | |
30 | #include "tw686x-regs.h" | |
31 | ||
32 | #define AUDIO_CHANNEL_OFFSET 8 | |
33 | ||
34 | void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests, | |
35 | unsigned int pb_status) | |
36 | { | |
37 | unsigned long flags; | |
38 | unsigned int ch, pb; | |
39 | ||
40 | for_each_set_bit(ch, &requests, max_channels(dev)) { | |
41 | struct tw686x_audio_channel *ac = &dev->audio_channels[ch]; | |
42 | struct tw686x_audio_buf *done = NULL; | |
43 | struct tw686x_audio_buf *next = NULL; | |
44 | struct tw686x_dma_desc *desc; | |
45 | ||
46 | pb = !!(pb_status & BIT(AUDIO_CHANNEL_OFFSET + ch)); | |
47 | ||
48 | spin_lock_irqsave(&ac->lock, flags); | |
49 | ||
50 | /* Sanity check */ | |
51 | if (!ac->ss || !ac->curr_bufs[0] || !ac->curr_bufs[1]) { | |
52 | spin_unlock_irqrestore(&ac->lock, flags); | |
53 | continue; | |
54 | } | |
55 | ||
56 | if (!list_empty(&ac->buf_list)) { | |
57 | next = list_first_entry(&ac->buf_list, | |
58 | struct tw686x_audio_buf, list); | |
59 | list_move_tail(&next->list, &ac->buf_list); | |
60 | done = ac->curr_bufs[!pb]; | |
61 | ac->curr_bufs[pb] = next; | |
62 | } | |
63 | spin_unlock_irqrestore(&ac->lock, flags); | |
64 | ||
65 | desc = &ac->dma_descs[pb]; | |
66 | if (done && next && desc->virt) { | |
67 | memcpy(done->virt, desc->virt, desc->size); | |
68 | ac->ptr = done->dma - ac->buf[0].dma; | |
69 | snd_pcm_period_elapsed(ac->ss); | |
70 | } | |
71 | } | |
72 | } | |
73 | ||
74 | static int tw686x_pcm_hw_params(struct snd_pcm_substream *ss, | |
75 | struct snd_pcm_hw_params *hw_params) | |
76 | { | |
77 | return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); | |
78 | } | |
79 | ||
80 | static int tw686x_pcm_hw_free(struct snd_pcm_substream *ss) | |
81 | { | |
82 | return snd_pcm_lib_free_pages(ss); | |
83 | } | |
84 | ||
85 | /* | |
86 | * The audio device rate is global and shared among all | |
87 | * capture channels. The driver makes no effort to prevent | |
88 | * rate modifications. User is free change the rate, but it | |
89 | * means changing the rate for all capture sub-devices. | |
90 | */ | |
91 | static const struct snd_pcm_hardware tw686x_capture_hw = { | |
92 | .info = (SNDRV_PCM_INFO_MMAP | | |
93 | SNDRV_PCM_INFO_INTERLEAVED | | |
94 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
95 | SNDRV_PCM_INFO_MMAP_VALID), | |
96 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
97 | .rates = SNDRV_PCM_RATE_8000_48000, | |
98 | .rate_min = 8000, | |
99 | .rate_max = 48000, | |
100 | .channels_min = 1, | |
101 | .channels_max = 1, | |
102 | .buffer_bytes_max = TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ, | |
103 | .period_bytes_min = TW686X_AUDIO_PAGE_SZ, | |
104 | .period_bytes_max = TW686X_AUDIO_PAGE_SZ, | |
105 | .periods_min = TW686X_AUDIO_PERIODS_MIN, | |
106 | .periods_max = TW686X_AUDIO_PERIODS_MAX, | |
107 | }; | |
108 | ||
109 | static int tw686x_pcm_open(struct snd_pcm_substream *ss) | |
110 | { | |
111 | struct tw686x_dev *dev = snd_pcm_substream_chip(ss); | |
112 | struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; | |
113 | struct snd_pcm_runtime *rt = ss->runtime; | |
114 | int err; | |
115 | ||
116 | ac->ss = ss; | |
117 | rt->hw = tw686x_capture_hw; | |
118 | ||
119 | err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); | |
120 | if (err < 0) | |
121 | return err; | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | static int tw686x_pcm_close(struct snd_pcm_substream *ss) | |
127 | { | |
128 | struct tw686x_dev *dev = snd_pcm_substream_chip(ss); | |
129 | struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; | |
130 | ||
131 | ac->ss = NULL; | |
132 | return 0; | |
133 | } | |
134 | ||
135 | static int tw686x_pcm_prepare(struct snd_pcm_substream *ss) | |
136 | { | |
137 | struct tw686x_dev *dev = snd_pcm_substream_chip(ss); | |
138 | struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; | |
139 | struct snd_pcm_runtime *rt = ss->runtime; | |
140 | unsigned int period_size = snd_pcm_lib_period_bytes(ss); | |
141 | struct tw686x_audio_buf *p_buf, *b_buf; | |
142 | unsigned long flags; | |
143 | int i; | |
144 | ||
145 | spin_lock_irqsave(&dev->lock, flags); | |
146 | tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); | |
147 | spin_unlock_irqrestore(&dev->lock, flags); | |
148 | ||
149 | if (dev->audio_rate != rt->rate) { | |
150 | u32 reg; | |
151 | ||
152 | dev->audio_rate = rt->rate; | |
153 | reg = ((125000000 / rt->rate) << 16) + | |
154 | ((125000000 % rt->rate) << 16) / rt->rate; | |
155 | ||
156 | reg_write(dev, AUDIO_CONTROL2, reg); | |
157 | } | |
158 | ||
159 | if (period_size != TW686X_AUDIO_PAGE_SZ || | |
160 | rt->periods < TW686X_AUDIO_PERIODS_MIN || | |
161 | rt->periods > TW686X_AUDIO_PERIODS_MAX) { | |
162 | return -EINVAL; | |
163 | } | |
164 | ||
165 | spin_lock_irqsave(&ac->lock, flags); | |
166 | INIT_LIST_HEAD(&ac->buf_list); | |
167 | ||
168 | for (i = 0; i < rt->periods; i++) { | |
169 | ac->buf[i].dma = rt->dma_addr + period_size * i; | |
170 | ac->buf[i].virt = rt->dma_area + period_size * i; | |
171 | INIT_LIST_HEAD(&ac->buf[i].list); | |
172 | list_add_tail(&ac->buf[i].list, &ac->buf_list); | |
173 | } | |
174 | ||
175 | p_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); | |
176 | list_move_tail(&p_buf->list, &ac->buf_list); | |
177 | ||
178 | b_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); | |
179 | list_move_tail(&b_buf->list, &ac->buf_list); | |
180 | ||
181 | ac->curr_bufs[0] = p_buf; | |
182 | ac->curr_bufs[1] = b_buf; | |
183 | ac->ptr = 0; | |
184 | spin_unlock_irqrestore(&ac->lock, flags); | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) | |
190 | { | |
191 | struct tw686x_dev *dev = snd_pcm_substream_chip(ss); | |
192 | struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; | |
193 | unsigned long flags; | |
194 | int err = 0; | |
195 | ||
196 | switch (cmd) { | |
197 | case SNDRV_PCM_TRIGGER_START: | |
198 | if (ac->curr_bufs[0] && ac->curr_bufs[1]) { | |
199 | spin_lock_irqsave(&dev->lock, flags); | |
200 | tw686x_enable_channel(dev, | |
201 | AUDIO_CHANNEL_OFFSET + ac->ch); | |
202 | spin_unlock_irqrestore(&dev->lock, flags); | |
203 | ||
204 | mod_timer(&dev->dma_delay_timer, | |
205 | jiffies + msecs_to_jiffies(100)); | |
206 | } else { | |
207 | err = -EIO; | |
208 | } | |
209 | break; | |
210 | case SNDRV_PCM_TRIGGER_STOP: | |
211 | spin_lock_irqsave(&dev->lock, flags); | |
212 | tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); | |
213 | spin_unlock_irqrestore(&dev->lock, flags); | |
214 | ||
215 | spin_lock_irqsave(&ac->lock, flags); | |
216 | ac->curr_bufs[0] = NULL; | |
217 | ac->curr_bufs[1] = NULL; | |
218 | spin_unlock_irqrestore(&ac->lock, flags); | |
219 | break; | |
220 | default: | |
221 | err = -EINVAL; | |
222 | } | |
223 | return err; | |
224 | } | |
225 | ||
226 | static snd_pcm_uframes_t tw686x_pcm_pointer(struct snd_pcm_substream *ss) | |
227 | { | |
228 | struct tw686x_dev *dev = snd_pcm_substream_chip(ss); | |
229 | struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; | |
230 | ||
231 | return bytes_to_frames(ss->runtime, ac->ptr); | |
232 | } | |
233 | ||
234 | static struct snd_pcm_ops tw686x_pcm_ops = { | |
235 | .open = tw686x_pcm_open, | |
236 | .close = tw686x_pcm_close, | |
237 | .ioctl = snd_pcm_lib_ioctl, | |
238 | .hw_params = tw686x_pcm_hw_params, | |
239 | .hw_free = tw686x_pcm_hw_free, | |
240 | .prepare = tw686x_pcm_prepare, | |
241 | .trigger = tw686x_pcm_trigger, | |
242 | .pointer = tw686x_pcm_pointer, | |
243 | }; | |
244 | ||
245 | static int tw686x_snd_pcm_init(struct tw686x_dev *dev) | |
246 | { | |
247 | struct snd_card *card = dev->snd_card; | |
248 | struct snd_pcm *pcm; | |
249 | struct snd_pcm_substream *ss; | |
250 | unsigned int i; | |
251 | int err; | |
252 | ||
253 | err = snd_pcm_new(card, card->driver, 0, 0, max_channels(dev), &pcm); | |
254 | if (err < 0) | |
255 | return err; | |
256 | ||
257 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tw686x_pcm_ops); | |
258 | snd_pcm_chip(pcm) = dev; | |
259 | pcm->info_flags = 0; | |
260 | strlcpy(pcm->name, "tw686x PCM", sizeof(pcm->name)); | |
261 | ||
262 | for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; | |
263 | ss; ss = ss->next, i++) | |
264 | snprintf(ss->name, sizeof(ss->name), "vch%u audio", i); | |
265 | ||
266 | return snd_pcm_lib_preallocate_pages_for_all(pcm, | |
267 | SNDRV_DMA_TYPE_DEV, | |
268 | snd_dma_pci_data(dev->pci_dev), | |
269 | TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ, | |
270 | TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ); | |
271 | } | |
272 | ||
273 | static void tw686x_audio_dma_free(struct tw686x_dev *dev, | |
274 | struct tw686x_audio_channel *ac) | |
275 | { | |
276 | int pb; | |
277 | ||
278 | for (pb = 0; pb < 2; pb++) { | |
279 | if (!ac->dma_descs[pb].virt) | |
280 | continue; | |
281 | pci_free_consistent(dev->pci_dev, ac->dma_descs[pb].size, | |
282 | ac->dma_descs[pb].virt, | |
283 | ac->dma_descs[pb].phys); | |
284 | ac->dma_descs[pb].virt = NULL; | |
285 | } | |
286 | } | |
287 | ||
288 | static int tw686x_audio_dma_alloc(struct tw686x_dev *dev, | |
289 | struct tw686x_audio_channel *ac) | |
290 | { | |
291 | int pb; | |
292 | ||
293 | for (pb = 0; pb < 2; pb++) { | |
294 | u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch]; | |
295 | void *virt; | |
296 | ||
297 | virt = pci_alloc_consistent(dev->pci_dev, TW686X_AUDIO_PAGE_SZ, | |
298 | &ac->dma_descs[pb].phys); | |
299 | if (!virt) { | |
300 | dev_err(&dev->pci_dev->dev, | |
301 | "dma%d: unable to allocate audio DMA %s-buffer\n", | |
302 | ac->ch, pb ? "B" : "P"); | |
303 | return -ENOMEM; | |
304 | } | |
305 | ac->dma_descs[pb].virt = virt; | |
306 | ac->dma_descs[pb].size = TW686X_AUDIO_PAGE_SZ; | |
307 | reg_write(dev, reg, ac->dma_descs[pb].phys); | |
308 | } | |
309 | return 0; | |
310 | } | |
311 | ||
312 | void tw686x_audio_free(struct tw686x_dev *dev) | |
313 | { | |
314 | unsigned long flags; | |
315 | u32 dma_ch_mask; | |
316 | u32 dma_cmd; | |
317 | ||
318 | spin_lock_irqsave(&dev->lock, flags); | |
319 | dma_cmd = reg_read(dev, DMA_CMD); | |
320 | dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE); | |
321 | reg_write(dev, DMA_CMD, dma_cmd & ~0xff00); | |
322 | reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask & ~0xff00); | |
323 | spin_unlock_irqrestore(&dev->lock, flags); | |
324 | ||
325 | if (!dev->snd_card) | |
326 | return; | |
327 | snd_card_free(dev->snd_card); | |
328 | dev->snd_card = NULL; | |
329 | } | |
330 | ||
331 | int tw686x_audio_init(struct tw686x_dev *dev) | |
332 | { | |
333 | struct pci_dev *pci_dev = dev->pci_dev; | |
334 | struct snd_card *card; | |
335 | int err, ch; | |
336 | ||
337 | /* | |
338 | * AUDIO_CONTROL1 | |
339 | * DMA byte length [31:19] = 4096 (i.e. ALSA period) | |
340 | * External audio enable [0] = enabled | |
341 | */ | |
342 | reg_write(dev, AUDIO_CONTROL1, 0x80000001); | |
343 | ||
344 | err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1, | |
345 | SNDRV_DEFAULT_STR1, | |
346 | THIS_MODULE, 0, &card); | |
347 | if (err < 0) | |
348 | return err; | |
349 | ||
350 | dev->snd_card = card; | |
351 | strlcpy(card->driver, "tw686x", sizeof(card->driver)); | |
352 | strlcpy(card->shortname, "tw686x", sizeof(card->shortname)); | |
353 | strlcpy(card->longname, pci_name(pci_dev), sizeof(card->longname)); | |
354 | snd_card_set_dev(card, &pci_dev->dev); | |
355 | ||
356 | for (ch = 0; ch < max_channels(dev); ch++) { | |
357 | struct tw686x_audio_channel *ac; | |
358 | ||
359 | ac = &dev->audio_channels[ch]; | |
360 | spin_lock_init(&ac->lock); | |
361 | ac->dev = dev; | |
362 | ac->ch = ch; | |
363 | ||
364 | err = tw686x_audio_dma_alloc(dev, ac); | |
365 | if (err < 0) | |
366 | goto err_cleanup; | |
367 | } | |
368 | ||
369 | err = tw686x_snd_pcm_init(dev); | |
370 | if (err < 0) | |
371 | goto err_cleanup; | |
372 | ||
373 | err = snd_card_register(card); | |
374 | if (!err) | |
375 | return 0; | |
376 | ||
377 | err_cleanup: | |
378 | for (ch = 0; ch < max_channels(dev); ch++) { | |
379 | if (!dev->audio_channels[ch].dev) | |
380 | continue; | |
381 | tw686x_audio_dma_free(dev, &dev->audio_channels[ch]); | |
382 | } | |
383 | snd_card_free(card); | |
384 | dev->snd_card = NULL; | |
385 | return err; | |
386 | } |