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
16static isa_module_info* gISA;
17
18static void
19hw_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
33static int
34hw_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
47static void
48hw_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
55static int
56hw_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
64static int
65hw_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
79static void
80hw_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
97static void
98hw_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
121static void
122hw_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
139static void
140hw_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
146static int32
147hw_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
192static status_t
193hw_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
213static status_t
214hw_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
233status_t
234sb16_stream_setup_buffers(sb16_dev_t* dev, sb16_stream_t* s, const char* desc)
235{
236	return B_OK;
237}
238
239status_t
240sb16_stream_start(sb16_dev_t* dev, sb16_stream_t* s)
241{
242	return B_OK;
243}
244
245status_t
246sb16_stream_stop(sb16_dev_t* dev, sb16_stream_t* s)
247{
248	return B_OK;
249}
250
251void
252sb16_stream_buffer_done(sb16_stream_t* stream)
253{
254}
255
256//#pragma mark -
257
258status_t
259sb16_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
278void
279sb16_hw_stop(sb16_dev_t* dev)
280{
281}
282
283void
284sb16_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