1 /* MikMod sound library
2 (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 /*==============================================================================
23 Windows Sound System I/O routines (CS42XX, ESS18XX, GUS+DaughterBoard etc)
24 Written by Andrew Zabolotny <bit@eltech.ru>
26 ==============================================================================*/
38 #include <sys/nearptr.h>
39 #include <sys/farptr.h>
44 /********************************************* Private variables/routines *****/
48 /* WSS frequency rates... lower bit selects one of two frequency generators */
49 static unsigned int wss_rates[14][2] = {
50 {5510, 0x00 | WSSM_XTAL2},
51 {6620, 0x0E | WSSM_XTAL2},
52 {8000, 0x00 | WSSM_XTAL1},
53 {9600, 0x0E | WSSM_XTAL1},
54 {11025, 0x02 | WSSM_XTAL2},
55 {16000, 0x02 | WSSM_XTAL1},
56 {18900, 0x04 | WSSM_XTAL2},
57 {22050, 0x06 | WSSM_XTAL2},
58 {27420, 0x04 | WSSM_XTAL1},
59 {32000, 0x06 | WSSM_XTAL1},
60 {33075, 0x0C | WSSM_XTAL2},
61 {37800, 0x08 | WSSM_XTAL2},
62 {44100, 0x0A | WSSM_XTAL2},
63 {48000, 0x0C | WSSM_XTAL1}
68 /* Make sure its not a spurious IRQ */
69 if (!irq_check(wss.irq_handle))
74 /* Clear IRQ status */
75 outportb(WSS_STATUS, 0);
77 /* Write transfer count again */
78 __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff);
79 __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8);
80 irq_ack(wss.irq_handle);
83 if (wss.timer_callback)
87 static void wss_irq_end()
91 /* WSS accepts some conventional values instead of frequency in Hz... */
92 static unsigned char __wss_getrate(unsigned int *freq)
94 int i, best = -1, delta = 0xffff;
96 for (i = 0; i < 14; i++) {
97 int newdelta = abs(wss_rates[i][0] - *freq);
99 best = i, delta = newdelta;
102 *freq = wss_rates[best][0];
103 return wss_rates[best][1];
106 /* Check if we really have a WSS compatible card on given address */
107 static boolean __wss_ping()
109 /* Disable CODEC operations first */
110 __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
111 /* Now put some harmless values in registers and check them */
112 __wss_outreg(WSSR_COUNT_LOW, 0xaa);
113 __wss_outreg(WSSR_COUNT_HIGH, 0x55);
114 return (__wss_inreg(WSSR_COUNT_LOW) == 0xaa)
115 && (__wss_inreg(WSSR_COUNT_HIGH) == 0x55);
118 static boolean __wss_reset()
125 /* Now select the test/initialization register */
127 while (inportb(WSS_ADDR) != WSSR_TEST_INIT) {
128 outportb(WSS_ADDR, WSSR_TEST_INIT);
134 while (inportb(WSS_DATA) & WSSM_CALIB_IN_PROGRESS) {
135 outportb(WSS_ADDR, WSSR_TEST_INIT);
140 /* Enable playback IRQ */
141 __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE);
142 __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ);
144 /* Clear IRQ status */
145 outportb(WSS_STATUS, 0);
150 static boolean __wss_setformat(unsigned char format)
154 outportb(WSS_ADDR, WSSM_MCE | WSSR_PLAY_FORMAT);
155 outportb(WSS_DATA, format);
156 inportb(WSS_DATA); /* ERRATA SHEETS ... */
157 inportb(WSS_DATA); /* ERRATA SHEETS ... */
159 /* Wait end of syncronization ... */
163 /* Turn off the ModeChangeEnable bit: do it until it works */
165 while (inportb(WSS_ADDR) != WSSR_PLAY_FORMAT) {
166 outportb(WSS_ADDR, WSSR_PLAY_FORMAT);
171 return __wss_reset();
174 /**************************************************** WSS detection stuff *****/
176 static int __wss_irq_irqdetect(int irqno)
178 unsigned char status = inportb(WSS_STATUS);
179 /* Clear IRQ status */
180 outportb(WSS_STATUS, 0);
181 /* Reset transfer counter */
182 __wss_outreg(WSSR_COUNT_LOW, 0);
183 __wss_outreg(WSSR_COUNT_HIGH, 0);
184 return (status & WSSM_INT);
187 static boolean __wss_detect()
189 /* First find the port number */
191 static unsigned int wss_ports[] =
192 { 0x32c, 0x530, 0x604, 0xE80, 0xF40 };
194 for (i = 0; i < 5; i++) {
195 wss.port = wss_ports[i];
205 /* Now disable output */
208 /* Detect the DMA channel */
210 static int __dma[] = { 0, 1, 3 };
213 /* Enable playback IRQ */
214 __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE);
215 __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ);
217 /* Start a short DMA transfer and check if DMA count is zero */
218 for (i = 0; i < 3; i++) {
219 unsigned int timer, status, freq = 44100;
223 dma_disable(wss.dma);
224 dma_set_mode(wss.dma, DMA_MODE_WRITE);
225 dma_clear_ff(wss.dma);
226 dma_set_count(wss.dma, 10);
229 /* Clear IRQ status */
230 outportb(WSS_STATUS, 0);
232 __wss_setformat(__wss_getrate(&freq));
233 __wss_outreg(WSSR_COUNT_LOW, 1);
234 __wss_outreg(WSSR_COUNT_HIGH, 0);
235 /* Tell codec to start transfer */
236 __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
239 timer = _farnspeekl(0x46c);
241 while (_farnspeekl(0x46c) - timer <= 2)
242 if (dma_get_count(wss.dma) == 0)
244 __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
245 dma_disable(wss.dma);
247 /* Now check if DMA transfer count is zero and an IRQ is pending */
248 status = inportb(WSS_STATUS);
249 outportb(WSS_STATUS, 0);
250 if ((dma_get_count(wss.dma) == 0) && (status & WSSM_INT))
260 /* Now detect the IRQ number */
262 unsigned int i, irqmask, freq = 5510;
263 unsigned long timer, delta = 0x7fffffff;
265 /* IRQ can be one of 2,3,5,7,10 */
266 irq_detect_start(0x04ac, __wss_irq_irqdetect);
268 dma_disable(wss.dma);
269 dma_set_mode(wss.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
270 dma_clear_ff(wss.dma);
271 dma_set_count(wss.dma, 1);
274 __wss_setformat(__wss_getrate(&freq));
276 /* Clear IRQ status */
277 outportb(WSS_STATUS, 0);
279 __wss_outreg(WSSR_COUNT_LOW, 0);
280 __wss_outreg(WSSR_COUNT_HIGH, 0);
282 /* Prepare timeout counter */
284 timer = _farnspeekl(0x46c);
285 while (timer == _farnspeekl(0x46c));
286 timer = _farnspeekl(0x46c);
288 /* Reset all IRQ counters */
291 /* Tell codec to start transfer */
292 __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
294 /* Now wait 1/18 seconds */
295 while (timer == _farnspeekl(0x46c));
296 __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
297 dma_disable(wss.dma);
299 /* Given frequency 5510Hz, a buffer size of 1 byte and a time interval
300 of 1/18.2 second, we should have received about 302 interrupts */
301 for (i = 2; i <= 10; i++) {
302 int count = abs(302 - irq_detect_get(i, &irqmask));
304 wss.irq = i, delta = count;
317 /*************************************************** High-level interface *****/
319 /* Detect whenever WSS is present and fill "wss" structure */
324 /* Try to find the port and DMA from environment */
327 while (env && *env) {
328 /* Skip whitespace */
329 while ((*env == ' ') || (*env == '\t'))
338 wss.port = strtol(env, &env, 16);
343 wss.irq = strtol(env, &env, 10);
348 wss.dma = strtol(env, &env, 10);
351 /* Skip other values */
352 while (*env && (*env != ' ') && (*env != '\t'))
358 /* Try to fill the gaps in wss hardware parameters */
361 if (!wss.port || !wss.irq || !wss.dma)
381 /* Open WSS for usage */
384 __dpmi_meminfo struct_info;
393 /* Now lock the wss structure in memory */
394 struct_info.address = __djgpp_base_address + (unsigned long)&wss;
395 struct_info.size = sizeof(wss);
396 if (__dpmi_lock_linear_region(&struct_info))
399 /* Hook the WSS IRQ */
401 irq_hook(wss.irq, wss_irq, (long)wss_irq_end - (long)wss_irq);
402 if (!wss.irq_handle) {
403 __dpmi_unlock_linear_region(&struct_info);
407 /* Enable the interrupt */
408 irq_enable(wss.irq_handle);
417 /* Finish working with WSS */
420 __dpmi_meminfo struct_info;
426 /* Stop/free DMA buffer */
430 irq_unhook(wss.irq_handle);
431 wss.irq_handle = NULL;
433 /* Unlock the wss structure */
434 struct_info.address = __djgpp_base_address + (unsigned long)&wss;
435 struct_info.size = sizeof(wss);
436 __dpmi_unlock_linear_region(&struct_info);
441 /* Adjust frequency rate to nearest WSS available */
442 unsigned int wss_adjust_freq(unsigned int freq)
444 __wss_getrate(&freq);
448 /* Enable/disable speaker output */
449 /* Start playing from DMA buffer in either 8/16 bit mono/stereo */
450 boolean wss_start_dma(unsigned char mode, unsigned int freq)
453 unsigned char format;
455 /* Stop DMA transfer if it is enabled */
458 /* Sanity check: we support only 8-bit unsigned and 16-bit signed formats */
459 if (((mode & WSSMODE_16BITS) && !(mode & WSSMODE_SIGNED))
460 || (!(mode & WSSMODE_16BITS) && (mode & WSSMODE_SIGNED)))
463 /* Find the nearest frequency divisor (rate) */
464 format = __wss_getrate(&freq);
467 /* Get a DMA buffer enough for a 1sec interval... 4K <= dmasize <= 32K */
469 if (mode & WSSMODE_STEREO)
471 if (mode & WSSMODE_16BITS)
474 if (dmabuffsize < 4096)
476 if (dmabuffsize > 32768)
478 dmabuffsize = (dmabuffsize + 255) & 0xffffff00;
480 wss.dma_buff = dma_allocate(wss.dma, dmabuffsize);
484 /* Fill DMA buffer with silence */
485 dmabuffsize = wss.dma_buff->size;
486 if (mode & WSSMODE_SIGNED)
487 memset(wss.dma_buff->linear, 0, dmabuffsize);
489 memset(wss.dma_buff->linear, 0x80, dmabuffsize);
491 /* Check data size and build a WSSR_PLAY_FORMAT value accordingly */
492 wss.samples = dmabuffsize;
493 if (mode & WSSMODE_16BITS) {
495 format |= WSSM_16BITS;
498 if (mode & WSSMODE_STEREO) {
500 format |= WSSM_STEREO;
503 if (!__wss_setformat(format)) {
508 /* Prime DMA for transfer */
509 dma_start(wss.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
511 /* Tell codec how many samples to transfer */
512 wss.samples = (wss.samples >> 1) - 1;
513 __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff);
514 __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8);
516 /* Tell codec to start transfer */
517 __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
522 /* Stop playing from DMA buffer */
528 __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
529 dma_disable(wss.dma);
530 dma_free(wss.dma_buff);
534 /* Query current position/total size of the DMA buffer */
535 void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos)
537 unsigned int dma_left;
538 *dma_size = wss.dma_buff->size;
539 /* It can happen we try to read DMA count when HI/LO bytes will be
542 unsigned int dma_left_test;
543 dma_clear_ff(wss.dma);
544 dma_left_test = dma_get_count(wss.dma);
545 dma_left = dma_get_count(wss.dma);
546 if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10))
549 *dma_pos = *dma_size - dma_left;
552 void wss_output(boolean enable)
555 wss.curlevel = wss.level;
559 __wss_outreg(WSSR_MASTER_L, wss.curlevel);
560 __wss_outreg(WSSR_MASTER_R, wss.curlevel);
563 void wss_level(int level)
569 wss.curlevel = wss.level = level ^ 63;
571 __wss_outreg(WSSR_MASTER_L, wss.curlevel);
572 __wss_outreg(WSSR_MASTER_R, wss.curlevel);