source: mainline/uspace/drv/audio/sb16/dsp.c@ 039337e8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 039337e8 was 346643c, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

audio: Use enum for sample format.

  • Property mode set to 100644
File size: 10.7 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28/** @addtogroup drvaudiosb16
29 * @{
30 */
31/** @file
32 * @brief DSP helper functions implementation
33 */
34
35#include <devman.h>
36#include <device/hw_res.h>
37#include <libarch/ddi.h>
38#include <libarch/barrier.h>
39#include <str_error.h>
40#include <bool.h>
41
42#include "dma.h"
43#include "ddf_log.h"
44#include "dsp_commands.h"
45#include "dsp.h"
46
47#define BUFFER_ID 1
48#define MAX_BUFFER_SIZE (PAGE_SIZE)
49
50#ifndef DSP_RETRY_COUNT
51#define DSP_RETRY_COUNT 100
52#endif
53
54#define DSP_RESET_RESPONSE 0xaa
55
56#define AUTO_DMA_MODE
57
58static inline int sb_dsp_read(sb_dsp_t *dsp, uint8_t *data)
59{
60 assert(data);
61 assert(dsp);
62 uint8_t status;
63 size_t attempts = DSP_RETRY_COUNT;
64 do {
65 status = pio_read_8(&dsp->regs->dsp_read_status);
66 } while (--attempts && ((status & DSP_READ_READY) == 0));
67
68 if ((status & DSP_READ_READY) == 0)
69 return EIO;
70
71 *data = pio_read_8(&dsp->regs->dsp_data_read);
72 return EOK;
73}
74
75static inline int sb_dsp_write(sb_dsp_t *dsp, uint8_t data)
76{
77 assert(dsp);
78 uint8_t status;
79 size_t attempts = DSP_RETRY_COUNT;
80 do {
81 status = pio_read_8(&dsp->regs->dsp_write);
82 } while (--attempts && ((status & DSP_WRITE_BUSY) != 0));
83
84 if ((status & DSP_WRITE_BUSY))
85 return EIO;
86
87 pio_write_8(&dsp->regs->dsp_write, data);
88 return EOK;
89}
90
91static inline void sb_dsp_reset(sb_dsp_t *dsp)
92{
93 assert(dsp);
94 /* Reset DSP, see Chapter 2 of Sound Blaster HW programming guide */
95 pio_write_8(&dsp->regs->dsp_reset, 1);
96 udelay(3); /* Keep reset for 3 us */
97 pio_write_8(&dsp->regs->dsp_reset, 0);
98}
99
100static inline int sb_setup_dma(sb_dsp_t *dsp, uintptr_t pa, size_t size)
101{
102 async_sess_t *sess = devman_parent_device_connect(EXCHANGE_ATOMIC,
103 dsp->sb_dev->handle, IPC_FLAG_BLOCKING);
104 if (!sess)
105 return ENOMEM;
106
107 const int ret = hw_res_dma_channel_setup(sess,
108 dsp->dma16_channel, pa, size,
109 DMA_MODE_READ | DMA_MODE_AUTO | DMA_MODE_ON_DEMAND);
110 async_hangup(sess);
111 return ret;
112}
113
114static inline int sb_setup_buffer(sb_dsp_t *dsp, size_t size)
115{
116 assert(dsp);
117 if (size > MAX_BUFFER_SIZE || size == 0 || (size % 2) == 1)
118 size = MAX_BUFFER_SIZE;
119 uint8_t *buffer = dma_create_buffer24(size);
120 if (buffer == NULL) {
121 ddf_log_error("Failed to allocate DMA buffer.");
122 return ENOMEM;
123 }
124
125 const uintptr_t pa = addr_to_phys(buffer);
126 assert(pa < (1 << 25));
127
128 /* Set 16 bit channel */
129 const int ret = sb_setup_dma(dsp, pa, size);
130 if (ret == EOK) {
131 dsp->buffer.data = buffer;
132 dsp->buffer.size = size;
133 bzero(dsp->buffer.data, dsp->buffer.size);
134 } else {
135 ddf_log_error("Failed to setup DMA16 channel: %s.",
136 str_error(ret));
137 dma_destroy_buffer(buffer);
138 }
139 return ret;
140}
141
142static inline void sb_clear_buffer(sb_dsp_t *dsp)
143{
144 dma_destroy_buffer(dsp->buffer.data);
145 dsp->buffer.data = NULL;
146 dsp->buffer.size = 0;
147}
148
149static inline size_t sample_count(pcm_sample_format_t format, size_t byte_count)
150{
151 return byte_count / pcm_sample_format_size(format);
152}
153
154int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
155 int dma8, int dma16)
156{
157 assert(dsp);
158 dsp->regs = regs;
159 dsp->dma8_channel = dma8;
160 dsp->dma16_channel = dma16;
161 dsp->event_session = NULL;
162 dsp->event_exchange = NULL;
163 dsp->sb_dev = dev;
164 sb_dsp_reset(dsp);
165 /* "DSP takes about 100 microseconds to initialize itself" */
166 udelay(100);
167 uint8_t response;
168 const int ret = sb_dsp_read(dsp, &response);
169 if (ret != EOK) {
170 ddf_log_error("Failed to read DSP reset response value.");
171 return ret;
172 }
173 if (response != DSP_RESET_RESPONSE) {
174 ddf_log_error("Invalid DSP reset response: %x.", response);
175 return EIO;
176 }
177
178 /* Get DSP version number */
179 sb_dsp_write(dsp, DSP_VERSION);
180 sb_dsp_read(dsp, &dsp->version.major);
181 sb_dsp_read(dsp, &dsp->version.minor);
182
183 return ret;
184}
185
186void sb_dsp_interrupt(sb_dsp_t *dsp)
187{
188 assert(dsp);
189 if (dsp->event_exchange) {
190 async_msg_0(dsp->event_exchange, IPC_FIRST_USER_METHOD);
191 } else {
192 ddf_log_warning("Interrupt with no event consumer.");
193 }
194#ifndef AUTO_DMA_MODE
195 if (dsp->active.playing)
196 sb_dsp_write(dsp, SINGLE_DMA_16B_DA);
197 else
198 sb_dsp_write(dsp, SINGLE_DMA_16B_AD);
199
200 sb_dsp_write(dsp, dsp->active.mode);
201 sb_dsp_write(dsp, (dsp->active.samples - 1) & 0xff);
202 sb_dsp_write(dsp, (dsp->active.samples - 1) >> 8);
203#endif
204}
205
206int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size, unsigned *id)
207{
208 assert(dsp);
209 assert(size);
210
211 /* buffer is already setup by for someone, refuse to work until
212 * it's released */
213 if (dsp->buffer.data)
214 return EBUSY;
215
216 const int ret = sb_setup_buffer(dsp, *size);
217 if (ret == EOK) {
218 ddf_log_debug("Providing buffer(%u): %p, %zu B.",
219 BUFFER_ID, dsp->buffer.data, dsp->buffer.size);
220
221 if (buffer)
222 *buffer = dsp->buffer.data;
223 if (size)
224 *size = dsp->buffer.size;
225 if (id)
226 *id = BUFFER_ID;
227 }
228 return ret;
229}
230
231int sb_dsp_set_event_session(sb_dsp_t *dsp, unsigned id, async_sess_t *session)
232{
233 assert(dsp);
234 assert(session);
235 if (id != BUFFER_ID)
236 return ENOENT;
237 if (dsp->event_session)
238 return EBUSY;
239 dsp->event_session = session;
240 ddf_log_debug("Set event session.");
241 return EOK;
242}
243
244int sb_dsp_release_buffer(sb_dsp_t *dsp, unsigned id)
245{
246 assert(dsp);
247 if (id != BUFFER_ID)
248 return ENOENT;
249 sb_clear_buffer(dsp);
250 async_exchange_end(dsp->event_exchange);
251 dsp->event_exchange = NULL;
252 if (dsp->event_session)
253 async_hangup(dsp->event_session);
254 dsp->event_session = NULL;
255 ddf_log_debug("DSP buffer released.");
256 return EOK;
257}
258
259int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned id, unsigned parts,
260 unsigned channels, unsigned sampling_rate, pcm_sample_format_t format)
261{
262 assert(dsp);
263
264 if (!dsp->event_session)
265 return EINVAL;
266
267 /* Play block size must be even number (we use DMA 16)*/
268 if (dsp->buffer.size % (parts * 2))
269 return EINVAL;
270
271 const unsigned play_block_size = dsp->buffer.size / parts;
272
273 /* Check supported parameters */
274 ddf_log_debug("Requested playback on buffer \"%u\" (%u parts): %uHz, "
275 "%s, %u channel(s).", id, parts, sampling_rate,
276 pcm_sample_format_str(format), channels);
277 if (id != BUFFER_ID)
278 return ENOENT;
279 if (channels != 1 && channels != 2)
280 return ENOTSUP;
281 if (sampling_rate > 44100)
282 return ENOTSUP;
283 // FIXME We only support 16 bit playback
284 if (format != PCM_SAMPLE_UINT16_LE && format != PCM_SAMPLE_SINT16_LE)
285 return ENOTSUP;
286
287 dsp->event_exchange = async_exchange_begin(dsp->event_session);
288 if (!dsp->event_exchange)
289 return ENOMEM;
290
291 const bool sign = (format == PCM_SAMPLE_SINT16_LE);
292
293 sb_dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
294 sb_dsp_write(dsp, sampling_rate >> 8);
295 sb_dsp_write(dsp, sampling_rate & 0xff);
296
297 ddf_log_verbose("Sample rate: %hhx:%hhx.",
298 sampling_rate >> 8, sampling_rate & 0xff);
299
300#ifdef AUTO_DMA_MODE
301 sb_dsp_write(dsp, AUTO_DMA_16B_DA_FIFO);
302#else
303 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
304#endif
305
306 dsp->active.mode = 0 |
307 (sign ? DSP_MODE_SIGNED : 0) | (channels == 2 ? DSP_MODE_STEREO : 0);
308 sb_dsp_write(dsp, dsp->active.mode);
309
310 dsp->active.samples = sample_count(format, play_block_size);
311 sb_dsp_write(dsp, (dsp->active.samples - 1) & 0xff);
312 sb_dsp_write(dsp, (dsp->active.samples - 1) >> 8);
313
314 ddf_log_verbose("Playback started, interrupt every %u samples "
315 "(~1/%u sec)", dsp->active.samples,
316 sampling_rate / (dsp->active.samples * channels));
317
318 dsp->active.playing = true;
319
320 return EOK;
321}
322
323int sb_dsp_stop_playback(sb_dsp_t *dsp, unsigned id)
324{
325 assert(dsp);
326 if (id != BUFFER_ID)
327 return ENOENT;
328 async_exchange_end(dsp->event_exchange);
329 sb_dsp_write(dsp, DMA_16B_EXIT);
330 return EOK;
331}
332
333int sb_dsp_start_record(sb_dsp_t *dsp, unsigned id, unsigned parts,
334 unsigned channels, unsigned sampling_rate, pcm_sample_format_t format)
335{
336 assert(dsp);
337
338 if (!dsp->event_session)
339 return EINVAL;
340
341 /* Play block size must be even number (we use DMA 16)*/
342 if (dsp->buffer.size % (parts * 2))
343 return EINVAL;
344
345 const unsigned play_block_size = dsp->buffer.size / parts;
346
347 /* Check supported parameters */
348 ddf_log_debug("Requested recording on buffer \"%u\" (%u parts): %uHz, "
349 "%s, %u channel(s).", id, parts, sampling_rate,
350 pcm_sample_format_str(format), channels);
351 if (id != BUFFER_ID)
352 return ENOENT;
353 if (channels != 1 && channels != 2)
354 return ENOTSUP;
355 if (sampling_rate > 44100)
356 return ENOTSUP;
357 // FIXME We only support 16 bit recording
358 if (format != PCM_SAMPLE_UINT16_LE && format != PCM_SAMPLE_SINT16_LE)
359 return ENOTSUP;
360
361 dsp->event_exchange = async_exchange_begin(dsp->event_session);
362 if (!dsp->event_exchange)
363 return ENOMEM;
364
365 const bool sign = (format == PCM_SAMPLE_SINT16_LE);
366
367 sb_dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
368 sb_dsp_write(dsp, sampling_rate >> 8);
369 sb_dsp_write(dsp, sampling_rate & 0xff);
370
371 ddf_log_verbose("Sampling rate: %hhx:%hhx.",
372 sampling_rate >> 8, sampling_rate & 0xff);
373
374#ifdef AUTO_DMA_MODE
375 sb_dsp_write(dsp, AUTO_DMA_16B_AD_FIFO);
376#else
377 sb_dsp_write(dsp, SINGLE_DMA_16B_AD_FIFO);
378#endif
379
380 dsp->active.mode = 0 |
381 (sign ? DSP_MODE_SIGNED : 0) | (channels == 2 ? DSP_MODE_STEREO : 0);
382 sb_dsp_write(dsp, dsp->active.mode);
383
384 dsp->active.samples = sample_count(format, play_block_size);
385 sb_dsp_write(dsp, (dsp->active.samples - 1) & 0xff);
386 sb_dsp_write(dsp, (dsp->active.samples - 1) >> 8);
387
388 ddf_log_verbose("Recording started started, interrupt every %u samples "
389 "(~1/%u sec)", dsp->active.samples,
390 sampling_rate / (dsp->active.samples * channels));
391 dsp->active.playing = false;
392
393 return EOK;
394}
395
396int sb_dsp_stop_record(sb_dsp_t *dsp, unsigned id)
397{
398 assert(dsp);
399 if (id != BUFFER_ID)
400 return ENOENT;
401 async_exchange_end(dsp->event_exchange);
402 sb_dsp_write(dsp, DMA_16B_EXIT);
403 return EOK;
404}
405/**
406 * @}
407 */
Note: See TracBrowser for help on using the repository browser.