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

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

sb16: Drop support for 8bit format for now.

Looks like 8bit playback needs working dma8 channel (need to verify).

  • Property mode set to 100644
File size: 9.3 KB
RevLine 
[763444e]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
[c885a21]35#include <devman.h>
36#include <device/hw_res.h>
[763444e]37#include <libarch/ddi.h>
[0b4f060]38#include <libarch/barrier.h>
[01aef43]39#include <str_error.h>
[c885a21]40#include <bool.h>
[0f2e7c1]41
42#include "dma.h"
[bf38143]43#include "ddf_log.h"
44#include "dsp_commands.h"
[763444e]45#include "dsp.h"
46
[0ea8f83]47#define BUFFER_SIZE (PAGE_SIZE)
[dce7e41]48#define PLAY_BLOCK_SIZE (BUFFER_SIZE / 2)
[bf38143]49
50#ifndef DSP_RETRY_COUNT
51#define DSP_RETRY_COUNT 100
52#endif
53
54#define DSP_RESET_RESPONSE 0xaa
55
[5984107]56#define AUTO_DMA_MODE
57
[bf38143]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/*----------------------------------------------------------------------------*/
[c885a21]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/*----------------------------------------------------------------------------*/
[01aef43]114static inline int sb_setup_buffer(sb_dsp_t *dsp)
115{
116 assert(dsp);
[0ea8f83]117 uint8_t *buffer = dma_create_buffer24(BUFFER_SIZE);
[f14e6ea]118 if (buffer == NULL) {
119 ddf_log_error("Failed to allocate buffer.\n");
120 return ENOMEM;
121 }
122
[01aef43]123 const uintptr_t pa = addr_to_phys(buffer);
[f14e6ea]124 assert(pa < (1 << 25));
[7257eea6]125 /* Set 16 bit channel */
[c885a21]126 const int ret = sb_setup_dma(dsp, pa, BUFFER_SIZE);
[01aef43]127 if (ret == EOK) {
[dce7e41]128 dsp->buffer.data = buffer;
129 dsp->buffer.position = buffer;
130 dsp->buffer.size = BUFFER_SIZE;
[962ef67]131 bzero(buffer, BUFFER_SIZE);
[01aef43]132 } else {
[5984107]133 ddf_log_error("Failed to setup DMA16 channel %s.\n",
[01aef43]134 str_error(ret));
[0ea8f83]135 dma_destroy_buffer(buffer);
[01aef43]136 }
137 return ret;
138}
139/*----------------------------------------------------------------------------*/
[7257eea6]140static inline void sb_clear_buffer(sb_dsp_t *dsp)
141{
[0ea8f83]142 dma_destroy_buffer(dsp->buffer.data);
[dce7e41]143 dsp->buffer.data = NULL;
144 dsp->buffer.position = NULL;
145 dsp->buffer.size = 0;
[7257eea6]146}
147/*----------------------------------------------------------------------------*/
[0b4f060]148static inline size_t sample_count(uint8_t mode, size_t byte_count)
149{
[84d6c39]150 // FIXME we only support 16 bit playback for now.
151 return byte_count / 2;
152
153 if (mode & DSP_MODE_SIGNED) {
[0b4f060]154 return byte_count / 2;
155 }
156 return byte_count;
157}
158/*----------------------------------------------------------------------------*/
[c885a21]159int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
160 int dma8, int dma16)
[bf38143]161{
162 assert(dsp);
163 dsp->regs = regs;
[c885a21]164 dsp->dma8_channel = dma8;
165 dsp->dma16_channel = dma16;
166 dsp->sb_dev = dev;
[bf38143]167 sb_dsp_reset(dsp);
168 /* "DSP takes about 100 microseconds to initialize itself" */
169 udelay(100);
170 uint8_t response;
171 const int ret = sb_dsp_read(dsp, &response);
172 if (ret != EOK) {
173 ddf_log_error("Failed to read DSP reset response value.\n");
174 return ret;
175 }
176 if (response != DSP_RESET_RESPONSE) {
177 ddf_log_error("Invalid DSP reset response: %x.\n", response);
178 return EIO;
179 }
180
181 /* Get DSP version number */
182 sb_dsp_write(dsp, DSP_VERSION);
183 sb_dsp_read(dsp, &dsp->version.major);
184 sb_dsp_read(dsp, &dsp->version.minor);
[01aef43]185
186 return ret;
[bf38143]187}
188/*----------------------------------------------------------------------------*/
[f14e6ea]189void sb_dsp_interrupt(sb_dsp_t *dsp)
190{
191 assert(dsp);
[413225d]192 const size_t remain_size =
193 dsp->playing.size - (dsp->playing.position - dsp->playing.data);
[dce7e41]194
195 if (remain_size == 0) {
[ed3f8d5]196#ifdef AUTO_DMA_MODE
197 sb_dsp_write(dsp, DMA_16B_EXIT);
198#endif
[dce7e41]199 ddf_log_note("Nothing more to play");
200 sb_clear_buffer(dsp);
201 return;
202 }
[ac149d5]203 if (remain_size <= PLAY_BLOCK_SIZE) {
[dce7e41]204 ddf_log_note("Last %zu bytes to play.\n", remain_size);
205 /* This is the last block */
206 memcpy(dsp->buffer.position, dsp->playing.position, remain_size);
[0b4f060]207 write_barrier();
[dce7e41]208 dsp->playing.position += remain_size;
209 dsp->buffer.position += remain_size;
[0b4f060]210 const size_t samples =
211 sample_count(dsp->playing.mode, remain_size);
[dce7e41]212 sb_dsp_write(dsp, SINGLE_DMA_16B_DA);
213 sb_dsp_write(dsp, dsp->playing.mode);
[0b4f060]214 sb_dsp_write(dsp, (samples - 1) & 0xff);
215 sb_dsp_write(dsp, (samples - 1) >> 8);
[dce7e41]216 return;
217 }
[ed3f8d5]218 /* Copy new data */
[dce7e41]219 memcpy(dsp->buffer.position, dsp->playing.position, PLAY_BLOCK_SIZE);
[0b4f060]220 write_barrier();
[ed3f8d5]221 /* Adjust position */
[dce7e41]222 dsp->playing.position += PLAY_BLOCK_SIZE;
223 dsp->buffer.position += PLAY_BLOCK_SIZE;
224 /* Wrap around */
225 if (dsp->buffer.position == (dsp->buffer.data + dsp->buffer.size))
226 dsp->buffer.position = dsp->buffer.data;
[ed3f8d5]227#ifndef AUTO_DMA_MODE
228 const size_t samples = sample_count(dsp->playing.mode, PLAY_BLOCK_SIZE);
229 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
230 sb_dsp_write(dsp, dsp->playing.mode);
231 sb_dsp_write(dsp, (samples - 1) & 0xff);
232 sb_dsp_write(dsp, (samples - 1) >> 8);
233#endif
[dce7e41]234
[f14e6ea]235}
236/*----------------------------------------------------------------------------*/
[bf38143]237int sb_dsp_play_direct(sb_dsp_t *dsp, const uint8_t *data, size_t size,
[763444e]238 unsigned sampling_rate, unsigned channels, unsigned bit_depth)
239{
[bf38143]240 assert(dsp);
[763444e]241 if (channels != 1 || bit_depth != 8)
242 return EIO;
243 /* In microseconds */
244 const unsigned wait_period = 1000000 / sampling_rate;
245 while (size--) {
[bf38143]246 pio_write_8(&dsp->regs->dsp_write, DIRECT_8B_OUTPUT);
247 pio_write_8(&dsp->regs->dsp_write, *data++);
[763444e]248 udelay(wait_period);
249 }
250 return EOK;
251}
[01aef43]252/*----------------------------------------------------------------------------*/
[5c62b5f]253int sb_dsp_play(sb_dsp_t *dsp, const void *data, size_t size,
[0b4f060]254 uint16_t sampling_rate, unsigned channels, unsigned sample_size)
[01aef43]255{
256 assert(dsp);
257 if (!data)
258 return EOK;
259
260 /* Check supported parameters */
[84d6c39]261 if (sample_size != 16) // FIXME We only support 16 bit playback
[01aef43]262 return ENOTSUP;
263 if (channels != 1 && channels != 2)
264 return ENOTSUP;
265
[5984107]266 ddf_log_debug("Buffer prepare.\n");
[01aef43]267 const int ret = sb_setup_buffer(dsp);
[dce7e41]268 if (ret != EOK)
269 return ret;
[01aef43]270
[ed3f8d5]271 const size_t copy_size = size < BUFFER_SIZE ? size : BUFFER_SIZE;
[dce7e41]272 const size_t play_size =
273 size < PLAY_BLOCK_SIZE ? size : PLAY_BLOCK_SIZE;
274
275 dsp->playing.data = data;
[ac149d5]276 dsp->playing.position = data + copy_size;
[dce7e41]277 dsp->playing.size = size;
[0b4f060]278 dsp->playing.mode = 0;
[ed3f8d5]279
[84d6c39]280 dsp->playing.mode |= DSP_MODE_SIGNED;
[0b4f060]281 if (channels == 2)
[6233c4e]282 dsp->playing.mode |= DSP_MODE_STEREO;
[0b4f060]283
284 const size_t samples = sample_count(dsp->playing.mode, play_size);
285
[6233c4e]286 ddf_log_debug("Playing %s sound: %zu(%zu) bytes => %zu samples.\n",
287 mode_to_str(dsp->playing.mode), play_size, size, samples);
[ac149d5]288
289 memcpy(dsp->buffer.data, dsp->playing.data, copy_size);
[0b4f060]290 write_barrier();
[ac149d5]291
[dce7e41]292 sb_dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
293 sb_dsp_write(dsp, sampling_rate >> 8);
294 sb_dsp_write(dsp, sampling_rate & 0xff);
295
[0b4f060]296 ddf_log_debug("Sampling rate: %hhx:%hhx.\n",
[5984107]297 sampling_rate >> 8, sampling_rate & 0xff);
298
[ed3f8d5]299#ifdef AUTO_DMA_MODE
300 if (play_size < size) {
[7785e951]301 sb_dsp_write(dsp, AUTO_DMA_16B_DA_FIFO);
[ed3f8d5]302 } else {
303 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
[5984107]304 }
[ed3f8d5]305#else
306 sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
307#endif
308
[dce7e41]309 sb_dsp_write(dsp, dsp->playing.mode);
[0b4f060]310 sb_dsp_write(dsp, (samples - 1) & 0xff);
311 sb_dsp_write(dsp, (samples - 1) >> 8);
[dce7e41]312
313 return EOK;
[01aef43]314}
[763444e]315/**
316 * @}
317 */
Note: See TracBrowser for help on using the repository browser.