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