18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz>, 48c2ecf20Sopenharmony_ci * Hannu Savolainen 1993-1996, 58c2ecf20Sopenharmony_ci * Rob Hooft 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Most if code is ported from OSS/Lite. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <sound/opl3.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/ioport.h> 198c2ecf20Sopenharmony_ci#include <sound/minors.h> 208c2ecf20Sopenharmony_ci#include "opl3_voice.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Hannu Savolainen 1993-1996, Rob Hooft"); 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)"); 248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void snd_opl2_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci unsigned long flags; 298c2ecf20Sopenharmony_ci unsigned long port; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci /* 328c2ecf20Sopenharmony_ci * The original 2-OP synth requires a quite long delay 338c2ecf20Sopenharmony_ci * after writing to a register. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci spin_lock_irqsave(&opl3->reg_lock, flags); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci outb((unsigned char) cmd, port); 418c2ecf20Sopenharmony_ci udelay(10); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci outb((unsigned char) val, port + 1); 448c2ecf20Sopenharmony_ci udelay(30); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opl3->reg_lock, flags); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci unsigned long flags; 528c2ecf20Sopenharmony_ci unsigned long port; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * The OPL-3 survives with just two INBs 568c2ecf20Sopenharmony_ci * after writing to a register. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci spin_lock_irqsave(&opl3->reg_lock, flags); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci outb((unsigned char) cmd, port); 648c2ecf20Sopenharmony_ci inb(opl3->l_port); 658c2ecf20Sopenharmony_ci inb(opl3->l_port); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci outb((unsigned char) val, port + 1); 688c2ecf20Sopenharmony_ci inb(opl3->l_port); 698c2ecf20Sopenharmony_ci inb(opl3->l_port); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opl3->reg_lock, flags); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int snd_opl3_detect(struct snd_opl3 * opl3) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * This function returns 1 if the FM chip is present at the given I/O port 788c2ecf20Sopenharmony_ci * The detection algorithm plays with the timer built in the FM chip and 798c2ecf20Sopenharmony_ci * looks for a change in the status register. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Note! The timers of the FM chip are not connected to AdLib (and compatible) 828c2ecf20Sopenharmony_ci * boards. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Note2! The chip is initialized if detected. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci unsigned char stat1, stat2, signature; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Reset timers 1 and 2 */ 908c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); 918c2ecf20Sopenharmony_ci /* Reset the IRQ of the FM chip */ 928c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); 938c2ecf20Sopenharmony_ci signature = stat1 = inb(opl3->l_port); /* Status register */ 948c2ecf20Sopenharmony_ci if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */ 958c2ecf20Sopenharmony_ci snd_printd("OPL3: stat1 = 0x%x\n", stat1); 968c2ecf20Sopenharmony_ci return -ENODEV; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci /* Set timer1 to 0xff */ 998c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff); 1008c2ecf20Sopenharmony_ci /* Unmask and start timer 1 */ 1018c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START); 1028c2ecf20Sopenharmony_ci /* Now we have to delay at least 80us */ 1038c2ecf20Sopenharmony_ci udelay(200); 1048c2ecf20Sopenharmony_ci /* Read status after timers have expired */ 1058c2ecf20Sopenharmony_ci stat2 = inb(opl3->l_port); 1068c2ecf20Sopenharmony_ci /* Stop the timers */ 1078c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); 1088c2ecf20Sopenharmony_ci /* Reset the IRQ of the FM chip */ 1098c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); 1108c2ecf20Sopenharmony_ci if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */ 1118c2ecf20Sopenharmony_ci snd_printd("OPL3: stat2 = 0x%x\n", stat2); 1128c2ecf20Sopenharmony_ci return -ENODEV; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* If the toplevel code knows exactly the type of chip, don't try 1168c2ecf20Sopenharmony_ci to detect it. */ 1178c2ecf20Sopenharmony_ci if (opl3->hardware != OPL3_HW_AUTO) 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */ 1218c2ecf20Sopenharmony_ci if (signature == 0x06) { /* OPL2 */ 1228c2ecf20Sopenharmony_ci opl3->hardware = OPL3_HW_OPL2; 1238c2ecf20Sopenharmony_ci } else { 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * If we had an OPL4 chip, opl3->hardware would have been set 1268c2ecf20Sopenharmony_ci * by the OPL4 driver; so we can assume OPL3 here. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci if (snd_BUG_ON(!opl3->r_port)) 1298c2ecf20Sopenharmony_ci return -ENODEV; 1308c2ecf20Sopenharmony_ci opl3->hardware = OPL3_HW_OPL3; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * AdLib timers 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * Timer 1 - 80us 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int snd_opl3_timer1_start(struct snd_timer * timer) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci unsigned long flags; 1468c2ecf20Sopenharmony_ci unsigned char tmp; 1478c2ecf20Sopenharmony_ci unsigned int ticks; 1488c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci opl3 = snd_timer_chip(timer); 1518c2ecf20Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 1528c2ecf20Sopenharmony_ci ticks = timer->sticks; 1538c2ecf20Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK; 1548c2ecf20Sopenharmony_ci opl3->timer_enable = tmp; 1558c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */ 1568c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ 1578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int snd_opl3_timer1_stop(struct snd_timer * timer) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci unsigned long flags; 1648c2ecf20Sopenharmony_ci unsigned char tmp; 1658c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci opl3 = snd_timer_chip(timer); 1688c2ecf20Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 1698c2ecf20Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START; 1708c2ecf20Sopenharmony_ci opl3->timer_enable = tmp; 1718c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ 1728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* 1778c2ecf20Sopenharmony_ci * Timer 2 - 320us 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int snd_opl3_timer2_start(struct snd_timer * timer) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci unsigned long flags; 1838c2ecf20Sopenharmony_ci unsigned char tmp; 1848c2ecf20Sopenharmony_ci unsigned int ticks; 1858c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci opl3 = snd_timer_chip(timer); 1888c2ecf20Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 1898c2ecf20Sopenharmony_ci ticks = timer->sticks; 1908c2ecf20Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK; 1918c2ecf20Sopenharmony_ci opl3->timer_enable = tmp; 1928c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */ 1938c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ 1948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int snd_opl3_timer2_stop(struct snd_timer * timer) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci unsigned long flags; 2018c2ecf20Sopenharmony_ci unsigned char tmp; 2028c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci opl3 = snd_timer_chip(timer); 2058c2ecf20Sopenharmony_ci spin_lock_irqsave(&opl3->timer_lock, flags); 2068c2ecf20Sopenharmony_ci tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START; 2078c2ecf20Sopenharmony_ci opl3->timer_enable = tmp; 2088c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ 2098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opl3->timer_lock, flags); 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic const struct snd_timer_hardware snd_opl3_timer1 = 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci .flags = SNDRV_TIMER_HW_STOP, 2208c2ecf20Sopenharmony_ci .resolution = 80000, 2218c2ecf20Sopenharmony_ci .ticks = 256, 2228c2ecf20Sopenharmony_ci .start = snd_opl3_timer1_start, 2238c2ecf20Sopenharmony_ci .stop = snd_opl3_timer1_stop, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct snd_timer_hardware snd_opl3_timer2 = 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci .flags = SNDRV_TIMER_HW_STOP, 2298c2ecf20Sopenharmony_ci .resolution = 320000, 2308c2ecf20Sopenharmony_ci .ticks = 256, 2318c2ecf20Sopenharmony_ci .start = snd_opl3_timer2_start, 2328c2ecf20Sopenharmony_ci .stop = snd_opl3_timer2_stop, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct snd_timer *timer = NULL; 2388c2ecf20Sopenharmony_ci struct snd_timer_id tid; 2398c2ecf20Sopenharmony_ci int err; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_CARD; 2428c2ecf20Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 2438c2ecf20Sopenharmony_ci tid.card = opl3->card->number; 2448c2ecf20Sopenharmony_ci tid.device = timer_no; 2458c2ecf20Sopenharmony_ci tid.subdevice = 0; 2468c2ecf20Sopenharmony_ci if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) { 2478c2ecf20Sopenharmony_ci strcpy(timer->name, "AdLib timer #1"); 2488c2ecf20Sopenharmony_ci timer->private_data = opl3; 2498c2ecf20Sopenharmony_ci timer->hw = snd_opl3_timer1; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci opl3->timer1 = timer; 2528c2ecf20Sopenharmony_ci return err; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct snd_timer *timer = NULL; 2588c2ecf20Sopenharmony_ci struct snd_timer_id tid; 2598c2ecf20Sopenharmony_ci int err; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_CARD; 2628c2ecf20Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 2638c2ecf20Sopenharmony_ci tid.card = opl3->card->number; 2648c2ecf20Sopenharmony_ci tid.device = timer_no; 2658c2ecf20Sopenharmony_ci tid.subdevice = 0; 2668c2ecf20Sopenharmony_ci if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) { 2678c2ecf20Sopenharmony_ci strcpy(timer->name, "AdLib timer #2"); 2688c2ecf20Sopenharmony_ci timer->private_data = opl3; 2698c2ecf20Sopenharmony_ci timer->hw = snd_opl3_timer2; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci opl3->timer2 = timer; 2728c2ecf20Sopenharmony_ci return err; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_civoid snd_opl3_interrupt(struct snd_hwdep * hw) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci unsigned char status; 2828c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 2838c2ecf20Sopenharmony_ci struct snd_timer *timer; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (hw == NULL) 2868c2ecf20Sopenharmony_ci return; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci opl3 = hw->private_data; 2898c2ecf20Sopenharmony_ci status = inb(opl3->l_port); 2908c2ecf20Sopenharmony_ci#if 0 2918c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "AdLib IRQ status = 0x%x\n", status); 2928c2ecf20Sopenharmony_ci#endif 2938c2ecf20Sopenharmony_ci if (!(status & 0x80)) 2948c2ecf20Sopenharmony_ci return; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (status & 0x40) { 2978c2ecf20Sopenharmony_ci timer = opl3->timer1; 2988c2ecf20Sopenharmony_ci snd_timer_interrupt(timer, timer->sticks); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci if (status & 0x20) { 3018c2ecf20Sopenharmony_ci timer = opl3->timer2; 3028c2ecf20Sopenharmony_ci snd_timer_interrupt(timer, timer->sticks); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_interrupt); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/* 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int snd_opl3_free(struct snd_opl3 *opl3) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci if (snd_BUG_ON(!opl3)) 3158c2ecf20Sopenharmony_ci return -ENXIO; 3168c2ecf20Sopenharmony_ci if (opl3->private_free) 3178c2ecf20Sopenharmony_ci opl3->private_free(opl3); 3188c2ecf20Sopenharmony_ci snd_opl3_clear_patches(opl3); 3198c2ecf20Sopenharmony_ci release_and_free_resource(opl3->res_l_port); 3208c2ecf20Sopenharmony_ci release_and_free_resource(opl3->res_r_port); 3218c2ecf20Sopenharmony_ci kfree(opl3); 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int snd_opl3_dev_free(struct snd_device *device) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct snd_opl3 *opl3 = device->device_data; 3288c2ecf20Sopenharmony_ci return snd_opl3_free(opl3); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ciint snd_opl3_new(struct snd_card *card, 3328c2ecf20Sopenharmony_ci unsigned short hardware, 3338c2ecf20Sopenharmony_ci struct snd_opl3 **ropl3) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 3368c2ecf20Sopenharmony_ci .dev_free = snd_opl3_dev_free, 3378c2ecf20Sopenharmony_ci }; 3388c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 3398c2ecf20Sopenharmony_ci int err; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci *ropl3 = NULL; 3428c2ecf20Sopenharmony_ci opl3 = kzalloc(sizeof(*opl3), GFP_KERNEL); 3438c2ecf20Sopenharmony_ci if (!opl3) 3448c2ecf20Sopenharmony_ci return -ENOMEM; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci opl3->card = card; 3478c2ecf20Sopenharmony_ci opl3->hardware = hardware; 3488c2ecf20Sopenharmony_ci spin_lock_init(&opl3->reg_lock); 3498c2ecf20Sopenharmony_ci spin_lock_init(&opl3->timer_lock); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) { 3528c2ecf20Sopenharmony_ci snd_opl3_free(opl3); 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci *ropl3 = opl3; 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_new); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ciint snd_opl3_init(struct snd_opl3 *opl3) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci if (! opl3->command) { 3658c2ecf20Sopenharmony_ci printk(KERN_ERR "snd_opl3_init: command not defined!\n"); 3668c2ecf20Sopenharmony_ci return -EINVAL; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); 3708c2ecf20Sopenharmony_ci /* Melodic mode */ 3718c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci switch (opl3->hardware & OPL3_HW_MASK) { 3748c2ecf20Sopenharmony_ci case OPL3_HW_OPL2: 3758c2ecf20Sopenharmony_ci opl3->max_voices = MAX_OPL2_VOICES; 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case OPL3_HW_OPL3: 3788c2ecf20Sopenharmony_ci case OPL3_HW_OPL4: 3798c2ecf20Sopenharmony_ci opl3->max_voices = MAX_OPL3_VOICES; 3808c2ecf20Sopenharmony_ci /* Enter OPL3 mode */ 3818c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_init); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ciint snd_opl3_create(struct snd_card *card, 3898c2ecf20Sopenharmony_ci unsigned long l_port, 3908c2ecf20Sopenharmony_ci unsigned long r_port, 3918c2ecf20Sopenharmony_ci unsigned short hardware, 3928c2ecf20Sopenharmony_ci int integrated, 3938c2ecf20Sopenharmony_ci struct snd_opl3 ** ropl3) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 3968c2ecf20Sopenharmony_ci int err; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci *ropl3 = NULL; 3998c2ecf20Sopenharmony_ci if ((err = snd_opl3_new(card, hardware, &opl3)) < 0) 4008c2ecf20Sopenharmony_ci return err; 4018c2ecf20Sopenharmony_ci if (! integrated) { 4028c2ecf20Sopenharmony_ci if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) { 4038c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port); 4048c2ecf20Sopenharmony_ci snd_device_free(card, opl3); 4058c2ecf20Sopenharmony_ci return -EBUSY; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci if (r_port != 0 && 4088c2ecf20Sopenharmony_ci (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) { 4098c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port); 4108c2ecf20Sopenharmony_ci snd_device_free(card, opl3); 4118c2ecf20Sopenharmony_ci return -EBUSY; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci opl3->l_port = l_port; 4158c2ecf20Sopenharmony_ci opl3->r_port = r_port; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci switch (opl3->hardware) { 4188c2ecf20Sopenharmony_ci /* some hardware doesn't support timers */ 4198c2ecf20Sopenharmony_ci case OPL3_HW_OPL3_SV: 4208c2ecf20Sopenharmony_ci case OPL3_HW_OPL3_CS: 4218c2ecf20Sopenharmony_ci case OPL3_HW_OPL3_FM801: 4228c2ecf20Sopenharmony_ci opl3->command = &snd_opl3_command; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci default: 4258c2ecf20Sopenharmony_ci opl3->command = &snd_opl2_command; 4268c2ecf20Sopenharmony_ci if ((err = snd_opl3_detect(opl3)) < 0) { 4278c2ecf20Sopenharmony_ci snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n", 4288c2ecf20Sopenharmony_ci opl3->l_port, opl3->r_port); 4298c2ecf20Sopenharmony_ci snd_device_free(card, opl3); 4308c2ecf20Sopenharmony_ci return err; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci /* detect routine returns correct hardware type */ 4338c2ecf20Sopenharmony_ci switch (opl3->hardware & OPL3_HW_MASK) { 4348c2ecf20Sopenharmony_ci case OPL3_HW_OPL3: 4358c2ecf20Sopenharmony_ci case OPL3_HW_OPL4: 4368c2ecf20Sopenharmony_ci opl3->command = &snd_opl3_command; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci snd_opl3_init(opl3); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci *ropl3 = opl3; 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_create); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ciint snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int err; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (timer1_dev >= 0) 4538c2ecf20Sopenharmony_ci if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0) 4548c2ecf20Sopenharmony_ci return err; 4558c2ecf20Sopenharmony_ci if (timer2_dev >= 0) { 4568c2ecf20Sopenharmony_ci if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) { 4578c2ecf20Sopenharmony_ci snd_device_free(opl3->card, opl3->timer1); 4588c2ecf20Sopenharmony_ci opl3->timer1 = NULL; 4598c2ecf20Sopenharmony_ci return err; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_timer_new); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ciint snd_opl3_hwdep_new(struct snd_opl3 * opl3, 4688c2ecf20Sopenharmony_ci int device, int seq_device, 4698c2ecf20Sopenharmony_ci struct snd_hwdep ** rhwdep) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct snd_hwdep *hw; 4728c2ecf20Sopenharmony_ci struct snd_card *card = opl3->card; 4738c2ecf20Sopenharmony_ci int err; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (rhwdep) 4768c2ecf20Sopenharmony_ci *rhwdep = NULL; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* create hardware dependent device (direct FM) */ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) { 4818c2ecf20Sopenharmony_ci snd_device_free(card, opl3); 4828c2ecf20Sopenharmony_ci return err; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci hw->private_data = opl3; 4858c2ecf20Sopenharmony_ci hw->exclusive = 1; 4868c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 4878c2ecf20Sopenharmony_ci if (device == 0) 4888c2ecf20Sopenharmony_ci hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci strcpy(hw->name, hw->id); 4918c2ecf20Sopenharmony_ci switch (opl3->hardware & OPL3_HW_MASK) { 4928c2ecf20Sopenharmony_ci case OPL3_HW_OPL2: 4938c2ecf20Sopenharmony_ci strcpy(hw->name, "OPL2 FM"); 4948c2ecf20Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_OPL2; 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci case OPL3_HW_OPL3: 4978c2ecf20Sopenharmony_ci strcpy(hw->name, "OPL3 FM"); 4988c2ecf20Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_OPL3; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci case OPL3_HW_OPL4: 5018c2ecf20Sopenharmony_ci strcpy(hw->name, "OPL4 FM"); 5028c2ecf20Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_OPL4; 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* operators - only ioctl */ 5078c2ecf20Sopenharmony_ci hw->ops.open = snd_opl3_open; 5088c2ecf20Sopenharmony_ci hw->ops.ioctl = snd_opl3_ioctl; 5098c2ecf20Sopenharmony_ci hw->ops.write = snd_opl3_write; 5108c2ecf20Sopenharmony_ci hw->ops.release = snd_opl3_release; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci opl3->hwdep = hw; 5138c2ecf20Sopenharmony_ci opl3->seq_dev_num = seq_device; 5148c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER) 5158c2ecf20Sopenharmony_ci if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, 5168c2ecf20Sopenharmony_ci sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) { 5178c2ecf20Sopenharmony_ci strcpy(opl3->seq_dev->name, hw->name); 5188c2ecf20Sopenharmony_ci *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci#endif 5218c2ecf20Sopenharmony_ci if (rhwdep) 5228c2ecf20Sopenharmony_ci *rhwdep = hw; 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_hwdep_new); 527