1 /* 2 * Functions for accessing OPL4 devices 3 * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> 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 * 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, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20 #include "opl4_local.h" 21 #include <sound/initval.h> 22 #include <linux/ioport.h> 23 #include <linux/init.h> 24 #include <asm/io.h> 25 26 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 27 MODULE_DESCRIPTION("OPL4 driver"); 28 MODULE_LICENSE("GPL"); 29 30 static void inline snd_opl4_wait(struct snd_opl4 *opl4) 31 { 32 int timeout = 10; 33 while ((inb(opl4->fm_port) & OPL4_STATUS_BUSY) && --timeout > 0) 34 ; 35 } 36 37 void snd_opl4_write(struct snd_opl4 *opl4, u8 reg, u8 value) 38 { 39 snd_opl4_wait(opl4); 40 outb(reg, opl4->pcm_port); 41 42 snd_opl4_wait(opl4); 43 outb(value, opl4->pcm_port + 1); 44 } 45 46 EXPORT_SYMBOL(snd_opl4_write); 47 48 u8 snd_opl4_read(struct snd_opl4 *opl4, u8 reg) 49 { 50 snd_opl4_wait(opl4); 51 outb(reg, opl4->pcm_port); 52 53 snd_opl4_wait(opl4); 54 return inb(opl4->pcm_port + 1); 55 } 56 57 EXPORT_SYMBOL(snd_opl4_read); 58 59 void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size) 60 { 61 unsigned long flags; 62 u8 memcfg; 63 64 spin_lock_irqsave(&opl4->reg_lock, flags); 65 66 memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); 67 snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT); 68 69 snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16); 70 snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8); 71 snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset); 72 73 snd_opl4_wait(opl4); 74 outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port); 75 snd_opl4_wait(opl4); 76 insb(opl4->pcm_port + 1, buf, size); 77 78 snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg); 79 80 spin_unlock_irqrestore(&opl4->reg_lock, flags); 81 } 82 83 EXPORT_SYMBOL(snd_opl4_read_memory); 84 85 void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, int size) 86 { 87 unsigned long flags; 88 u8 memcfg; 89 90 spin_lock_irqsave(&opl4->reg_lock, flags); 91 92 memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); 93 snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT); 94 95 snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16); 96 snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8); 97 snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset); 98 99 snd_opl4_wait(opl4); 100 outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port); 101 snd_opl4_wait(opl4); 102 outsb(opl4->pcm_port + 1, buf, size); 103 104 snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg); 105 106 spin_unlock_irqrestore(&opl4->reg_lock, flags); 107 } 108 109 EXPORT_SYMBOL(snd_opl4_write_memory); 110 111 static void snd_opl4_enable_opl4(struct snd_opl4 *opl4) 112 { 113 outb(OPL3_REG_MODE, opl4->fm_port + 2); 114 inb(opl4->fm_port); 115 inb(opl4->fm_port); 116 outb(OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE, opl4->fm_port + 3); 117 inb(opl4->fm_port); 118 inb(opl4->fm_port); 119 } 120 121 static int snd_opl4_detect(struct snd_opl4 *opl4) 122 { 123 u8 id1, id2; 124 125 snd_opl4_enable_opl4(opl4); 126 127 id1 = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); 128 snd_printdd("OPL4[02]=%02x\n", id1); 129 switch (id1 & OPL4_DEVICE_ID_MASK) { 130 case 0x20: 131 opl4->hardware = OPL3_HW_OPL4; 132 break; 133 case 0x40: 134 opl4->hardware = OPL3_HW_OPL4_ML; 135 break; 136 default: 137 return -ENODEV; 138 } 139 140 snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x00); 141 snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0xff); 142 id1 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_FM); 143 id2 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_PCM); 144 snd_printdd("OPL4 id1=%02x id2=%02x\n", id1, id2); 145 if (id1 != 0x00 || id2 != 0xff) 146 return -ENODEV; 147 148 snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x3f); 149 snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0x3f); 150 snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, 0x00); 151 return 0; 152 } 153 154 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) 155 static void snd_opl4_seq_dev_free(struct snd_seq_device *seq_dev) 156 { 157 struct snd_opl4 *opl4 = seq_dev->private_data; 158 opl4->seq_dev = NULL; 159 } 160 161 static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device) 162 { 163 opl4->seq_dev_num = seq_device; 164 if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4, 165 sizeof(struct snd_opl4 *), &opl4->seq_dev) >= 0) { 166 strcpy(opl4->seq_dev->name, "OPL4 Wavetable"); 167 *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4; 168 opl4->seq_dev->private_data = opl4; 169 opl4->seq_dev->private_free = snd_opl4_seq_dev_free; 170 } 171 return 0; 172 } 173 #endif 174 175 static void snd_opl4_free(struct snd_opl4 *opl4) 176 { 177 #ifdef CONFIG_PROC_FS 178 snd_opl4_free_proc(opl4); 179 #endif 180 release_and_free_resource(opl4->res_fm_port); 181 release_and_free_resource(opl4->res_pcm_port); 182 kfree(opl4); 183 } 184 185 static int snd_opl4_dev_free(struct snd_device *device) 186 { 187 struct snd_opl4 *opl4 = device->device_data; 188 snd_opl4_free(opl4); 189 return 0; 190 } 191 192 int snd_opl4_create(struct snd_card *card, 193 unsigned long fm_port, unsigned long pcm_port, 194 int seq_device, 195 struct snd_opl3 **ropl3, struct snd_opl4 **ropl4) 196 { 197 struct snd_opl4 *opl4; 198 struct snd_opl3 *opl3; 199 int err; 200 static struct snd_device_ops ops = { 201 .dev_free = snd_opl4_dev_free 202 }; 203 204 if (ropl3) 205 *ropl3 = NULL; 206 if (ropl4) 207 *ropl4 = NULL; 208 209 opl4 = kzalloc(sizeof(*opl4), GFP_KERNEL); 210 if (!opl4) 211 return -ENOMEM; 212 213 opl4->res_fm_port = request_region(fm_port, 8, "OPL4 FM"); 214 opl4->res_pcm_port = request_region(pcm_port, 8, "OPL4 PCM/MIX"); 215 if (!opl4->res_fm_port || !opl4->res_pcm_port) { 216 snd_printk(KERN_ERR "opl4: can't grab ports 0x%lx, 0x%lx\n", fm_port, pcm_port); 217 snd_opl4_free(opl4); 218 return -EBUSY; 219 } 220 221 opl4->card = card; 222 opl4->fm_port = fm_port; 223 opl4->pcm_port = pcm_port; 224 spin_lock_init(&opl4->reg_lock); 225 mutex_init(&opl4->access_mutex); 226 227 err = snd_opl4_detect(opl4); 228 if (err < 0) { 229 snd_opl4_free(opl4); 230 snd_printd("OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port); 231 return err; 232 } 233 234 err = snd_device_new(card, SNDRV_DEV_CODEC, opl4, &ops); 235 if (err < 0) { 236 snd_opl4_free(opl4); 237 return err; 238 } 239 240 err = snd_opl3_create(card, fm_port, fm_port + 2, opl4->hardware, 1, &opl3); 241 if (err < 0) { 242 snd_device_free(card, opl4); 243 return err; 244 } 245 246 /* opl3 initialization disabled opl4, so reenable */ 247 snd_opl4_enable_opl4(opl4); 248 249 snd_opl4_create_mixer(opl4); 250 #ifdef CONFIG_PROC_FS 251 snd_opl4_create_proc(opl4); 252 #endif 253 254 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) 255 opl4->seq_client = -1; 256 if (opl4->hardware < OPL3_HW_OPL4_ML) 257 snd_opl4_create_seq_dev(opl4, seq_device); 258 #endif 259 260 if (ropl3) 261 *ropl3 = opl3; 262 if (ropl4) 263 *ropl4 = opl4; 264 return 0; 265 } 266 267 EXPORT_SYMBOL(snd_opl4_create); 268 269 static int __init alsa_opl4_init(void) 270 { 271 return 0; 272 } 273 274 static void __exit alsa_opl4_exit(void) 275 { 276 } 277 278 module_init(alsa_opl4_init) 279 module_exit(alsa_opl4_exit) 280