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
17static isa_module_info* gISA;
18
19
20static void
21hw_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
36static int
37hw_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
51static void
52hw_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
60static int
61hw_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
69static int
70hw_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
88static void
89hw_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
107static void
108hw_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
133static void
134hw_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
152static void
153hw_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
160static int32
161hw_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
205static status_t
206hw_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
227static status_t
228hw_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
249status_t
250sb16_stream_setup_buffers(sb16_dev_t* dev, sb16_stream_t* s, const char* desc)
251{
252	return B_OK;
253}
254
255
256status_t
257sb16_stream_start(sb16_dev_t* dev, sb16_stream_t* s)
258{
259	return B_OK;
260}
261
262
263status_t
264sb16_stream_stop(sb16_dev_t* dev, sb16_stream_t* s)
265{
266	return B_OK;
267}
268
269
270void
271sb16_stream_buffer_done(sb16_stream_t* stream)
272{
273}
274
275
276//#pragma mark -
277
278
279status_t
280sb16_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
300void
301sb16_hw_stop(sb16_dev_t* dev)
302{
303}
304
305
306void
307sb16_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