xref: /haiku/src/add-ons/kernel/drivers/audio/sb16/sb16_hw.c (revision 99351a72d3638705c3323c36fd551f1d3cd2111c)
1633b9791SIthamar R. Adema /*
2633b9791SIthamar R. Adema  * This code is very much based on the BeOS R4_ sb16 driver, which is:
3633b9791SIthamar R. Adema  * Copyright (C) 1998 Carlos Hasan. All Rights Reserved.
4633b9791SIthamar R. Adema  *
5633b9791SIthamar R. Adema  * This program is free software; you can redistribute it and/or modify
6633b9791SIthamar R. Adema  * it under the terms of the GNU General Public License as published by
7633b9791SIthamar R. Adema  * the Free Software Foundation; either version 2 of the License, or
8633b9791SIthamar R. Adema  * (at your option) any later version.
9633b9791SIthamar R. Adema  */
10633b9791SIthamar R. Adema 
11633b9791SIthamar R. Adema #include "driver.h"
12633b9791SIthamar R. Adema #include "hardware.h"
13633b9791SIthamar R. Adema 
14633b9791SIthamar R. Adema #include <drivers/ISA.h>
15633b9791SIthamar R. Adema 
16*99351a72SPulkoMandy 
17633b9791SIthamar R. Adema static isa_module_info* gISA;
18633b9791SIthamar R. Adema 
19*99351a72SPulkoMandy 
20633b9791SIthamar R. Adema static void
hw_codec_write_byte(sb16_dev_t * dev,uint8 value)21633b9791SIthamar R. Adema hw_codec_write_byte(sb16_dev_t* dev, uint8 value)
22633b9791SIthamar R. Adema {
23633b9791SIthamar R. Adema 	int i;
24633b9791SIthamar R. Adema 
25633b9791SIthamar R. Adema 	/* wait until the DSP is ready to receive data */
26633b9791SIthamar R. Adema 	for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
27633b9791SIthamar R. Adema 		if (!(gISA->read_io_8(dev->port + SB16_CODEC_WRITE_STATUS) & 0x80))
28633b9791SIthamar R. Adema 			break;
29633b9791SIthamar R. Adema 	}
30633b9791SIthamar R. Adema 
31633b9791SIthamar R. Adema 	/* write a byte to the DSP data port */
32633b9791SIthamar R. Adema 	gISA->write_io_8(dev->port + SB16_CODEC_WRITE_DATA, value);
33633b9791SIthamar R. Adema }
34633b9791SIthamar R. Adema 
35*99351a72SPulkoMandy 
36633b9791SIthamar R. Adema static int
hw_codec_read_byte(sb16_dev_t * dev)37633b9791SIthamar R. Adema hw_codec_read_byte(sb16_dev_t* dev)
38633b9791SIthamar R. Adema {
39633b9791SIthamar R. Adema 	int i;
40633b9791SIthamar R. Adema 	/* wait until the DSP has data available */
41633b9791SIthamar R. Adema 	for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
42633b9791SIthamar R. Adema 		if (gISA->read_io_8(dev->port + SB16_CODEC_READ_STATUS) & 0x80)
43633b9791SIthamar R. Adema 			break;
44633b9791SIthamar R. Adema 	}
45633b9791SIthamar R. Adema 
46633b9791SIthamar R. Adema 	/* read a byte from the DSP data port */
47633b9791SIthamar R. Adema 	return gISA->read_io_8(dev->port + SB16_CODEC_READ_DATA);
48633b9791SIthamar R. Adema }
49633b9791SIthamar R. Adema 
50*99351a72SPulkoMandy 
51633b9791SIthamar R. Adema static void
hw_codec_reg_write(sb16_dev_t * dev,uint8 index,uint8 value)52633b9791SIthamar R. Adema hw_codec_reg_write(sb16_dev_t* dev, uint8 index, uint8 value)
53633b9791SIthamar R. Adema {
54633b9791SIthamar R. Adema 	/* write a Mixer indirect register */
55633b9791SIthamar R. Adema 	gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
56633b9791SIthamar R. Adema 	gISA->write_io_8(dev->port + SB16_MIXER_DATA, value);
57633b9791SIthamar R. Adema }
58633b9791SIthamar R. Adema 
59*99351a72SPulkoMandy 
60633b9791SIthamar R. Adema static int
hw_codec_reg_read(sb16_dev_t * dev,uint8 index)61633b9791SIthamar R. Adema hw_codec_reg_read(sb16_dev_t* dev, uint8 index)
62633b9791SIthamar R. Adema {
63633b9791SIthamar R. Adema 	/* read a Mixer indirect register */
64633b9791SIthamar R. Adema 	gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
65633b9791SIthamar R. Adema 	return gISA->read_io_8(dev->port + SB16_MIXER_DATA);
66633b9791SIthamar R. Adema }
67633b9791SIthamar R. Adema 
68633b9791SIthamar R. Adema 
69633b9791SIthamar R. Adema static int
hw_codec_read_version(sb16_dev_t * dev)70633b9791SIthamar R. Adema hw_codec_read_version(sb16_dev_t* dev)
71633b9791SIthamar R. Adema {
72633b9791SIthamar R. Adema 	int minor, major;
73633b9791SIthamar R. Adema 
74633b9791SIthamar R. Adema 	/* query the DSP hardware version number */
75633b9791SIthamar R. Adema 	hw_codec_write_byte(dev, SB16_CODEC_VERSION);
76633b9791SIthamar R. Adema 	major = hw_codec_read_byte(dev);
77633b9791SIthamar R. Adema 	minor = hw_codec_read_byte(dev);
78633b9791SIthamar R. Adema 
79633b9791SIthamar R. Adema 	dprintf("%s: SB16 version %d.%d\n", __func__, major, minor);
80633b9791SIthamar R. Adema 
81633b9791SIthamar R. Adema 	return (major << 8) + minor;
82633b9791SIthamar R. Adema }
83633b9791SIthamar R. Adema 
84*99351a72SPulkoMandy 
85*99351a72SPulkoMandy #if 0
86*99351a72SPulkoMandy // TODO for recording support
87*99351a72SPulkoMandy 
88633b9791SIthamar R. Adema static void
89633b9791SIthamar R. Adema hw_codec_read_irq_setup(sb16_dev_t* dev)
90633b9791SIthamar R. Adema {
91633b9791SIthamar R. Adema 	/* query the current IRQ line resource */
92633b9791SIthamar R. Adema 	int mask = hw_codec_reg_read(dev, SB16_IRQ_SETUP);
93633b9791SIthamar R. Adema 
94633b9791SIthamar R. Adema 	dev->irq = 5;
95633b9791SIthamar R. Adema 
96633b9791SIthamar R. Adema 	if (mask & 0x01)
97633b9791SIthamar R. Adema 		dev->irq = 2;
98633b9791SIthamar R. Adema 	if (mask & 0x02)
99633b9791SIthamar R. Adema 		dev->irq = 5;
100633b9791SIthamar R. Adema 	if (mask & 0x04)
101633b9791SIthamar R. Adema 		dev->irq = 7;
102633b9791SIthamar R. Adema 	if (mask & 0x08)
103633b9791SIthamar R. Adema 		dev->irq = 10;
104633b9791SIthamar R. Adema }
105633b9791SIthamar R. Adema 
106*99351a72SPulkoMandy 
107633b9791SIthamar R. Adema static void
108633b9791SIthamar R. Adema hw_codec_read_dma_setup(sb16_dev_t* dev)
109633b9791SIthamar R. Adema {
110633b9791SIthamar R. Adema     /* query the current DMA channel resources */
111633b9791SIthamar R. Adema     int mask = hw_codec_reg_read(dev, SB16_DMA_SETUP);
112633b9791SIthamar R. Adema 
113633b9791SIthamar R. Adema     dev->dma8 = 1;
114633b9791SIthamar R. Adema 
115633b9791SIthamar R. Adema     if (mask & 0x01)
116633b9791SIthamar R. Adema         dev->dma8 = 0;
117633b9791SIthamar R. Adema     if (mask & 0x02)
118633b9791SIthamar R. Adema         dev->dma8 = 1;
119633b9791SIthamar R. Adema     if (mask & 0x08)
120633b9791SIthamar R. Adema         dev->dma8 = 3;
121633b9791SIthamar R. Adema 
122633b9791SIthamar R. Adema     dev->dma16 = dev->dma8;
123633b9791SIthamar R. Adema     if (mask & 0x20)
124633b9791SIthamar R. Adema         dev->dma16 = 5;
125633b9791SIthamar R. Adema     if (mask & 0x40)
126633b9791SIthamar R. Adema         dev->dma16 = 6;
127633b9791SIthamar R. Adema     if (mask & 0x80)
128633b9791SIthamar R. Adema         dev->dma16 = 7;
129633b9791SIthamar R. Adema }
130*99351a72SPulkoMandy #endif
131*99351a72SPulkoMandy 
132633b9791SIthamar R. Adema 
133633b9791SIthamar R. Adema static void
hw_codec_write_irq_setup(sb16_dev_t * dev)134633b9791SIthamar R. Adema hw_codec_write_irq_setup(sb16_dev_t* dev)
135633b9791SIthamar R. Adema {
136633b9791SIthamar R. Adema 	/* change programmable IRQ line resource */
137633b9791SIthamar R. Adema 	int mask = 0x02;
138633b9791SIthamar R. Adema 
139633b9791SIthamar R. Adema 	if (dev->irq == 2)
140633b9791SIthamar R. Adema 		mask = 0x01;
141633b9791SIthamar R. Adema 	if (dev->irq == 5)
142633b9791SIthamar R. Adema 		mask = 0x02;
143633b9791SIthamar R. Adema 	if (dev->irq == 7)
144633b9791SIthamar R. Adema 		mask = 0x04;
145633b9791SIthamar R. Adema 	if (dev->irq == 10)
146633b9791SIthamar R. Adema 		mask = 0x08;
147633b9791SIthamar R. Adema 
148633b9791SIthamar R. Adema 	hw_codec_reg_write(dev, SB16_IRQ_SETUP, mask);
149633b9791SIthamar R. Adema }
150633b9791SIthamar R. Adema 
151*99351a72SPulkoMandy 
152633b9791SIthamar R. Adema static void
hw_codec_write_dma_setup(sb16_dev_t * dev)153633b9791SIthamar R. Adema hw_codec_write_dma_setup(sb16_dev_t* dev)
154633b9791SIthamar R. Adema {
155633b9791SIthamar R. Adema 	/* change programmable DMA channel resources */
156633b9791SIthamar R. Adema 	hw_codec_reg_write(dev, SB16_DMA_SETUP, (1 << dev->dma8) | (1 << dev->dma16));
157633b9791SIthamar R. Adema }
158633b9791SIthamar R. Adema 
159*99351a72SPulkoMandy 
160633b9791SIthamar R. Adema static int32
hw_codec_inth(void * cookie)161633b9791SIthamar R. Adema hw_codec_inth(void* cookie)
162633b9791SIthamar R. Adema {
163633b9791SIthamar R. Adema 	sb16_dev_t* dev = (sb16_dev_t*)cookie;
164633b9791SIthamar R. Adema 	int32 rc = B_UNHANDLED_INTERRUPT;
165633b9791SIthamar R. Adema 
166633b9791SIthamar R. Adema 	/* read the IRQ interrupt status register */
167633b9791SIthamar R. Adema 	int status = hw_codec_reg_read(dev, SB16_IRQ_STATUS);
168633b9791SIthamar R. Adema 
169633b9791SIthamar R. Adema 	/* check if this hardware raised this interrupt */
170633b9791SIthamar R. Adema 	if (status & 0x03) {
171633b9791SIthamar R. Adema 		rc = B_HANDLED_INTERRUPT;
172633b9791SIthamar R. Adema 
173633b9791SIthamar R. Adema 		/* acknowledge DMA memory transfers */
174633b9791SIthamar R. Adema 		if (status & 0x01)
175633b9791SIthamar R. Adema 			gISA->read_io_8(dev->port + SB16_CODEC_ACK_8_BIT);
176633b9791SIthamar R. Adema 		if (status & 0x02)
177633b9791SIthamar R. Adema 			gISA->read_io_8(dev->port + SB16_CODEC_ACK_16_BIT);
178633b9791SIthamar R. Adema 
179633b9791SIthamar R. Adema 		/* acknowledge PIC interrupt signal */
180633b9791SIthamar R. Adema 		if (dev->irq >= 8)
181633b9791SIthamar R. Adema 			gISA->write_io_8(0xa0, 0x20);
182633b9791SIthamar R. Adema 
183633b9791SIthamar R. Adema 		gISA->write_io_8(0x20, 0x20);
184633b9791SIthamar R. Adema 
185633b9791SIthamar R. Adema 		/* handle buffer finished interrupt */
186633b9791SIthamar R. Adema 		if (((dev->playback_stream.bits >> 3) & status) != 0) {
187633b9791SIthamar R. Adema 			sb16_stream_buffer_done(&dev->playback_stream);
188633b9791SIthamar R. Adema 			rc = B_INVOKE_SCHEDULER;
189633b9791SIthamar R. Adema 		}
190633b9791SIthamar R. Adema 		if (((dev->record_stream.bits >> 3) & status) != 0) {
191633b9791SIthamar R. Adema 			sb16_stream_buffer_done(&dev->record_stream);
192633b9791SIthamar R. Adema 			rc = B_INVOKE_SCHEDULER;
193633b9791SIthamar R. Adema 		}
194633b9791SIthamar R. Adema 
195633b9791SIthamar R. Adema 		if ((status & 0x04) != 0) {
196633b9791SIthamar R. Adema 			/* MIDI stream done */
197633b9791SIthamar R. Adema 			rc = B_INVOKE_SCHEDULER;
198633b9791SIthamar R. Adema 		}
199633b9791SIthamar R. Adema 	}
200633b9791SIthamar R. Adema 
201633b9791SIthamar R. Adema 	return rc;
202633b9791SIthamar R. Adema }
203633b9791SIthamar R. Adema 
204633b9791SIthamar R. Adema 
205633b9791SIthamar R. Adema static status_t
hw_codec_reset(sb16_dev_t * dev)206633b9791SIthamar R. Adema hw_codec_reset(sb16_dev_t* dev)
207633b9791SIthamar R. Adema {
208633b9791SIthamar R. Adema 	int times, delay;
209633b9791SIthamar R. Adema 
210633b9791SIthamar R. Adema 	/* try to reset the DSP hardware */
211633b9791SIthamar R. Adema 	for (times = 0; times < 10; times++) {
212633b9791SIthamar R. Adema 		gISA->write_io_8(dev->port + SB16_CODEC_RESET, 1);
213633b9791SIthamar R. Adema 
214633b9791SIthamar R. Adema 		for (delay = 0; delay < SB16_CODEC_RESET_DELAY; delay++)
215633b9791SIthamar R. Adema 			gISA->read_io_8(dev->port + SB16_CODEC_RESET);
216633b9791SIthamar R. Adema 
217633b9791SIthamar R. Adema 		gISA->write_io_8(dev->port + SB16_CODEC_RESET, 0);
218633b9791SIthamar R. Adema 
219633b9791SIthamar R. Adema 		if (hw_codec_read_byte(dev) == 0xaa)
220633b9791SIthamar R. Adema 			return B_OK;
221633b9791SIthamar R. Adema 	}
222633b9791SIthamar R. Adema 
223633b9791SIthamar R. Adema 	return B_IO_ERROR;
224633b9791SIthamar R. Adema }
225633b9791SIthamar R. Adema 
226*99351a72SPulkoMandy 
227633b9791SIthamar R. Adema static status_t
hw_codec_detect(sb16_dev_t * dev)228633b9791SIthamar R. Adema hw_codec_detect(sb16_dev_t* dev)
229633b9791SIthamar R. Adema {
230633b9791SIthamar R. Adema 	status_t rc;
231633b9791SIthamar R. Adema 
232633b9791SIthamar R. Adema 	if ((rc=hw_codec_reset(dev)) == B_OK) {
233633b9791SIthamar R. Adema 		if (hw_codec_read_version(dev) >= 0x400) {
234633b9791SIthamar R. Adema 			hw_codec_write_irq_setup(dev);
235633b9791SIthamar R. Adema 			hw_codec_write_dma_setup(dev);
236633b9791SIthamar R. Adema 			rc = B_OK;
237633b9791SIthamar R. Adema 		} else {
238633b9791SIthamar R. Adema 			rc = B_BAD_VALUE;
239633b9791SIthamar R. Adema 		}
240633b9791SIthamar R. Adema 	}
241633b9791SIthamar R. Adema 
242633b9791SIthamar R. Adema 	return rc;
243633b9791SIthamar R. Adema }
244633b9791SIthamar R. Adema 
245*99351a72SPulkoMandy 
246633b9791SIthamar R. Adema //#pragma mark -
247633b9791SIthamar R. Adema 
248*99351a72SPulkoMandy 
249633b9791SIthamar R. Adema status_t
sb16_stream_setup_buffers(sb16_dev_t * dev,sb16_stream_t * s,const char * desc)250633b9791SIthamar R. Adema sb16_stream_setup_buffers(sb16_dev_t* dev, sb16_stream_t* s, const char* desc)
251633b9791SIthamar R. Adema {
252633b9791SIthamar R. Adema 	return B_OK;
253633b9791SIthamar R. Adema }
254633b9791SIthamar R. Adema 
255*99351a72SPulkoMandy 
256633b9791SIthamar R. Adema status_t
sb16_stream_start(sb16_dev_t * dev,sb16_stream_t * s)257633b9791SIthamar R. Adema sb16_stream_start(sb16_dev_t* dev, sb16_stream_t* s)
258633b9791SIthamar R. Adema {
259633b9791SIthamar R. Adema 	return B_OK;
260633b9791SIthamar R. Adema }
261633b9791SIthamar R. Adema 
262*99351a72SPulkoMandy 
263633b9791SIthamar R. Adema status_t
sb16_stream_stop(sb16_dev_t * dev,sb16_stream_t * s)264633b9791SIthamar R. Adema sb16_stream_stop(sb16_dev_t* dev, sb16_stream_t* s)
265633b9791SIthamar R. Adema {
266633b9791SIthamar R. Adema 	return B_OK;
267633b9791SIthamar R. Adema }
268633b9791SIthamar R. Adema 
269*99351a72SPulkoMandy 
270633b9791SIthamar R. Adema void
sb16_stream_buffer_done(sb16_stream_t * stream)271633b9791SIthamar R. Adema sb16_stream_buffer_done(sb16_stream_t* stream)
272633b9791SIthamar R. Adema {
273633b9791SIthamar R. Adema }
274633b9791SIthamar R. Adema 
275*99351a72SPulkoMandy 
276633b9791SIthamar R. Adema //#pragma mark -
277633b9791SIthamar R. Adema 
278*99351a72SPulkoMandy 
279633b9791SIthamar R. Adema status_t
sb16_hw_init(sb16_dev_t * dev)280633b9791SIthamar R. Adema sb16_hw_init(sb16_dev_t* dev)
281633b9791SIthamar R. Adema {
282633b9791SIthamar R. Adema 	status_t rc;
283633b9791SIthamar R. Adema 
284633b9791SIthamar R. Adema 	/* First of all, grab the ISA module */
285633b9791SIthamar R. Adema 	if ((rc=get_module(B_ISA_MODULE_NAME, (module_info**)&gISA)) != B_OK)
286633b9791SIthamar R. Adema 		return rc;
287633b9791SIthamar R. Adema 
288633b9791SIthamar R. Adema 	/* Check if the hardware is sensible... */
289633b9791SIthamar R. Adema 	if ((rc=hw_codec_detect(dev)) == B_OK) {
290633b9791SIthamar R. Adema 		if ((rc=gISA->lock_isa_dma_channel(dev->dma8)) == B_OK &&
291633b9791SIthamar R. Adema 			(rc=gISA->lock_isa_dma_channel(dev->dma16)) == B_OK) {
292633b9791SIthamar R. Adema 			rc = install_io_interrupt_handler(dev->irq, hw_codec_inth, dev, 0);
293633b9791SIthamar R. Adema 		}
294633b9791SIthamar R. Adema 	}
295633b9791SIthamar R. Adema 
296633b9791SIthamar R. Adema 	return rc;
297633b9791SIthamar R. Adema }
298633b9791SIthamar R. Adema 
299*99351a72SPulkoMandy 
300633b9791SIthamar R. Adema void
sb16_hw_stop(sb16_dev_t * dev)301633b9791SIthamar R. Adema sb16_hw_stop(sb16_dev_t* dev)
302633b9791SIthamar R. Adema {
303633b9791SIthamar R. Adema }
304633b9791SIthamar R. Adema 
305*99351a72SPulkoMandy 
306633b9791SIthamar R. Adema void
sb16_hw_uninit(sb16_dev_t * dev)307633b9791SIthamar R. Adema sb16_hw_uninit(sb16_dev_t* dev)
308633b9791SIthamar R. Adema {
309633b9791SIthamar R. Adema 	remove_io_interrupt_handler(dev->irq, hw_codec_inth, dev);
310633b9791SIthamar R. Adema 
311633b9791SIthamar R. Adema 	if (gISA != NULL) {
312633b9791SIthamar R. Adema 		gISA->unlock_isa_dma_channel(dev->dma8);
313633b9791SIthamar R. Adema 
314633b9791SIthamar R. Adema 		if (dev->dma8 != dev->dma16)
315633b9791SIthamar R. Adema 			gISA->unlock_isa_dma_channel(dev->dma16);
316633b9791SIthamar R. Adema 
317633b9791SIthamar R. Adema 		put_module(B_ISA_MODULE_NAME);
318633b9791SIthamar R. Adema 	}
319633b9791SIthamar R. Adema 
320633b9791SIthamar R. Adema }
321633b9791SIthamar R. Adema 
322