xref: /haiku/src/add-ons/kernel/drivers/audio/sb16/sb16_hw.c (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
1 /*
2  * This code is very much based on the BeOS R4_ sb16 driver, which is:
3  * Copyright (C) 1998 Carlos Hasan. All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  */
10 
11 #include "driver.h"
12 #include "hardware.h"
13 
14 #include <drivers/ISA.h>
15 
16 
17 static isa_module_info* gISA;
18 
19 
20 static void
21 hw_codec_write_byte(sb16_dev_t* dev, uint8 value)
22 {
23 	int i;
24 
25 	/* wait until the DSP is ready to receive data */
26 	for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
27 		if (!(gISA->read_io_8(dev->port + SB16_CODEC_WRITE_STATUS) & 0x80))
28 			break;
29 	}
30 
31 	/* write a byte to the DSP data port */
32 	gISA->write_io_8(dev->port + SB16_CODEC_WRITE_DATA, value);
33 }
34 
35 
36 static int
37 hw_codec_read_byte(sb16_dev_t* dev)
38 {
39 	int i;
40 	/* wait until the DSP has data available */
41 	for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
42 		if (gISA->read_io_8(dev->port + SB16_CODEC_READ_STATUS) & 0x80)
43 			break;
44 	}
45 
46 	/* read a byte from the DSP data port */
47 	return gISA->read_io_8(dev->port + SB16_CODEC_READ_DATA);
48 }
49 
50 
51 static void
52 hw_codec_reg_write(sb16_dev_t* dev, uint8 index, uint8 value)
53 {
54 	/* write a Mixer indirect register */
55 	gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
56 	gISA->write_io_8(dev->port + SB16_MIXER_DATA, value);
57 }
58 
59 
60 static int
61 hw_codec_reg_read(sb16_dev_t* dev, uint8 index)
62 {
63 	/* read a Mixer indirect register */
64 	gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
65 	return gISA->read_io_8(dev->port + SB16_MIXER_DATA);
66 }
67 
68 
69 static int
70 hw_codec_read_version(sb16_dev_t* dev)
71 {
72 	int minor, major;
73 
74 	/* query the DSP hardware version number */
75 	hw_codec_write_byte(dev, SB16_CODEC_VERSION);
76 	major = hw_codec_read_byte(dev);
77 	minor = hw_codec_read_byte(dev);
78 
79 	dprintf("%s: SB16 version %d.%d\n", __func__, major, minor);
80 
81 	return (major << 8) + minor;
82 }
83 
84 
85 #if 0
86 // TODO for recording support
87 
88 static void
89 hw_codec_read_irq_setup(sb16_dev_t* dev)
90 {
91 	/* query the current IRQ line resource */
92 	int mask = hw_codec_reg_read(dev, SB16_IRQ_SETUP);
93 
94 	dev->irq = 5;
95 
96 	if (mask & 0x01)
97 		dev->irq = 2;
98 	if (mask & 0x02)
99 		dev->irq = 5;
100 	if (mask & 0x04)
101 		dev->irq = 7;
102 	if (mask & 0x08)
103 		dev->irq = 10;
104 }
105 
106 
107 static void
108 hw_codec_read_dma_setup(sb16_dev_t* dev)
109 {
110     /* query the current DMA channel resources */
111     int mask = hw_codec_reg_read(dev, SB16_DMA_SETUP);
112 
113     dev->dma8 = 1;
114 
115     if (mask & 0x01)
116         dev->dma8 = 0;
117     if (mask & 0x02)
118         dev->dma8 = 1;
119     if (mask & 0x08)
120         dev->dma8 = 3;
121 
122     dev->dma16 = dev->dma8;
123     if (mask & 0x20)
124         dev->dma16 = 5;
125     if (mask & 0x40)
126         dev->dma16 = 6;
127     if (mask & 0x80)
128         dev->dma16 = 7;
129 }
130 #endif
131 
132 
133 static void
134 hw_codec_write_irq_setup(sb16_dev_t* dev)
135 {
136 	/* change programmable IRQ line resource */
137 	int mask = 0x02;
138 
139 	if (dev->irq == 2)
140 		mask = 0x01;
141 	if (dev->irq == 5)
142 		mask = 0x02;
143 	if (dev->irq == 7)
144 		mask = 0x04;
145 	if (dev->irq == 10)
146 		mask = 0x08;
147 
148 	hw_codec_reg_write(dev, SB16_IRQ_SETUP, mask);
149 }
150 
151 
152 static void
153 hw_codec_write_dma_setup(sb16_dev_t* dev)
154 {
155 	/* change programmable DMA channel resources */
156 	hw_codec_reg_write(dev, SB16_DMA_SETUP, (1 << dev->dma8) | (1 << dev->dma16));
157 }
158 
159 
160 static int32
161 hw_codec_inth(void* cookie)
162 {
163 	sb16_dev_t* dev = (sb16_dev_t*)cookie;
164 	int32 rc = B_UNHANDLED_INTERRUPT;
165 
166 	/* read the IRQ interrupt status register */
167 	int status = hw_codec_reg_read(dev, SB16_IRQ_STATUS);
168 
169 	/* check if this hardware raised this interrupt */
170 	if (status & 0x03) {
171 		rc = B_HANDLED_INTERRUPT;
172 
173 		/* acknowledge DMA memory transfers */
174 		if (status & 0x01)
175 			gISA->read_io_8(dev->port + SB16_CODEC_ACK_8_BIT);
176 		if (status & 0x02)
177 			gISA->read_io_8(dev->port + SB16_CODEC_ACK_16_BIT);
178 
179 		/* acknowledge PIC interrupt signal */
180 		if (dev->irq >= 8)
181 			gISA->write_io_8(0xa0, 0x20);
182 
183 		gISA->write_io_8(0x20, 0x20);
184 
185 		/* handle buffer finished interrupt */
186 		if (((dev->playback_stream.bits >> 3) & status) != 0) {
187 			sb16_stream_buffer_done(&dev->playback_stream);
188 			rc = B_INVOKE_SCHEDULER;
189 		}
190 		if (((dev->record_stream.bits >> 3) & status) != 0) {
191 			sb16_stream_buffer_done(&dev->record_stream);
192 			rc = B_INVOKE_SCHEDULER;
193 		}
194 
195 		if ((status & 0x04) != 0) {
196 			/* MIDI stream done */
197 			rc = B_INVOKE_SCHEDULER;
198 		}
199 	}
200 
201 	return rc;
202 }
203 
204 
205 static status_t
206 hw_codec_reset(sb16_dev_t* dev)
207 {
208 	int times, delay;
209 
210 	/* try to reset the DSP hardware */
211 	for (times = 0; times < 10; times++) {
212 		gISA->write_io_8(dev->port + SB16_CODEC_RESET, 1);
213 
214 		for (delay = 0; delay < SB16_CODEC_RESET_DELAY; delay++)
215 			gISA->read_io_8(dev->port + SB16_CODEC_RESET);
216 
217 		gISA->write_io_8(dev->port + SB16_CODEC_RESET, 0);
218 
219 		if (hw_codec_read_byte(dev) == 0xaa)
220 			return B_OK;
221 	}
222 
223 	return B_IO_ERROR;
224 }
225 
226 
227 static status_t
228 hw_codec_detect(sb16_dev_t* dev)
229 {
230 	status_t rc;
231 
232 	if ((rc=hw_codec_reset(dev)) == B_OK) {
233 		if (hw_codec_read_version(dev) >= 0x400) {
234 			hw_codec_write_irq_setup(dev);
235 			hw_codec_write_dma_setup(dev);
236 			rc = B_OK;
237 		} else {
238 			rc = B_BAD_VALUE;
239 		}
240 	}
241 
242 	return rc;
243 }
244 
245 
246 //#pragma mark -
247 
248 
249 status_t
250 sb16_stream_setup_buffers(sb16_dev_t* dev, sb16_stream_t* s, const char* desc)
251 {
252 	return B_OK;
253 }
254 
255 
256 status_t
257 sb16_stream_start(sb16_dev_t* dev, sb16_stream_t* s)
258 {
259 	return B_OK;
260 }
261 
262 
263 status_t
264 sb16_stream_stop(sb16_dev_t* dev, sb16_stream_t* s)
265 {
266 	return B_OK;
267 }
268 
269 
270 void
271 sb16_stream_buffer_done(sb16_stream_t* stream)
272 {
273 }
274 
275 
276 //#pragma mark -
277 
278 
279 status_t
280 sb16_hw_init(sb16_dev_t* dev)
281 {
282 	status_t rc;
283 
284 	/* First of all, grab the ISA module */
285 	if ((rc=get_module(B_ISA_MODULE_NAME, (module_info**)&gISA)) != B_OK)
286 		return rc;
287 
288 	/* Check if the hardware is sensible... */
289 	if ((rc=hw_codec_detect(dev)) == B_OK) {
290 		if ((rc=gISA->lock_isa_dma_channel(dev->dma8)) == B_OK &&
291 			(rc=gISA->lock_isa_dma_channel(dev->dma16)) == B_OK) {
292 			rc = install_io_interrupt_handler(dev->irq, hw_codec_inth, dev, 0);
293 		}
294 	}
295 
296 	return rc;
297 }
298 
299 
300 void
301 sb16_hw_stop(sb16_dev_t* dev)
302 {
303 }
304 
305 
306 void
307 sb16_hw_uninit(sb16_dev_t* dev)
308 {
309 	remove_io_interrupt_handler(dev->irq, hw_codec_inth, dev);
310 
311 	if (gISA != NULL) {
312 		gISA->unlock_isa_dma_channel(dev->dma8);
313 
314 		if (dev->dma8 != dev->dma16)
315 			gISA->unlock_isa_dma_channel(dev->dma16);
316 
317 		put_module(B_ISA_MODULE_NAME);
318 	}
319 
320 }
321 
322