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

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

sb16: Use void* for playback data.

  • Property mode set to 100644
File size: 9.3 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_SIZE (PAGE_SIZE)
48#define PLAY_BLOCK_SIZE (BUFFER_SIZE / 2)
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)
115{
116 assert(dsp);
117 uint8_t *buffer = dma_create_buffer24(BUFFER_SIZE);
118 if (buffer == NULL) {
119 ddf_log_error("Failed to allocate buffer.\n");
120 return ENOMEM;
121 }
122
123 const uintptr_t pa = addr_to_phys(buffer);
124 assert(pa < (1 << 25));
125 /* Set 16 bit channel */
126 const int ret = sb_setup_dma(dsp, pa, BUFFER_SIZE);
127 if (ret == EOK) {
128 dsp->buffer.data = buffer;
129 dsp->buffer.position = buffer;
130 dsp->buffer.size = BUFFER_SIZE;
131 bzero(buffer, BUFFER_SIZE);
132 } else {
133 ddf_log_error("Failed to setup DMA16 channel %s.\n",
134 str_error(ret));
135 dma_destroy_buffer(buffer);
136 }
137 return ret;
138}
139/*----------------------------------------------------------------------------*/
140static inline void sb_clear_buffer(sb_dsp_t *dsp)
141{
142 dma_destroy_buffer(dsp->buffer.data);
143 dsp->buffer.data = NULL;
144 dsp->buffer.position = NULL;
145 dsp->buffer.size = 0;
146}
147/*----------------------------------------------------------------------------*/
148static inline size_t sample_count(uint8_t mode, size_t byte_count)
149{
150 if (mode & DSP_MODE_16BIT) {
151 return byte_count / 2;
152 }
153 return byte_count;
154}
155/*----------------------------------------------------------------------------*/
156int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
157 int dma8, int dma16)
158{
159 assert(dsp);
160 dsp->regs = regs;
161 dsp->dma8_channel = dma8;
162 dsp->dma16_channel = dma16;
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.\n");
171 return ret;
172 }
173 if (response != DSP_RESET_RESPONSE) {
174 ddf_log_error("Invalid DSP reset response: %x.\n", 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 const size_t remain_size =
190 dsp->playing.size - (dsp->playing.position - dsp->playing.data);
191
192 if (remain_size == 0) {
193#ifdef AUTO_DMA_MODE
194 sb_dsp_write(dsp, DMA_16B_EXIT);
195#endif
196 ddf_log_note("Nothing more to play");
197 sb_clear_buffer(dsp);
198 return;
199 }
200 if (remain_size <= PLAY_BLOCK_SIZE) {
201 ddf_log_note("Last %zu bytes to play.\n", remain_size);
202 /* This is the last block */
203 memcpy(dsp->buffer.position, dsp->playing.position, remain_size);
204 write_barrier();
205 dsp->playing.position += remain_size;
206 dsp->buffer.position += remain_size;
207 const size_t samples =
208 sample_count(dsp->playing.mode, remain_size);
209 sb_dsp_write(dsp, SINGLE_DMA_16B_DA);
210 sb_dsp_write(dsp, dsp->playing.mode);
211 sb_dsp_write(dsp, (samples - 1) & 0xff);
212 sb_dsp_write(dsp, (samples - 1) >> 8);
213 return;
214 }
215 /* Copy new data */
216 memcpy(dsp->buffer.position, dsp->playing.position, PLAY_BLOCK_SIZE);
217 write_barrier();
218 /* Adjust position */
219 dsp->playing.position += PLAY_BLOCK_SIZE;
220 dsp->buffer.position += PLAY_BLOCK_SIZE;
221 /* Wrap around */
222 if (dsp->buffer.position == (dsp->buffer.data + dsp->buffer.size))
223 dsp->buffer.position = dsp->buffer.data;
224#ifndef AUTO_DMA_MODE
225 const size_t samples = sample_count(dsp->playing.mode, PLAY_BLOCK_SIZE);
226 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
227 sb_dsp_write(dsp, dsp->playing.mode);
228 sb_dsp_write(dsp, (samples - 1) & 0xff);
229 sb_dsp_write(dsp, (samples - 1) >> 8);
230#endif
231
232}
233/*----------------------------------------------------------------------------*/
234int sb_dsp_play_direct(sb_dsp_t *dsp, const uint8_t *data, size_t size,
235 unsigned sampling_rate, unsigned channels, unsigned bit_depth)
236{
237 assert(dsp);
238 if (channels != 1 || bit_depth != 8)
239 return EIO;
240 /* In microseconds */
241 const unsigned wait_period = 1000000 / sampling_rate;
242 while (size--) {
243 pio_write_8(&dsp->regs->dsp_write, DIRECT_8B_OUTPUT);
244 pio_write_8(&dsp->regs->dsp_write, *data++);
245 udelay(wait_period);
246 }
247 return EOK;
248}
249/*----------------------------------------------------------------------------*/
250int sb_dsp_play(sb_dsp_t *dsp, const void *data, size_t size,
251 uint16_t sampling_rate, unsigned channels, unsigned sample_size)
252{
253 assert(dsp);
254 if (!data)
255 return EOK;
256
257 /* Check supported parameters */
258 if (sample_size != 8 && sample_size != 16)
259 return ENOTSUP;
260 if (channels != 1 && channels != 2)
261 return ENOTSUP;
262
263 ddf_log_debug("Buffer prepare.\n");
264 const int ret = sb_setup_buffer(dsp);
265 if (ret != EOK)
266 return ret;
267
268 const size_t copy_size = size < BUFFER_SIZE ? size : BUFFER_SIZE;
269 const size_t play_size =
270 size < PLAY_BLOCK_SIZE ? size : PLAY_BLOCK_SIZE;
271
272 dsp->playing.data = data;
273 dsp->playing.position = data + copy_size;
274 dsp->playing.size = size;
275 dsp->playing.mode = 0;
276
277 if (sample_size == 16)
278 dsp->playing.mode |= DSP_MODE_16BIT;
279 if (channels == 2)
280 dsp->playing.mode |= DSP_MODE_STEREO;
281
282 const size_t samples = sample_count(dsp->playing.mode, play_size);
283
284 ddf_log_debug("Playing %s sound: %zu(%zu) bytes => %zu samples.\n",
285 mode_to_str(dsp->playing.mode), play_size, size, samples);
286
287 memcpy(dsp->buffer.data, dsp->playing.data, copy_size);
288 write_barrier();
289
290 sb_dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
291 sb_dsp_write(dsp, sampling_rate >> 8);
292 sb_dsp_write(dsp, sampling_rate & 0xff);
293
294 ddf_log_debug("Sampling rate: %hhx:%hhx.\n",
295 sampling_rate >> 8, sampling_rate & 0xff);
296
297#ifdef AUTO_DMA_MODE
298 if (play_size < size) {
299 sb_dsp_write(dsp, AUTO_DMA_16B_DA_FIFO);
300 } else {
301 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
302 }
303#else
304 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
305#endif
306
307 sb_dsp_write(dsp, dsp->playing.mode);
308 sb_dsp_write(dsp, (samples - 1) & 0xff);
309 sb_dsp_write(dsp, (samples - 1) >> 8);
310
311 return EOK;
312}
313/**
314 * @}
315 */
Note: See TracBrowser for help on using the repository browser.