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