started the sound blaster audio driver
[bootcensus] / src / au_sb.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
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 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include "audio.h"
20 #include "au_sb.h"
21 #include "asmops.h"
22
23 #define REG_MIXPORT             (base_port + 0x4)
24 #define REG_MIXDATA             (base_port + 0x5)
25 #define REG_RESET               (base_port + 0x6)
26 #define REG_RDATA               (base_port + 0xa)
27 #define REG_WDATA               (base_port + 0xc)
28 #define REG_WSTAT               (base_port + 0xc)
29 #define REG_RSTAT               (base_port + 0xe)
30 #define REG_INTACK              (base_port + 0xe)
31 #define REG_INT16ACK    (base_port + 0xf)
32
33 #define WSTAT_BUSY              0x80
34 #define RSTAT_RDY               0x80
35
36 #define CMD_SET_RATE    0x41
37 #define CMD_PLAY_PCM    0xa6
38 #define CMD_STOP_PCM    0xd9
39 #define CMD_GET_VER             0xe1
40
41 #define VER_MAJOR(x)    ((x) >> 8)
42 #define VER_MINOR(x)    ((x) & 0xff)
43
44 static void write_dsp(unsigned char val);
45 static unsigned char read_dsp(void);
46 static int get_dsp_version(void);
47 static const char *sbname(int ver);
48
49 static int base_port;
50
51 int sb_detect(void)
52 {
53         int i, ver;
54
55         for(i=0; i<6; i++) {
56                 base_port = 0x200 + ((i + 1) << 4);
57                 if(sb_reset_dsp() == 0) {
58                         ver = get_dsp_version();
59                         printf("sb_detect: found %s (DSP v%d.%02d) at port %xh\n", sbname(ver),
60                                         VER_MAJOR(ver), VER_MINOR(ver), base_port);
61                         return 1;
62                 }
63         }
64
65         return 0;
66 }
67
68 int sb_reset_dsp(void)
69 {
70         int i;
71
72         outb(1, REG_RESET);
73         for(i=0; i<3; i++) iodelay();
74         outb(0, REG_RESET);
75
76         for(i=0; i<128; i++) {
77                 if(inb(REG_RSTAT) & RSTAT_RDY) {
78                         if(inb(REG_RDATA) == 0xaa) {
79                                 return 0;
80                         }
81                 }
82         }
83
84         return -1;
85 }
86
87 static void write_dsp(unsigned char val)
88 {
89         while(inb(REG_WSTAT) & WSTAT_BUSY);
90         outb(val, REG_WDATA);
91 }
92
93 static unsigned char read_dsp(void)
94 {
95         while((inb(REG_RSTAT) & RSTAT_RDY) == 0);
96         return inb(REG_RDATA);
97 }
98
99 static int get_dsp_version(void)
100 {
101         int major, minor;
102
103         write_dsp(CMD_GET_VER);
104         major = read_dsp();
105         minor = read_dsp();
106
107         return (major << 8) | minor;
108 }
109
110 #define V(maj, min)     (((maj) << 8) | (min))
111
112 static const char *sbname(int ver)
113 {
114         int major = VER_MAJOR(ver);
115         int minor = VER_MINOR(ver);
116
117         switch(major) {
118         case 1:
119                 if(minor == 5) {
120                         return "Sound Blaster 1.5";
121                 }
122                 return "Sound Blaster 1.0";
123
124         case 2:
125                 if(minor == 1 || minor == 2) {
126                         return "Sound Blaster 2.0";
127                 }
128                 break;
129
130         case 3:
131                 switch(minor) {
132                 case 0:
133                         return "Sound Blaster Pro";
134                 case 1:
135                 case 2:
136                         return "Sound Blaster Pro 2";
137                 case 5:
138                         return "Gallant SC-6000";
139                 default:
140                         break;
141                 }
142                 break;
143
144         case 4:
145                 switch(minor) {
146                 case 4:
147                 case 5:
148                         return "Sound Blaster 16";
149                 case 11:
150                         return "Sound Blaster 16 SCSI-2";
151                 case 12:
152                         return "Sound Blaster AWE 32";
153                 case 13:
154                         return "Sound Blaster ViBRA16C";
155                 case 16:
156                         return "Sound Blaster AWE 64";
157                 default:
158                         break;
159                 }
160                 break;
161         }
162
163         return "Unknown Sound Blaster";
164 }