source: mainline/uspace/drv/audio/sb16/dsp.c@ 43dec08

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

sb16, dsp: Add external buffer manipulation.

  • Property mode set to 100644
File size: 11.9 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 BUFFER_SIZE (PAGE_SIZE)
49#define PLAY_BLOCK_SIZE (BUFFER_SIZE / 2)
50
51#ifndef DSP_RETRY_COUNT
52#define DSP_RETRY_COUNT 100
53#endif
54
55#define DSP_RESET_RESPONSE 0xaa
56
57#define AUTO_DMA_MODE
58
59static inline int sb_dsp_read(sb_dsp_t *dsp, uint8_t *data)
60{
61 assert(data);
62 assert(dsp);
63 uint8_t status;
64 size_t attempts = DSP_RETRY_COUNT;
65 do {
66 status = pio_read_8(&dsp->regs->dsp_read_status);
67 } while (--attempts && ((status & DSP_READ_READY) == 0));
68
69 if ((status & DSP_READ_READY) == 0)
70 return EIO;
71
72 *data = pio_read_8(&dsp->regs->dsp_data_read);
73 return EOK;
74}
75/*----------------------------------------------------------------------------*/
76static inline int sb_dsp_write(sb_dsp_t *dsp, uint8_t data)
77{
78 assert(dsp);
79 uint8_t status;
80 size_t attempts = DSP_RETRY_COUNT;
81 do {
82 status = pio_read_8(&dsp->regs->dsp_write);
83 } while (--attempts && ((status & DSP_WRITE_BUSY) != 0));
84
85 if ((status & DSP_WRITE_BUSY))
86 return EIO;
87
88 pio_write_8(&dsp->regs->dsp_write, data);
89 return EOK;
90}
91/*----------------------------------------------------------------------------*/
92static inline void sb_dsp_reset(sb_dsp_t *dsp)
93{
94 assert(dsp);
95 /* Reset DSP, see Chapter 2 of Sound Blaster HW programming guide */
96 pio_write_8(&dsp->regs->dsp_reset, 1);
97 udelay(3); /* Keep reset for 3 us */
98 pio_write_8(&dsp->regs->dsp_reset, 0);
99}
100/*----------------------------------------------------------------------------*/
101static inline int sb_setup_dma(sb_dsp_t *dsp, uintptr_t pa, size_t size)
102{
103 async_sess_t *sess = devman_parent_device_connect(EXCHANGE_ATOMIC,
104 dsp->sb_dev->handle, IPC_FLAG_BLOCKING);
105 if (!sess)
106 return ENOMEM;
107
108 const int ret = hw_res_dma_channel_setup(sess,
109 dsp->dma16_channel, pa, size,
110 DMA_MODE_READ | DMA_MODE_AUTO | DMA_MODE_ON_DEMAND);
111 async_hangup(sess);
112 return ret;
113}
114/*----------------------------------------------------------------------------*/
115static inline int sb_setup_buffer(sb_dsp_t *dsp)
116{
117 assert(dsp);
118 uint8_t *buffer = dma_create_buffer24(BUFFER_SIZE);
119 if (buffer == NULL) {
120 ddf_log_error("Failed to allocate buffer.\n");
121 return ENOMEM;
122 }
123
124 const uintptr_t pa = addr_to_phys(buffer);
125 assert(pa < (1 << 25));
126 /* Set 16 bit channel */
127 const int ret = sb_setup_dma(dsp, pa, BUFFER_SIZE);
128 if (ret == EOK) {
129 dsp->buffer.data = buffer;
130 dsp->buffer.position = buffer;
131 dsp->buffer.size = BUFFER_SIZE;
132 bzero(buffer, BUFFER_SIZE);
133 } else {
134 ddf_log_error("Failed to setup DMA16 channel %s.\n",
135 str_error(ret));
136 dma_destroy_buffer(buffer);
137 }
138 return ret;
139}
140/*----------------------------------------------------------------------------*/
141static inline void sb_clear_buffer(sb_dsp_t *dsp)
142{
143 dma_destroy_buffer(dsp->buffer.data);
144 dsp->buffer.data = NULL;
145 dsp->buffer.position = NULL;
146 dsp->buffer.size = 0;
147}
148/*----------------------------------------------------------------------------*/
149static inline size_t sample_count(unsigned sample_size, size_t byte_count)
150{
151 if (sample_size == 16) {
152 return byte_count / 2;
153 }
154 return byte_count;
155}
156/*----------------------------------------------------------------------------*/
157int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
158 int dma8, int dma16)
159{
160 assert(dsp);
161 dsp->regs = regs;
162 dsp->dma8_channel = dma8;
163 dsp->dma16_channel = dma16;
164 dsp->sb_dev = dev;
165 sb_dsp_reset(dsp);
166 /* "DSP takes about 100 microseconds to initialize itself" */
167 udelay(100);
168 uint8_t response;
169 const int ret = sb_dsp_read(dsp, &response);
170 if (ret != EOK) {
171 ddf_log_error("Failed to read DSP reset response value.\n");
172 return ret;
173 }
174 if (response != DSP_RESET_RESPONSE) {
175 ddf_log_error("Invalid DSP reset response: %x.\n", response);
176 return EIO;
177 }
178
179 /* Get DSP version number */
180 sb_dsp_write(dsp, DSP_VERSION);
181 sb_dsp_read(dsp, &dsp->version.major);
182 sb_dsp_read(dsp, &dsp->version.minor);
183
184 return ret;
185}
186/*----------------------------------------------------------------------------*/
187void sb_dsp_interrupt(sb_dsp_t *dsp)
188{
189#if 0
190 assert(dsp);
191 const size_t remain_size =
192 dsp->playing.size - (dsp->playing.position - dsp->playing.data);
193
194 if (remain_size == 0) {
195#ifdef AUTO_DMA_MODE
196 sb_dsp_write(dsp, DMA_16B_EXIT);
197#endif
198 ddf_log_note("Nothing more to play");
199 sb_clear_buffer(dsp);
200 return;
201 }
202 if (remain_size <= PLAY_BLOCK_SIZE) {
203 ddf_log_note("Last %zu bytes to play.\n", remain_size);
204 /* This is the last block */
205 memcpy(dsp->buffer.position, dsp->playing.position, remain_size);
206 write_barrier();
207 dsp->playing.position += remain_size;
208 dsp->buffer.position += remain_size;
209 const size_t samples =
210 sample_count(dsp->playing.mode, remain_size);
211 sb_dsp_write(dsp, SINGLE_DMA_16B_DA);
212 sb_dsp_write(dsp, dsp->playing.mode);
213 sb_dsp_write(dsp, (samples - 1) & 0xff);
214 sb_dsp_write(dsp, (samples - 1) >> 8);
215 return;
216 }
217 /* Copy new data */
218 memcpy(dsp->buffer.position, dsp->playing.position, PLAY_BLOCK_SIZE);
219 write_barrier();
220 /* Adjust position */
221 dsp->playing.position += PLAY_BLOCK_SIZE;
222 dsp->buffer.position += PLAY_BLOCK_SIZE;
223 /* Wrap around */
224 if (dsp->buffer.position == (dsp->buffer.data + dsp->buffer.size))
225 dsp->buffer.position = dsp->buffer.data;
226#ifndef AUTO_DMA_MODE
227 const size_t samples = sample_count(dsp->playing.mode, PLAY_BLOCK_SIZE);
228 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
229 sb_dsp_write(dsp, dsp->playing.mode);
230 sb_dsp_write(dsp, (samples - 1) & 0xff);
231 sb_dsp_write(dsp, (samples - 1) >> 8);
232#endif
233#endif
234}
235/*----------------------------------------------------------------------------*/
236int sb_dsp_play_direct(sb_dsp_t *dsp, const uint8_t *data, size_t size,
237 unsigned sampling_rate, unsigned channels, unsigned bit_depth)
238{
239 assert(dsp);
240 if (channels != 1 || bit_depth != 8)
241 return EIO;
242 /* In microseconds */
243 const unsigned wait_period = 1000000 / sampling_rate;
244 while (size--) {
245 pio_write_8(&dsp->regs->dsp_write, DIRECT_8B_OUTPUT);
246 pio_write_8(&dsp->regs->dsp_write, *data++);
247 udelay(wait_period);
248 }
249 return EOK;
250}
251/*----------------------------------------------------------------------------*/
252int sb_dsp_play(sb_dsp_t *dsp, const void *data, size_t size,
253 uint16_t sampling_rate, unsigned channels, unsigned sample_size)
254{
255 assert(dsp);
256 if (!data)
257 return EOK;
258
259 /* Check supported parameters */
260 if (sample_size != 16) // FIXME We only support 16 bit playback
261 return ENOTSUP;
262 if (channels != 1 && channels != 2)
263 return ENOTSUP;
264
265 ddf_log_debug("Buffer prepare.\n");
266 const int ret = sb_setup_buffer(dsp);
267 if (ret != EOK)
268 return ret;
269
270 const size_t copy_size = size < BUFFER_SIZE ? size : BUFFER_SIZE;
271 const size_t play_size =
272 size < PLAY_BLOCK_SIZE ? size : PLAY_BLOCK_SIZE;
273
274 dsp->playing.data = data;
275 dsp->playing.position = data + copy_size;
276 dsp->playing.size = size;
277 dsp->playing.mode = 0;
278
279 dsp->playing.mode |= DSP_MODE_SIGNED;
280 if (channels == 2)
281 dsp->playing.mode |= DSP_MODE_STEREO;
282
283 const size_t samples = sample_count(sample_size, play_size);
284
285 ddf_log_debug("Playing %s sound: %zu(%zu) bytes => %zu samples.\n",
286 mode_to_str(dsp->playing.mode), play_size, size, samples);
287
288 memcpy(dsp->buffer.data, dsp->playing.data, copy_size);
289 write_barrier();
290
291 sb_dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
292 sb_dsp_write(dsp, sampling_rate >> 8);
293 sb_dsp_write(dsp, sampling_rate & 0xff);
294
295 ddf_log_debug("Sampling rate: %hhx:%hhx.\n",
296 sampling_rate >> 8, sampling_rate & 0xff);
297
298#ifdef AUTO_DMA_MODE
299 if (play_size < size) {
300 sb_dsp_write(dsp, AUTO_DMA_16B_DA_FIFO);
301 } else {
302 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
303 }
304#else
305 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
306#endif
307
308 sb_dsp_write(dsp, dsp->playing.mode);
309 sb_dsp_write(dsp, (samples - 1) & 0xff);
310 sb_dsp_write(dsp, (samples - 1) >> 8);
311
312 return EOK;
313}
314/*----------------------------------------------------------------------------*/
315int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size, unsigned *id)
316{
317 assert(dsp);
318 const int ret = sb_setup_buffer(dsp);
319 ddf_log_debug("Providing buffer(%u): %p, %zu.\n",
320 BUFFER_ID, dsp->buffer.data, dsp->buffer.size);
321 if (ret == EOK && buffer)
322 *buffer = dsp->buffer.data;
323 if (ret == EOK && size)
324 *size = dsp->buffer.size;
325 if (ret == EOK && id)
326 *id = BUFFER_ID;
327 return ret;
328}
329/*----------------------------------------------------------------------------*/
330int sb_dsp_release_buffer(sb_dsp_t *dsp, unsigned id)
331{
332 assert(dsp);
333 if (id != BUFFER_ID)
334 return ENOENT;
335 sb_clear_buffer(dsp);
336 return EOK;
337}
338/*----------------------------------------------------------------------------*/
339int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned id, unsigned sampling_rate,
340 unsigned sample_size, unsigned channels, bool sign)
341{
342 assert(dsp);
343
344 /* Check supported parameters */
345 ddf_log_debug("Starting playback on buffer(%u): rate: %u, size: %u, "
346 " channels: %u, signed: %s.\n", id, sampling_rate, sample_size,
347 channels, sign ? "YES" : "NO" );
348 if (id != BUFFER_ID)
349 return ENOENT;
350 if (sample_size != 16) // FIXME We only support 16 bit playback
351 return ENOTSUP;
352 if (channels != 1 && channels != 2)
353 return ENOTSUP;
354 if (sampling_rate > 44100)
355 return ENOTSUP;
356
357
358 sb_dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
359 sb_dsp_write(dsp, sampling_rate >> 8);
360 sb_dsp_write(dsp, sampling_rate & 0xff);
361
362 ddf_log_debug("Sampling rate: %hhx:%hhx.\n",
363 sampling_rate >> 8, sampling_rate & 0xff);
364
365#ifdef AUTO_DMA_MODE
366 sb_dsp_write(dsp, AUTO_DMA_16B_DA_FIFO);
367#else
368 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
369#endif
370
371 const uint8_t mode =
372 (sign ? DSP_MODE_SIGNED : 0) | (channels == 2 ? DSP_MODE_STEREO : 0);
373 sb_dsp_write(dsp, mode);
374
375 const uint16_t samples = sample_count(sample_size, PLAY_BLOCK_SIZE);
376 sb_dsp_write(dsp, (samples - 1) & 0xff);
377 sb_dsp_write(dsp, (samples - 1) >> 8);
378
379 return EOK;
380 return ENOTSUP;
381}
382/*----------------------------------------------------------------------------*/
383int sb_dsp_stop_playback(sb_dsp_t *dsp, unsigned id)
384{
385 assert(dsp);
386 if (id != BUFFER_ID)
387 return ENOENT;
388 sb_dsp_write(dsp, DMA_16B_EXIT);
389 return EOK;
390}
391/*----------------------------------------------------------------------------*/
392int sb_dsp_start_record(sb_dsp_t *dsp, unsigned id, unsigned sample_rate,
393 unsigned sample_size, unsigned channels, bool sign)
394{
395 return ENOTSUP;
396}
397/*----------------------------------------------------------------------------*/
398int sb_dsp_stop_record(sb_dsp_t *dsp, unsigned id)
399{
400 return ENOTSUP;
401}
402/**
403 * @}
404 */
Note: See TracBrowser for help on using the repository browser.