162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz>, 462306a36Sopenharmony_ci * Hannu Savolainen 1993-1996, 562306a36Sopenharmony_ci * Rob Hooft 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Most if code is ported from OSS/Lite. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <sound/opl3.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/ioport.h> 1962306a36Sopenharmony_ci#include <sound/minors.h> 2062306a36Sopenharmony_ci#include "opl3_voice.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Hannu Savolainen 1993-1996, Rob Hooft"); 2362306a36Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)"); 2462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void snd_opl2_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci unsigned long flags; 2962306a36Sopenharmony_ci unsigned long port; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* 3262306a36Sopenharmony_ci * The original 2-OP synth requires a quite long delay 3362306a36Sopenharmony_ci * after writing to a register. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci spin_lock_irqsave(&opl3->reg_lock, flags); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci outb((unsigned char) cmd, port); 4162306a36Sopenharmony_ci udelay(10); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci outb((unsigned char) val, port + 1); 4462306a36Sopenharmony_ci udelay(30); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci spin_unlock_irqrestore(&opl3->reg_lock, flags); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci unsigned long flags; 5262306a36Sopenharmony_ci unsigned long port; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* 5562306a36Sopenharmony_ci * The OPL-3 survives with just two INBs 5662306a36Sopenharmony_ci * after writing to a register. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci spin_lock_irqsave(&opl3->reg_lock, flags); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci outb((unsigned char) cmd, port); 6462306a36Sopenharmony_ci inb(opl3->l_port); 6562306a36Sopenharmony_ci inb(opl3->l_port); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci outb((unsigned char) val, port + 1); 6862306a36Sopenharmony_ci inb(opl3->l_port); 6962306a36Sopenharmony_ci inb(opl3->l_port); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci spin_unlock_irqrestore(&opl3->reg_lock, flags); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int snd_opl3_detect(struct snd_opl3 * opl3) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * This function returns 1 if the FM chip is present at the given I/O port 7862306a36Sopenharmony_ci * The detection algorithm plays with the timer built in the FM chip and 7962306a36Sopenharmony_ci * looks for a change in the status register. 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * Note! The timers of the FM chip are not connected to AdLib (and compatible) 8262306a36Sopenharmony_ci * boards. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Note2! The chip is initialized if detected. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci unsigned char stat1, stat2, signature; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Reset timers 1 and 2 */ 9062306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); 9162306a36Sopenharmony_ci /* Reset the IRQ of the FM chip */ 9262306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); 9362306a36Sopenharmony_ci signature = stat1 = inb(opl3->l_port); /* Status register */ 9462306a36Sopenharmony_ci if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */ 9562306a36Sopenharmony_ci snd_printd("OPL3: stat1 = 0x%x\n", stat1); 9662306a36Sopenharmony_ci return -ENODEV; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci /* Set timer1 to 0xff */ 9962306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff); 10062306a36Sopenharmony_ci /* Unmask and start timer 1 */ 10162306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START); 10262306a36Sopenharmony_ci /* Now we have to delay at least 80us */ 10362306a36Sopenharmony_ci udelay(200); 10462306a36Sopenharmony_ci /* Read status after timers have expired */ 10562306a36Sopenharmony_ci stat2 = inb(opl3->l_port); 10662306a36Sopenharmony_ci /* Stop the timers */ 10762306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); 10862306a36Sopenharmony_ci /* Reset the IRQ of the FM chip */ 10962306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); 11062306a36Sopenharmony_ci if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */ 11162306a36Sopenharmony_ci snd_printd("OPL3: stat2 = 0x%x\n", stat2); 11262306a36Sopenharmony_ci return -ENODEV; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* If the toplevel code knows exactly the type of chip, don't try 11662306a36Sopenharmony_ci to detect it. */ 11762306a36Sopenharmony_ci if (opl3->hardware != OPL3_HW_AUTO) 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */ 12162306a36Sopenharmony_ci if (signature == 0x06) { /* OPL2 */ 12262306a36Sopenharmony_ci opl3->hardware = OPL3_HW_OPL2; 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * If we had an OPL4 chip, opl3->hardware would have been set 12662306a36Sopenharmony_ci * by the OPL4 driver; so we can assume OPL3 here. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci if (snd_BUG_ON(!opl3->r_port)) 12962306a36Sopenharmony_ci return -ENODEV; 13062306a36Sopenharmony_ci opl3->hardware = OPL3_HW_OPL3; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * AdLib timers 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci * Timer 1 - 80us 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int snd_opl3_timer1_start(struct snd_timer * timer) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci unsigned long flags; 14662306a36Sopenharmony_ci unsigned char tmp; 14762306a36Sopenharmony_ci unsigned int ticks; 14862306a36Sopenharmony_ci struct snd_opl3 *opl3; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci opl3 = snd_timer_chip(timer); 15162306a36Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 15262306a36Sopenharmony_ci ticks = timer->sticks; 15362306a36Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK; 15462306a36Sopenharmony_ci opl3->timer_enable = tmp; 15562306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */ 15662306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ 15762306a36Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int snd_opl3_timer1_stop(struct snd_timer * timer) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci unsigned char tmp; 16562306a36Sopenharmony_ci struct snd_opl3 *opl3; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci opl3 = snd_timer_chip(timer); 16862306a36Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 16962306a36Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START; 17062306a36Sopenharmony_ci opl3->timer_enable = tmp; 17162306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ 17262306a36Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * Timer 2 - 320us 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int snd_opl3_timer2_start(struct snd_timer * timer) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci unsigned long flags; 18362306a36Sopenharmony_ci unsigned char tmp; 18462306a36Sopenharmony_ci unsigned int ticks; 18562306a36Sopenharmony_ci struct snd_opl3 *opl3; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci opl3 = snd_timer_chip(timer); 18862306a36Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 18962306a36Sopenharmony_ci ticks = timer->sticks; 19062306a36Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK; 19162306a36Sopenharmony_ci opl3->timer_enable = tmp; 19262306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */ 19362306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ 19462306a36Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int snd_opl3_timer2_stop(struct snd_timer * timer) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci unsigned long flags; 20162306a36Sopenharmony_ci unsigned char tmp; 20262306a36Sopenharmony_ci struct snd_opl3 *opl3; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci opl3 = snd_timer_chip(timer); 20562306a36Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 20662306a36Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START; 20762306a36Sopenharmony_ci opl3->timer_enable = tmp; 20862306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ 20962306a36Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const struct snd_timer_hardware snd_opl3_timer1 = 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci .flags = SNDRV_TIMER_HW_STOP, 22062306a36Sopenharmony_ci .resolution = 80000, 22162306a36Sopenharmony_ci .ticks = 256, 22262306a36Sopenharmony_ci .start = snd_opl3_timer1_start, 22362306a36Sopenharmony_ci .stop = snd_opl3_timer1_stop, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const struct snd_timer_hardware snd_opl3_timer2 = 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci .flags = SNDRV_TIMER_HW_STOP, 22962306a36Sopenharmony_ci .resolution = 320000, 23062306a36Sopenharmony_ci .ticks = 256, 23162306a36Sopenharmony_ci .start = snd_opl3_timer2_start, 23262306a36Sopenharmony_ci .stop = snd_opl3_timer2_stop, 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct snd_timer *timer = NULL; 23862306a36Sopenharmony_ci struct snd_timer_id tid; 23962306a36Sopenharmony_ci int err; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_CARD; 24262306a36Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 24362306a36Sopenharmony_ci tid.card = opl3->card->number; 24462306a36Sopenharmony_ci tid.device = timer_no; 24562306a36Sopenharmony_ci tid.subdevice = 0; 24662306a36Sopenharmony_ci err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer); 24762306a36Sopenharmony_ci if (err >= 0) { 24862306a36Sopenharmony_ci strcpy(timer->name, "AdLib timer #1"); 24962306a36Sopenharmony_ci timer->private_data = opl3; 25062306a36Sopenharmony_ci timer->hw = snd_opl3_timer1; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci opl3->timer1 = timer; 25362306a36Sopenharmony_ci return err; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct snd_timer *timer = NULL; 25962306a36Sopenharmony_ci struct snd_timer_id tid; 26062306a36Sopenharmony_ci int err; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_CARD; 26362306a36Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 26462306a36Sopenharmony_ci tid.card = opl3->card->number; 26562306a36Sopenharmony_ci tid.device = timer_no; 26662306a36Sopenharmony_ci tid.subdevice = 0; 26762306a36Sopenharmony_ci err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer); 26862306a36Sopenharmony_ci if (err >= 0) { 26962306a36Sopenharmony_ci strcpy(timer->name, "AdLib timer #2"); 27062306a36Sopenharmony_ci timer->private_data = opl3; 27162306a36Sopenharmony_ci timer->hw = snd_opl3_timer2; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci opl3->timer2 = timer; 27462306a36Sopenharmony_ci return err; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_civoid snd_opl3_interrupt(struct snd_hwdep * hw) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci unsigned char status; 28462306a36Sopenharmony_ci struct snd_opl3 *opl3; 28562306a36Sopenharmony_ci struct snd_timer *timer; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (hw == NULL) 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci opl3 = hw->private_data; 29162306a36Sopenharmony_ci status = inb(opl3->l_port); 29262306a36Sopenharmony_ci#if 0 29362306a36Sopenharmony_ci snd_printk(KERN_DEBUG "AdLib IRQ status = 0x%x\n", status); 29462306a36Sopenharmony_ci#endif 29562306a36Sopenharmony_ci if (!(status & 0x80)) 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (status & 0x40) { 29962306a36Sopenharmony_ci timer = opl3->timer1; 30062306a36Sopenharmony_ci snd_timer_interrupt(timer, timer->sticks); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci if (status & 0x20) { 30362306a36Sopenharmony_ci timer = opl3->timer2; 30462306a36Sopenharmony_ci snd_timer_interrupt(timer, timer->sticks); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_interrupt); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int snd_opl3_free(struct snd_opl3 *opl3) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if (snd_BUG_ON(!opl3)) 31762306a36Sopenharmony_ci return -ENXIO; 31862306a36Sopenharmony_ci if (opl3->private_free) 31962306a36Sopenharmony_ci opl3->private_free(opl3); 32062306a36Sopenharmony_ci snd_opl3_clear_patches(opl3); 32162306a36Sopenharmony_ci release_and_free_resource(opl3->res_l_port); 32262306a36Sopenharmony_ci release_and_free_resource(opl3->res_r_port); 32362306a36Sopenharmony_ci kfree(opl3); 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int snd_opl3_dev_free(struct snd_device *device) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct snd_opl3 *opl3 = device->device_data; 33062306a36Sopenharmony_ci return snd_opl3_free(opl3); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciint snd_opl3_new(struct snd_card *card, 33462306a36Sopenharmony_ci unsigned short hardware, 33562306a36Sopenharmony_ci struct snd_opl3 **ropl3) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci static const struct snd_device_ops ops = { 33862306a36Sopenharmony_ci .dev_free = snd_opl3_dev_free, 33962306a36Sopenharmony_ci }; 34062306a36Sopenharmony_ci struct snd_opl3 *opl3; 34162306a36Sopenharmony_ci int err; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci *ropl3 = NULL; 34462306a36Sopenharmony_ci opl3 = kzalloc(sizeof(*opl3), GFP_KERNEL); 34562306a36Sopenharmony_ci if (!opl3) 34662306a36Sopenharmony_ci return -ENOMEM; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci opl3->card = card; 34962306a36Sopenharmony_ci opl3->hardware = hardware; 35062306a36Sopenharmony_ci spin_lock_init(&opl3->reg_lock); 35162306a36Sopenharmony_ci spin_lock_init(&opl3->timer_lock); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops); 35462306a36Sopenharmony_ci if (err < 0) { 35562306a36Sopenharmony_ci snd_opl3_free(opl3); 35662306a36Sopenharmony_ci return err; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci *ropl3 = opl3; 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_new); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ciint snd_opl3_init(struct snd_opl3 *opl3) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci if (! opl3->command) { 36862306a36Sopenharmony_ci printk(KERN_ERR "snd_opl3_init: command not defined!\n"); 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); 37362306a36Sopenharmony_ci /* Melodic mode */ 37462306a36Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci switch (opl3->hardware & OPL3_HW_MASK) { 37762306a36Sopenharmony_ci case OPL3_HW_OPL2: 37862306a36Sopenharmony_ci opl3->max_voices = MAX_OPL2_VOICES; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci case OPL3_HW_OPL3: 38162306a36Sopenharmony_ci case OPL3_HW_OPL4: 38262306a36Sopenharmony_ci opl3->max_voices = MAX_OPL3_VOICES; 38362306a36Sopenharmony_ci /* Enter OPL3 mode */ 38462306a36Sopenharmony_ci opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_init); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ciint snd_opl3_create(struct snd_card *card, 39262306a36Sopenharmony_ci unsigned long l_port, 39362306a36Sopenharmony_ci unsigned long r_port, 39462306a36Sopenharmony_ci unsigned short hardware, 39562306a36Sopenharmony_ci int integrated, 39662306a36Sopenharmony_ci struct snd_opl3 ** ropl3) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct snd_opl3 *opl3; 39962306a36Sopenharmony_ci int err; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci *ropl3 = NULL; 40262306a36Sopenharmony_ci err = snd_opl3_new(card, hardware, &opl3); 40362306a36Sopenharmony_ci if (err < 0) 40462306a36Sopenharmony_ci return err; 40562306a36Sopenharmony_ci if (! integrated) { 40662306a36Sopenharmony_ci opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)"); 40762306a36Sopenharmony_ci if (!opl3->res_l_port) { 40862306a36Sopenharmony_ci snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port); 40962306a36Sopenharmony_ci snd_device_free(card, opl3); 41062306a36Sopenharmony_ci return -EBUSY; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci if (r_port != 0) { 41362306a36Sopenharmony_ci opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)"); 41462306a36Sopenharmony_ci if (!opl3->res_r_port) { 41562306a36Sopenharmony_ci snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port); 41662306a36Sopenharmony_ci snd_device_free(card, opl3); 41762306a36Sopenharmony_ci return -EBUSY; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci opl3->l_port = l_port; 42262306a36Sopenharmony_ci opl3->r_port = r_port; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci switch (opl3->hardware) { 42562306a36Sopenharmony_ci /* some hardware doesn't support timers */ 42662306a36Sopenharmony_ci case OPL3_HW_OPL3_SV: 42762306a36Sopenharmony_ci case OPL3_HW_OPL3_CS: 42862306a36Sopenharmony_ci case OPL3_HW_OPL3_FM801: 42962306a36Sopenharmony_ci opl3->command = &snd_opl3_command; 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci default: 43262306a36Sopenharmony_ci opl3->command = &snd_opl2_command; 43362306a36Sopenharmony_ci err = snd_opl3_detect(opl3); 43462306a36Sopenharmony_ci if (err < 0) { 43562306a36Sopenharmony_ci snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n", 43662306a36Sopenharmony_ci opl3->l_port, opl3->r_port); 43762306a36Sopenharmony_ci snd_device_free(card, opl3); 43862306a36Sopenharmony_ci return err; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci /* detect routine returns correct hardware type */ 44162306a36Sopenharmony_ci switch (opl3->hardware & OPL3_HW_MASK) { 44262306a36Sopenharmony_ci case OPL3_HW_OPL3: 44362306a36Sopenharmony_ci case OPL3_HW_OPL4: 44462306a36Sopenharmony_ci opl3->command = &snd_opl3_command; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci snd_opl3_init(opl3); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci *ropl3 = opl3; 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_create); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ciint snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci int err; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (timer1_dev >= 0) { 46162306a36Sopenharmony_ci err = snd_opl3_timer1_init(opl3, timer1_dev); 46262306a36Sopenharmony_ci if (err < 0) 46362306a36Sopenharmony_ci return err; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci if (timer2_dev >= 0) { 46662306a36Sopenharmony_ci err = snd_opl3_timer2_init(opl3, timer2_dev); 46762306a36Sopenharmony_ci if (err < 0) { 46862306a36Sopenharmony_ci snd_device_free(opl3->card, opl3->timer1); 46962306a36Sopenharmony_ci opl3->timer1 = NULL; 47062306a36Sopenharmony_ci return err; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_timer_new); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciint snd_opl3_hwdep_new(struct snd_opl3 * opl3, 47962306a36Sopenharmony_ci int device, int seq_device, 48062306a36Sopenharmony_ci struct snd_hwdep ** rhwdep) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct snd_hwdep *hw; 48362306a36Sopenharmony_ci struct snd_card *card = opl3->card; 48462306a36Sopenharmony_ci int err; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (rhwdep) 48762306a36Sopenharmony_ci *rhwdep = NULL; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* create hardware dependent device (direct FM) */ 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw); 49262306a36Sopenharmony_ci if (err < 0) { 49362306a36Sopenharmony_ci snd_device_free(card, opl3); 49462306a36Sopenharmony_ci return err; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci hw->private_data = opl3; 49762306a36Sopenharmony_ci hw->exclusive = 1; 49862306a36Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 49962306a36Sopenharmony_ci if (device == 0) 50062306a36Sopenharmony_ci hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; 50162306a36Sopenharmony_ci#endif 50262306a36Sopenharmony_ci strcpy(hw->name, hw->id); 50362306a36Sopenharmony_ci switch (opl3->hardware & OPL3_HW_MASK) { 50462306a36Sopenharmony_ci case OPL3_HW_OPL2: 50562306a36Sopenharmony_ci strcpy(hw->name, "OPL2 FM"); 50662306a36Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_OPL2; 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci case OPL3_HW_OPL3: 50962306a36Sopenharmony_ci strcpy(hw->name, "OPL3 FM"); 51062306a36Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_OPL3; 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci case OPL3_HW_OPL4: 51362306a36Sopenharmony_ci strcpy(hw->name, "OPL4 FM"); 51462306a36Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_OPL4; 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* operators - only ioctl */ 51962306a36Sopenharmony_ci hw->ops.open = snd_opl3_open; 52062306a36Sopenharmony_ci hw->ops.ioctl = snd_opl3_ioctl; 52162306a36Sopenharmony_ci hw->ops.write = snd_opl3_write; 52262306a36Sopenharmony_ci hw->ops.release = snd_opl3_release; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci opl3->hwdep = hw; 52562306a36Sopenharmony_ci opl3->seq_dev_num = seq_device; 52662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER) 52762306a36Sopenharmony_ci if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, 52862306a36Sopenharmony_ci sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) { 52962306a36Sopenharmony_ci strcpy(opl3->seq_dev->name, hw->name); 53062306a36Sopenharmony_ci *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci#endif 53362306a36Sopenharmony_ci if (rhwdep) 53462306a36Sopenharmony_ci *rhwdep = hw; 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_hwdep_new); 539