Commit | Line | Data |
---|---|---|
faa4fd2a BC |
1 | /* |
2 | * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com | |
3 | * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | #include <linux/mempool.h> | |
22 | #include <linux/poll.h> | |
23 | #include <linux/kthread.h> | |
24 | #include <linux/freezer.h> | |
25 | ||
26 | #include <sound/core.h> | |
27 | #include <sound/initval.h> | |
28 | #include <sound/pcm.h> | |
29 | #include <sound/control.h> | |
30 | ||
31 | #include "solo6010.h" | |
32 | #include "solo6010-tw28.h" | |
33 | ||
34 | #define G723_INTR_ORDER 0 | |
35 | #define G723_FDMA_PAGES 32 | |
36 | #define G723_PERIOD_BYTES 48 | |
37 | #define G723_PERIOD_BLOCK 1024 | |
38 | #define G723_FRAMES_PER_PAGE 48 | |
39 | ||
40 | /* Sets up channels 16-19 for decoding and 0-15 for encoding */ | |
41 | #define OUTMODE_MASK 0x300 | |
42 | ||
43 | #define SAMPLERATE 8000 | |
44 | #define BITRATE 25 | |
45 | ||
46 | /* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page | |
47 | * is broken down to 20 * 48 byte regions (one for each channel possible) | |
48 | * with the rest of the page being dummy data. */ | |
49 | #define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) | |
50 | #define IRQ_PAGES 4 // 0 - 4 | |
51 | #define PERIODS_MIN (1 << IRQ_PAGES) | |
52 | #define PERIODS_MAX G723_FDMA_PAGES | |
53 | ||
54 | struct solo_snd_pcm { | |
55 | int on; | |
56 | spinlock_t lock; | |
57 | struct solo6010_dev *solo_dev; | |
58 | unsigned char g723_buf[G723_PERIOD_BYTES]; | |
59 | }; | |
60 | ||
61 | static void solo_g723_config(struct solo6010_dev *solo_dev) | |
62 | { | |
63 | int clk_div; | |
64 | ||
65 | clk_div = SOLO_CLOCK_MHZ / (SAMPLERATE * (BITRATE * 2) * 2); | |
66 | ||
67 | solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, | |
68 | SOLO_AUDIO_BITRATE(BITRATE) | | |
69 | SOLO_AUDIO_CLK_DIV(clk_div)); | |
70 | ||
71 | solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, | |
72 | SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) | | |
73 | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) | | |
74 | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); | |
75 | ||
76 | solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, | |
77 | SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE | | |
78 | SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK)); | |
79 | } | |
80 | ||
81 | void solo_g723_isr(struct solo6010_dev *solo_dev) | |
82 | { | |
83 | struct snd_pcm_str *pstr = | |
84 | &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; | |
85 | struct snd_pcm_substream *ss; | |
86 | struct solo_snd_pcm *solo_pcm; | |
87 | ||
88 | solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723); | |
89 | ||
90 | for (ss = pstr->substream; ss != NULL; ss = ss->next) { | |
91 | if (snd_pcm_substream_chip(ss) == NULL) | |
92 | continue; | |
93 | ||
94 | /* This means open() hasn't been called on this one */ | |
95 | if (snd_pcm_substream_chip(ss) == solo_dev) | |
96 | continue; | |
97 | ||
98 | /* Haven't triggered a start yet */ | |
99 | solo_pcm = snd_pcm_substream_chip(ss); | |
100 | if (!solo_pcm->on) | |
101 | continue; | |
102 | ||
103 | snd_pcm_period_elapsed(ss); | |
104 | } | |
105 | } | |
106 | ||
107 | static int snd_solo_hw_params(struct snd_pcm_substream *ss, | |
108 | struct snd_pcm_hw_params *hw_params) | |
109 | { | |
110 | return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); | |
111 | } | |
112 | ||
113 | static int snd_solo_hw_free(struct snd_pcm_substream *ss) | |
114 | { | |
115 | return snd_pcm_lib_free_pages(ss); | |
116 | } | |
117 | ||
118 | static struct snd_pcm_hardware snd_solo_pcm_hw = { | |
119 | .info = (SNDRV_PCM_INFO_MMAP | | |
120 | SNDRV_PCM_INFO_INTERLEAVED | | |
121 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
122 | SNDRV_PCM_INFO_MMAP_VALID), | |
123 | .formats = SNDRV_PCM_FMTBIT_U8, | |
124 | .rates = SNDRV_PCM_RATE_8000, | |
125 | .rate_min = 8000, | |
126 | .rate_max = 8000, | |
127 | .channels_min = 1, | |
128 | .channels_max = 1, | |
129 | .buffer_bytes_max = MAX_BUFFER, | |
130 | .period_bytes_min = G723_PERIOD_BYTES, | |
131 | .period_bytes_max = G723_PERIOD_BYTES, | |
132 | .periods_min = PERIODS_MIN, | |
133 | .periods_max = PERIODS_MAX, | |
134 | }; | |
135 | ||
136 | static int snd_solo_pcm_open(struct snd_pcm_substream *ss) | |
137 | { | |
138 | struct solo6010_dev *solo_dev = snd_pcm_substream_chip(ss); | |
139 | struct solo_snd_pcm *solo_pcm; | |
140 | ||
141 | solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); | |
142 | if (solo_pcm == NULL) | |
143 | return -ENOMEM; | |
144 | ||
145 | spin_lock_init(&solo_pcm->lock); | |
146 | solo_pcm->solo_dev = solo_dev; | |
147 | ss->runtime->hw = snd_solo_pcm_hw; | |
148 | ||
149 | snd_pcm_substream_chip(ss) = solo_pcm; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static int snd_solo_pcm_close(struct snd_pcm_substream *ss) | |
155 | { | |
156 | struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); | |
157 | ||
158 | snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; | |
159 | kfree(solo_pcm); | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) | |
165 | { | |
166 | struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); | |
167 | struct solo6010_dev *solo_dev = solo_pcm->solo_dev; | |
168 | int ret = 0; | |
169 | ||
170 | spin_lock(&solo_pcm->lock); | |
171 | ||
172 | switch (cmd) { | |
173 | case SNDRV_PCM_TRIGGER_START: | |
174 | if (solo_pcm->on == 0) { | |
175 | /* If this is the first user, switch on interrupts */ | |
176 | if (atomic_inc_return(&solo_dev->snd_users) == 1) | |
177 | solo6010_irq_on(solo_dev, SOLO_IRQ_G723); | |
178 | solo_pcm->on = 1; | |
179 | } | |
180 | break; | |
181 | case SNDRV_PCM_TRIGGER_STOP: | |
182 | if (solo_pcm->on) { | |
183 | /* If this was our last user, switch them off */ | |
184 | if (atomic_dec_return(&solo_dev->snd_users) == 0) | |
185 | solo6010_irq_off(solo_dev, SOLO_IRQ_G723); | |
186 | solo_pcm->on = 0; | |
187 | } | |
188 | break; | |
189 | default: | |
190 | ret = -EINVAL; | |
191 | } | |
192 | ||
193 | spin_unlock(&solo_pcm->lock); | |
194 | ||
195 | return ret; | |
196 | } | |
197 | ||
198 | static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) | |
199 | { | |
200 | return 0; | |
201 | } | |
202 | ||
203 | static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) | |
204 | { | |
205 | struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); | |
206 | struct solo6010_dev *solo_dev = solo_pcm->solo_dev; | |
207 | snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; | |
208 | ||
209 | return idx * G723_FRAMES_PER_PAGE; | |
210 | } | |
211 | ||
212 | static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, | |
213 | snd_pcm_uframes_t pos, void __user *dst, | |
214 | snd_pcm_uframes_t count) | |
215 | { | |
216 | struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); | |
217 | struct solo6010_dev *solo_dev = solo_pcm->solo_dev; | |
218 | int err, i; | |
219 | ||
220 | for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { | |
221 | int page = (pos / G723_FRAMES_PER_PAGE) + i; | |
222 | ||
223 | err = solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_G723E, 0, | |
224 | solo_pcm->g723_buf, | |
225 | SOLO_G723_EXT_ADDR(solo_dev) + | |
226 | (page * G723_PERIOD_BLOCK) + | |
227 | (ss->number * G723_PERIOD_BYTES), | |
228 | G723_PERIOD_BYTES); | |
229 | if (err) | |
230 | return err; | |
231 | ||
232 | err = copy_to_user(dst + (i * G723_PERIOD_BYTES), | |
233 | solo_pcm->g723_buf, G723_PERIOD_BYTES); | |
234 | ||
235 | if (err) | |
6a4ca039 | 236 | return -EFAULT; |
faa4fd2a BC |
237 | } |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
242 | static struct snd_pcm_ops snd_solo_pcm_ops = { | |
243 | .open = snd_solo_pcm_open, | |
244 | .close = snd_solo_pcm_close, | |
245 | .ioctl = snd_pcm_lib_ioctl, | |
246 | .hw_params = snd_solo_hw_params, | |
247 | .hw_free = snd_solo_hw_free, | |
248 | .prepare = snd_solo_pcm_prepare, | |
249 | .trigger = snd_solo_pcm_trigger, | |
250 | .pointer = snd_solo_pcm_pointer, | |
251 | .copy = snd_solo_pcm_copy, | |
252 | }; | |
253 | ||
254 | static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, | |
255 | struct snd_ctl_elem_info *info) | |
256 | { | |
257 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
258 | info->count = 1; | |
259 | info->value.integer.min = 0; | |
260 | info->value.integer.max = 15; | |
261 | info->value.integer.step = 1; | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
266 | static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, | |
267 | struct snd_ctl_elem_value *value) | |
268 | { | |
269 | struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol); | |
270 | u8 ch = value->id.numid - 1; | |
271 | ||
272 | value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); | |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
277 | static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, | |
278 | struct snd_ctl_elem_value *value) | |
279 | { | |
280 | struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol); | |
281 | u8 ch = value->id.numid - 1; | |
282 | u8 old_val; | |
283 | ||
284 | old_val = tw28_get_audio_gain(solo_dev, ch); | |
285 | if (old_val == value->value.integer.value[0]) | |
286 | return 0; | |
287 | ||
288 | tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); | |
289 | ||
290 | return 1; | |
291 | } | |
292 | ||
293 | static struct snd_kcontrol_new snd_solo_capture_volume = { | |
294 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
295 | .name = "Capture Volume", | |
296 | .info = snd_solo_capture_volume_info, | |
297 | .get = snd_solo_capture_volume_get, | |
298 | .put = snd_solo_capture_volume_put, | |
299 | }; | |
300 | ||
301 | static int solo_snd_pcm_init(struct solo6010_dev *solo_dev) | |
302 | { | |
303 | struct snd_card *card = solo_dev->snd_card; | |
304 | struct snd_pcm *pcm; | |
305 | struct snd_pcm_substream *ss; | |
306 | int ret; | |
307 | int i; | |
308 | ||
309 | ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, | |
310 | &pcm); | |
311 | if (ret < 0) | |
312 | return ret; | |
313 | ||
314 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
315 | &snd_solo_pcm_ops); | |
316 | ||
317 | snd_pcm_chip(pcm) = solo_dev; | |
318 | pcm->info_flags = 0; | |
319 | strcpy(pcm->name, card->shortname); | |
320 | ||
321 | for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; | |
322 | ss; ss = ss->next, i++) | |
323 | sprintf(ss->name, "Camera #%d Audio", i); | |
324 | ||
325 | ret = snd_pcm_lib_preallocate_pages_for_all(pcm, | |
326 | SNDRV_DMA_TYPE_CONTINUOUS, | |
327 | snd_dma_continuous_data(GFP_KERNEL), | |
328 | MAX_BUFFER, MAX_BUFFER); | |
329 | if (ret < 0) | |
330 | return ret; | |
331 | ||
332 | solo_dev->snd_pcm = pcm; | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | int solo_g723_init(struct solo6010_dev *solo_dev) | |
338 | { | |
339 | static struct snd_device_ops ops = { NULL }; | |
340 | struct snd_card *card; | |
341 | struct snd_kcontrol_new kctl; | |
342 | char name[32]; | |
343 | int ret; | |
344 | ||
345 | atomic_set(&solo_dev->snd_users, 0); | |
346 | ||
347 | /* Allows for easier mapping between video and audio */ | |
348 | sprintf(name, "Softlogic%d", solo_dev->vfd->num); | |
349 | ||
350 | ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, | |
351 | &solo_dev->snd_card); | |
352 | if (ret < 0) | |
353 | return ret; | |
354 | ||
355 | card = solo_dev->snd_card; | |
356 | ||
357 | strcpy(card->driver, SOLO6010_NAME); | |
358 | strcpy(card->shortname, "SOLO-6010 Audio"); | |
359 | sprintf(card->longname, "%s on %s IRQ %d", card->shortname, | |
360 | pci_name(solo_dev->pdev), solo_dev->pdev->irq); | |
361 | snd_card_set_dev(card, &solo_dev->pdev->dev); | |
362 | ||
363 | ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); | |
364 | if (ret < 0) | |
365 | goto snd_error; | |
366 | ||
367 | /* Mixer controls */ | |
368 | strcpy(card->mixername, "SOLO-6010"); | |
369 | kctl = snd_solo_capture_volume; | |
370 | kctl.count = solo_dev->nr_chans; | |
371 | ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); | |
372 | if (ret < 0) | |
373 | return ret; | |
374 | ||
375 | if ((ret = solo_snd_pcm_init(solo_dev)) < 0) | |
376 | goto snd_error; | |
377 | ||
378 | if ((ret = snd_card_register(card)) < 0) | |
379 | goto snd_error; | |
380 | ||
381 | solo_g723_config(solo_dev); | |
382 | ||
383 | dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); | |
384 | ||
385 | return 0; | |
386 | ||
387 | snd_error: | |
388 | snd_card_free(card); | |
389 | return ret; | |
390 | } | |
391 | ||
392 | void solo_g723_exit(struct solo6010_dev *solo_dev) | |
393 | { | |
394 | solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); | |
395 | solo6010_irq_off(solo_dev, SOLO_IRQ_G723); | |
396 | ||
397 | snd_card_free(solo_dev->snd_card); | |
398 | } |