18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Low-level ALSA driver for the ENSONIQ SoundScape 48c2ecf20Sopenharmony_ci * Copyright (c) by Chris Rankin 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This driver was written in part using information obtained from 78c2ecf20Sopenharmony_ci * the OSS/Free SoundScape driver, written by Hannu Savolainen. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/isa.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/firmware.h> 168c2ecf20Sopenharmony_ci#include <linux/pnp.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <asm/dma.h> 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/wss.h> 228c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 238c2ecf20Sopenharmony_ci#include <sound/initval.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chris Rankin"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ENSONIQ SoundScape driver"); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_ciMODULE_FIRMWARE("sndscape.co0"); 308c2ecf20Sopenharmony_ciMODULE_FIRMWARE("sndscape.co1"); 318c2ecf20Sopenharmony_ciMODULE_FIRMWARE("sndscape.co2"); 328c2ecf20Sopenharmony_ciMODULE_FIRMWARE("sndscape.co3"); 338c2ecf20Sopenharmony_ciMODULE_FIRMWARE("sndscape.co4"); 348c2ecf20Sopenharmony_ciMODULE_FIRMWARE("scope.cod"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 378c2ecf20Sopenharmony_cistatic char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 388c2ecf20Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 398c2ecf20Sopenharmony_cistatic long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 408c2ecf20Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; 418c2ecf20Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; 428c2ecf20Sopenharmony_cistatic int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 438c2ecf20Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 448c2ecf20Sopenharmony_cistatic bool joystick[SNDRV_CARDS]; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index number for SoundScape soundcard"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "Description for SoundScape card"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for SoundScape driver."); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cimodule_param_hw_array(wss_port, long, ioport, NULL, 0444); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(wss_port, "WSS Port # for SoundScape driver."); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for SoundScape driver."); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_irq, int, irq, NULL, 0444); 628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver."); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cimodule_param_hw_array(dma, int, dma, NULL, 0444); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma, "DMA # for SoundScape driver."); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444); 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver."); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cimodule_param_array(joystick, bool, NULL, 0444); 718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick, "Enable gameport."); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 748c2ecf20Sopenharmony_cistatic int isa_registered; 758c2ecf20Sopenharmony_cistatic int pnp_registered; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct pnp_card_device_id sscape_pnpids[] = { 788c2ecf20Sopenharmony_ci { .id = "ENS3081", .devs = { { "ENS0000" } } }, /* Soundscape PnP */ 798c2ecf20Sopenharmony_ci { .id = "ENS4081", .devs = { { "ENS1011" } } }, /* VIVO90 */ 808c2ecf20Sopenharmony_ci { .id = "" } /* end */ 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, sscape_pnpids); 848c2ecf20Sopenharmony_ci#endif 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define HOST_CTRL_IO(i) ((i) + 2) 888c2ecf20Sopenharmony_ci#define HOST_DATA_IO(i) ((i) + 3) 898c2ecf20Sopenharmony_ci#define ODIE_ADDR_IO(i) ((i) + 4) 908c2ecf20Sopenharmony_ci#define ODIE_DATA_IO(i) ((i) + 5) 918c2ecf20Sopenharmony_ci#define CODEC_IO(i) ((i) + 8) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define IC_ODIE 1 948c2ecf20Sopenharmony_ci#define IC_OPUS 2 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define RX_READY 0x01 978c2ecf20Sopenharmony_ci#define TX_READY 0x02 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define CMD_ACK 0x80 1008c2ecf20Sopenharmony_ci#define CMD_SET_MIDI_VOL 0x84 1018c2ecf20Sopenharmony_ci#define CMD_GET_MIDI_VOL 0x85 1028c2ecf20Sopenharmony_ci#define CMD_XXX_MIDI_VOL 0x86 1038c2ecf20Sopenharmony_ci#define CMD_SET_EXTMIDI 0x8a 1048c2ecf20Sopenharmony_ci#define CMD_GET_EXTMIDI 0x8b 1058c2ecf20Sopenharmony_ci#define CMD_SET_MT32 0x8c 1068c2ecf20Sopenharmony_ci#define CMD_GET_MT32 0x8d 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cienum GA_REG { 1098c2ecf20Sopenharmony_ci GA_INTSTAT_REG = 0, 1108c2ecf20Sopenharmony_ci GA_INTENA_REG, 1118c2ecf20Sopenharmony_ci GA_DMAA_REG, 1128c2ecf20Sopenharmony_ci GA_DMAB_REG, 1138c2ecf20Sopenharmony_ci GA_INTCFG_REG, 1148c2ecf20Sopenharmony_ci GA_DMACFG_REG, 1158c2ecf20Sopenharmony_ci GA_CDCFG_REG, 1168c2ecf20Sopenharmony_ci GA_SMCFGA_REG, 1178c2ecf20Sopenharmony_ci GA_SMCFGB_REG, 1188c2ecf20Sopenharmony_ci GA_HMCTL_REG 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define DMA_8BIT 0x80 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cienum card_type { 1258c2ecf20Sopenharmony_ci MEDIA_FX, /* Sequoia S-1000 */ 1268c2ecf20Sopenharmony_ci SSCAPE, /* Sequoia S-2000 */ 1278c2ecf20Sopenharmony_ci SSCAPE_PNP, 1288c2ecf20Sopenharmony_ci SSCAPE_VIVO, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistruct soundscape { 1328c2ecf20Sopenharmony_ci spinlock_t lock; 1338c2ecf20Sopenharmony_ci unsigned io_base; 1348c2ecf20Sopenharmony_ci int ic_type; 1358c2ecf20Sopenharmony_ci enum card_type type; 1368c2ecf20Sopenharmony_ci struct resource *io_res; 1378c2ecf20Sopenharmony_ci struct resource *wss_res; 1388c2ecf20Sopenharmony_ci struct snd_wss *chip; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci unsigned char midi_vol; 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define INVALID_IRQ ((unsigned)-1) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic inline struct soundscape *get_card_soundscape(struct snd_card *c) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return (struct soundscape *) (c->private_data); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Allocates some kernel memory that we can use for DMA. 1538c2ecf20Sopenharmony_ci * I think this means that the memory has to map to 1548c2ecf20Sopenharmony_ci * contiguous pages of physical memory. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic struct snd_dma_buffer *get_dmabuf(struct soundscape *s, 1578c2ecf20Sopenharmony_ci struct snd_dma_buffer *buf, 1588c2ecf20Sopenharmony_ci unsigned long size) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci if (buf) { 1618c2ecf20Sopenharmony_ci if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, 1628c2ecf20Sopenharmony_ci s->chip->card->dev, 1638c2ecf20Sopenharmony_ci size, buf) < 0) { 1648c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Failed to allocate " 1658c2ecf20Sopenharmony_ci "%lu bytes for DMA\n", 1668c2ecf20Sopenharmony_ci size); 1678c2ecf20Sopenharmony_ci return NULL; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return buf; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * Release the DMA-able kernel memory ... 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistatic void free_dmabuf(struct snd_dma_buffer *buf) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (buf && buf->area) 1808c2ecf20Sopenharmony_ci snd_dma_free_pages(buf); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* 1848c2ecf20Sopenharmony_ci * This function writes to the SoundScape's control registers, 1858c2ecf20Sopenharmony_ci * but doesn't do any locking. It's up to the caller to do that. 1868c2ecf20Sopenharmony_ci * This is why this function is "unsafe" ... 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, 1898c2ecf20Sopenharmony_ci unsigned char val) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci outb(reg, ODIE_ADDR_IO(io_base)); 1928c2ecf20Sopenharmony_ci outb(val, ODIE_DATA_IO(io_base)); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * Write to the SoundScape's control registers, and do the 1978c2ecf20Sopenharmony_ci * necessary locking ... 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void sscape_write(struct soundscape *s, enum GA_REG reg, 2008c2ecf20Sopenharmony_ci unsigned char val) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci unsigned long flags; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 2058c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, reg, val); 2068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * Read from the SoundScape's control registers, but leave any 2118c2ecf20Sopenharmony_ci * locking to the caller. This is why the function is "unsafe" ... 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_cistatic inline unsigned char sscape_read_unsafe(unsigned io_base, 2148c2ecf20Sopenharmony_ci enum GA_REG reg) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci outb(reg, ODIE_ADDR_IO(io_base)); 2178c2ecf20Sopenharmony_ci return inb(ODIE_DATA_IO(io_base)); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* 2218c2ecf20Sopenharmony_ci * Puts the SoundScape into "host" mode, as compared to "MIDI" mode 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_cistatic inline void set_host_mode_unsafe(unsigned io_base) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci outb(0x0, HOST_CTRL_IO(io_base)); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * Puts the SoundScape into "MIDI" mode, as compared to "host" mode 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_cistatic inline void set_midi_mode_unsafe(unsigned io_base) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci outb(0x3, HOST_CTRL_IO(io_base)); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * Read the SoundScape's host-mode control register, but leave 2388c2ecf20Sopenharmony_ci * any locking issues to the caller ... 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic inline int host_read_unsafe(unsigned io_base) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci int data = -1; 2438c2ecf20Sopenharmony_ci if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) 2448c2ecf20Sopenharmony_ci data = inb(HOST_DATA_IO(io_base)); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return data; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * Read the SoundScape's host-mode control register, performing 2518c2ecf20Sopenharmony_ci * a limited amount of busy-waiting if the register isn't ready. 2528c2ecf20Sopenharmony_ci * Also leaves all locking-issues to the caller ... 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_cistatic int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci int data; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci while (((data = host_read_unsafe(io_base)) < 0) && (timeout != 0)) { 2598c2ecf20Sopenharmony_ci udelay(100); 2608c2ecf20Sopenharmony_ci --timeout; 2618c2ecf20Sopenharmony_ci } /* while */ 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return data; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Write to the SoundScape's host-mode control registers, but 2688c2ecf20Sopenharmony_ci * leave any locking issues to the caller ... 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_cistatic inline int host_write_unsafe(unsigned io_base, unsigned char data) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) { 2738c2ecf20Sopenharmony_ci outb(data, HOST_DATA_IO(io_base)); 2748c2ecf20Sopenharmony_ci return 1; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* 2818c2ecf20Sopenharmony_ci * Write to the SoundScape's host-mode control registers, performing 2828c2ecf20Sopenharmony_ci * a limited amount of busy-waiting if the register isn't ready. 2838c2ecf20Sopenharmony_ci * Also leaves all locking-issues to the caller ... 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistatic int host_write_ctrl_unsafe(unsigned io_base, unsigned char data, 2868c2ecf20Sopenharmony_ci unsigned timeout) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int err; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) { 2918c2ecf20Sopenharmony_ci udelay(100); 2928c2ecf20Sopenharmony_ci --timeout; 2938c2ecf20Sopenharmony_ci } /* while */ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return err; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* 3008c2ecf20Sopenharmony_ci * Check that the MIDI subsystem is operational. If it isn't, 3018c2ecf20Sopenharmony_ci * then we will hang the computer if we try to use it ... 3028c2ecf20Sopenharmony_ci * 3038c2ecf20Sopenharmony_ci * NOTE: This check is based upon observation, not documentation. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_cistatic inline int verify_mpu401(const struct snd_mpu401 *mpu) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci return ((inb(MPU401C(mpu)) & 0xc0) == 0x80); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* 3118c2ecf20Sopenharmony_ci * This is apparently the standard way to initialise an MPU-401 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_cistatic inline void initialise_mpu401(const struct snd_mpu401 *mpu) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci outb(0, MPU401D(mpu)); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* 3198c2ecf20Sopenharmony_ci * Tell the SoundScape to activate the AD1845 chip (I think). 3208c2ecf20Sopenharmony_ci * The AD1845 detection fails if we *don't* do this, so I 3218c2ecf20Sopenharmony_ci * think that this is a good idea ... 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistatic void activate_ad1845_unsafe(unsigned io_base) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci unsigned char val = sscape_read_unsafe(io_base, GA_HMCTL_REG); 3268c2ecf20Sopenharmony_ci sscape_write_unsafe(io_base, GA_HMCTL_REG, (val & 0xcf) | 0x10); 3278c2ecf20Sopenharmony_ci sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * Do the necessary ALSA-level cleanup to deallocate our driver ... 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_cistatic void soundscape_free(struct snd_card *c) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct soundscape *sscape = get_card_soundscape(c); 3368c2ecf20Sopenharmony_ci release_and_free_resource(sscape->io_res); 3378c2ecf20Sopenharmony_ci release_and_free_resource(sscape->wss_res); 3388c2ecf20Sopenharmony_ci free_dma(sscape->chip->dma1); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* 3428c2ecf20Sopenharmony_ci * Tell the SoundScape to begin a DMA transfer using the given channel. 3438c2ecf20Sopenharmony_ci * All locking issues are left to the caller. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_cistatic void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci sscape_write_unsafe(io_base, reg, 3488c2ecf20Sopenharmony_ci sscape_read_unsafe(io_base, reg) | 0x01); 3498c2ecf20Sopenharmony_ci sscape_write_unsafe(io_base, reg, 3508c2ecf20Sopenharmony_ci sscape_read_unsafe(io_base, reg) & 0xfe); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* 3548c2ecf20Sopenharmony_ci * Wait for a DMA transfer to complete. This is a "limited busy-wait", 3558c2ecf20Sopenharmony_ci * and all locking issues are left to the caller. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_cistatic int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, 3588c2ecf20Sopenharmony_ci unsigned timeout) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) { 3618c2ecf20Sopenharmony_ci udelay(100); 3628c2ecf20Sopenharmony_ci --timeout; 3638c2ecf20Sopenharmony_ci } /* while */ 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return sscape_read_unsafe(io_base, reg) & 0x01; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* 3698c2ecf20Sopenharmony_ci * Wait for the On-Board Processor to return its start-up 3708c2ecf20Sopenharmony_ci * acknowledgement sequence. This wait is too long for 3718c2ecf20Sopenharmony_ci * us to perform "busy-waiting", and so we must sleep. 3728c2ecf20Sopenharmony_ci * This in turn means that we must not be holding any 3738c2ecf20Sopenharmony_ci * spinlocks when we call this function. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_cistatic int obp_startup_ack(struct soundscape *s, unsigned timeout) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci unsigned long end_time = jiffies + msecs_to_jiffies(timeout); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci do { 3808c2ecf20Sopenharmony_ci unsigned long flags; 3818c2ecf20Sopenharmony_ci int x; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 3848c2ecf20Sopenharmony_ci x = host_read_unsafe(s->io_base); 3858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 3868c2ecf20Sopenharmony_ci if (x == 0xfe || x == 0xff) 3878c2ecf20Sopenharmony_ci return 1; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci msleep(10); 3908c2ecf20Sopenharmony_ci } while (time_before(jiffies, end_time)); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* 3968c2ecf20Sopenharmony_ci * Wait for the host to return its start-up acknowledgement 3978c2ecf20Sopenharmony_ci * sequence. This wait is too long for us to perform 3988c2ecf20Sopenharmony_ci * "busy-waiting", and so we must sleep. This in turn means 3998c2ecf20Sopenharmony_ci * that we must not be holding any spinlocks when we call 4008c2ecf20Sopenharmony_ci * this function. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic int host_startup_ack(struct soundscape *s, unsigned timeout) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci unsigned long end_time = jiffies + msecs_to_jiffies(timeout); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci do { 4078c2ecf20Sopenharmony_ci unsigned long flags; 4088c2ecf20Sopenharmony_ci int x; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 4118c2ecf20Sopenharmony_ci x = host_read_unsafe(s->io_base); 4128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 4138c2ecf20Sopenharmony_ci if (x == 0xfe) 4148c2ecf20Sopenharmony_ci return 1; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci msleep(10); 4178c2ecf20Sopenharmony_ci } while (time_before(jiffies, end_time)); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* 4238c2ecf20Sopenharmony_ci * Upload a byte-stream into the SoundScape using DMA channel A. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_cistatic int upload_dma_data(struct soundscape *s, const unsigned char *data, 4268c2ecf20Sopenharmony_ci size_t size) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci unsigned long flags; 4298c2ecf20Sopenharmony_ci struct snd_dma_buffer dma; 4308c2ecf20Sopenharmony_ci int ret; 4318c2ecf20Sopenharmony_ci unsigned char val; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (!get_dmabuf(s, &dma, PAGE_ALIGN(32 * 1024))) 4348c2ecf20Sopenharmony_ci return -ENOMEM; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * Reset the board ... 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG); 4428c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Enable the DMA channels and configure them ... 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci val = (s->chip->dma1 << 4) | DMA_8BIT; 4488c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_DMAA_REG, val); 4498c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * Take the board out of reset ... 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG); 4558c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * Upload the firmware to the SoundScape 4598c2ecf20Sopenharmony_ci * board through the DMA channel ... 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci while (size != 0) { 4628c2ecf20Sopenharmony_ci unsigned long len; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci len = min(size, dma.bytes); 4658c2ecf20Sopenharmony_ci memcpy(dma.area, data, len); 4668c2ecf20Sopenharmony_ci data += len; 4678c2ecf20Sopenharmony_ci size -= len; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE); 4708c2ecf20Sopenharmony_ci sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG); 4718c2ecf20Sopenharmony_ci if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) { 4728c2ecf20Sopenharmony_ci /* 4738c2ecf20Sopenharmony_ci * Don't forget to release this spinlock we're holding 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci snd_printk(KERN_ERR 4788c2ecf20Sopenharmony_ci "sscape: DMA upload has timed out\n"); 4798c2ecf20Sopenharmony_ci ret = -EAGAIN; 4808c2ecf20Sopenharmony_ci goto _release_dma; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci } /* while */ 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci set_host_mode_unsafe(s->io_base); 4858c2ecf20Sopenharmony_ci outb(0x0, s->io_base); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * Boot the board ... (I think) 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG); 4918c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40); 4928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* 4958c2ecf20Sopenharmony_ci * If all has gone well, then the board should acknowledge 4968c2ecf20Sopenharmony_ci * the new upload and tell us that it has rebooted OK. We 4978c2ecf20Sopenharmony_ci * give it 5 seconds (max) ... 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ci ret = 0; 5008c2ecf20Sopenharmony_ci if (!obp_startup_ack(s, 5000)) { 5018c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: No response " 5028c2ecf20Sopenharmony_ci "from on-board processor after upload\n"); 5038c2ecf20Sopenharmony_ci ret = -EAGAIN; 5048c2ecf20Sopenharmony_ci } else if (!host_startup_ack(s, 5000)) { 5058c2ecf20Sopenharmony_ci snd_printk(KERN_ERR 5068c2ecf20Sopenharmony_ci "sscape: SoundScape failed to initialise\n"); 5078c2ecf20Sopenharmony_ci ret = -EAGAIN; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci_release_dma: 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * NOTE!!! We are NOT holding any spinlocks at this point !!! 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_OPUS ? 0x40 : 0x70)); 5158c2ecf20Sopenharmony_ci free_dmabuf(&dma); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return ret; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci/* 5218c2ecf20Sopenharmony_ci * Upload the bootblock(?) into the SoundScape. The only 5228c2ecf20Sopenharmony_ci * purpose of this block of code seems to be to tell 5238c2ecf20Sopenharmony_ci * us which version of the microcode we should be using. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_cistatic int sscape_upload_bootblock(struct snd_card *card) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct soundscape *sscape = get_card_soundscape(card); 5288c2ecf20Sopenharmony_ci unsigned long flags; 5298c2ecf20Sopenharmony_ci const struct firmware *init_fw = NULL; 5308c2ecf20Sopenharmony_ci int data = 0; 5318c2ecf20Sopenharmony_ci int ret; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = request_firmware(&init_fw, "scope.cod", card->dev); 5348c2ecf20Sopenharmony_ci if (ret < 0) { 5358c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Error loading scope.cod"); 5368c2ecf20Sopenharmony_ci return ret; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci ret = upload_dma_data(sscape, init_fw->data, init_fw->size); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci release_firmware(init_fw); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci spin_lock_irqsave(&sscape->lock, flags); 5438c2ecf20Sopenharmony_ci if (ret == 0) 5448c2ecf20Sopenharmony_ci data = host_read_ctrl_unsafe(sscape->io_base, 100); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (data & 0x10) 5478c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sscape->lock, flags); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci data &= 0xf; 5528c2ecf20Sopenharmony_ci if (ret == 0 && data > 7) { 5538c2ecf20Sopenharmony_ci snd_printk(KERN_ERR 5548c2ecf20Sopenharmony_ci "sscape: timeout reading firmware version\n"); 5558c2ecf20Sopenharmony_ci ret = -EAGAIN; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return (ret == 0) ? data : ret; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/* 5628c2ecf20Sopenharmony_ci * Upload the microcode into the SoundScape. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_cistatic int sscape_upload_microcode(struct snd_card *card, int version) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct soundscape *sscape = get_card_soundscape(card); 5678c2ecf20Sopenharmony_ci const struct firmware *init_fw = NULL; 5688c2ecf20Sopenharmony_ci char name[14]; 5698c2ecf20Sopenharmony_ci int err; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "sndscape.co%d", version); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci err = request_firmware(&init_fw, name, card->dev); 5748c2ecf20Sopenharmony_ci if (err < 0) { 5758c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d", 5768c2ecf20Sopenharmony_ci version); 5778c2ecf20Sopenharmony_ci return err; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci err = upload_dma_data(sscape, init_fw->data, init_fw->size); 5808c2ecf20Sopenharmony_ci if (err == 0) 5818c2ecf20Sopenharmony_ci snd_printk(KERN_INFO "sscape: MIDI firmware loaded %zu KBs\n", 5828c2ecf20Sopenharmony_ci init_fw->size >> 10); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci release_firmware(init_fw); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return err; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/* 5908c2ecf20Sopenharmony_ci * Mixer control for the SoundScape's MIDI device. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_cistatic int sscape_midi_info(struct snd_kcontrol *ctl, 5938c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 5968c2ecf20Sopenharmony_ci uinfo->count = 1; 5978c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 5988c2ecf20Sopenharmony_ci uinfo->value.integer.max = 127; 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int sscape_midi_get(struct snd_kcontrol *kctl, 6038c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uctl) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kctl); 6068c2ecf20Sopenharmony_ci struct snd_card *card = chip->card; 6078c2ecf20Sopenharmony_ci register struct soundscape *s = get_card_soundscape(card); 6088c2ecf20Sopenharmony_ci unsigned long flags; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 6118c2ecf20Sopenharmony_ci uctl->value.integer.value[0] = s->midi_vol; 6128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int sscape_midi_put(struct snd_kcontrol *kctl, 6178c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uctl) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kctl); 6208c2ecf20Sopenharmony_ci struct snd_card *card = chip->card; 6218c2ecf20Sopenharmony_ci struct soundscape *s = get_card_soundscape(card); 6228c2ecf20Sopenharmony_ci unsigned long flags; 6238c2ecf20Sopenharmony_ci int change; 6248c2ecf20Sopenharmony_ci unsigned char new_val; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci new_val = uctl->value.integer.value[0] & 127; 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * We need to put the board into HOST mode before we 6318c2ecf20Sopenharmony_ci * can send any volume-changing HOST commands ... 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ci set_host_mode_unsafe(s->io_base); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* 6368c2ecf20Sopenharmony_ci * To successfully change the MIDI volume setting, you seem to 6378c2ecf20Sopenharmony_ci * have to write a volume command, write the new volume value, 6388c2ecf20Sopenharmony_ci * and then perform another volume-related command. Perhaps the 6398c2ecf20Sopenharmony_ci * first command is an "open" and the second command is a "close"? 6408c2ecf20Sopenharmony_ci */ 6418c2ecf20Sopenharmony_ci if (s->midi_vol == new_val) { 6428c2ecf20Sopenharmony_ci change = 0; 6438c2ecf20Sopenharmony_ci goto __skip_change; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci change = host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100) 6468c2ecf20Sopenharmony_ci && host_write_ctrl_unsafe(s->io_base, new_val, 100) 6478c2ecf20Sopenharmony_ci && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100) 6488c2ecf20Sopenharmony_ci && host_write_ctrl_unsafe(s->io_base, new_val, 100); 6498c2ecf20Sopenharmony_ci s->midi_vol = new_val; 6508c2ecf20Sopenharmony_ci__skip_change: 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* 6538c2ecf20Sopenharmony_ci * Take the board out of HOST mode and back into MIDI mode ... 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_ci set_midi_mode_unsafe(s->io_base); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 6588c2ecf20Sopenharmony_ci return change; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new midi_mixer_ctl = { 6628c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 6638c2ecf20Sopenharmony_ci .name = "MIDI", 6648c2ecf20Sopenharmony_ci .info = sscape_midi_info, 6658c2ecf20Sopenharmony_ci .get = sscape_midi_get, 6668c2ecf20Sopenharmony_ci .put = sscape_midi_put 6678c2ecf20Sopenharmony_ci}; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/* 6708c2ecf20Sopenharmony_ci * The SoundScape can use two IRQs from a possible set of four. 6718c2ecf20Sopenharmony_ci * These IRQs are encoded as bit patterns so that they can be 6728c2ecf20Sopenharmony_ci * written to the control registers. 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_cistatic unsigned get_irq_config(int sscape_type, int irq) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci static const int valid_irq[] = { 9, 5, 7, 10 }; 6778c2ecf20Sopenharmony_ci static const int old_irq[] = { 9, 7, 5, 15 }; 6788c2ecf20Sopenharmony_ci unsigned cfg; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (sscape_type == MEDIA_FX) { 6818c2ecf20Sopenharmony_ci for (cfg = 0; cfg < ARRAY_SIZE(old_irq); ++cfg) 6828c2ecf20Sopenharmony_ci if (irq == old_irq[cfg]) 6838c2ecf20Sopenharmony_ci return cfg; 6848c2ecf20Sopenharmony_ci } else { 6858c2ecf20Sopenharmony_ci for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg) 6868c2ecf20Sopenharmony_ci if (irq == valid_irq[cfg]) 6878c2ecf20Sopenharmony_ci return cfg; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return INVALID_IRQ; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/* 6948c2ecf20Sopenharmony_ci * Perform certain arcane port-checks to see whether there 6958c2ecf20Sopenharmony_ci * is a SoundScape board lurking behind the given ports. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_cistatic int detect_sscape(struct soundscape *s, long wss_io) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci unsigned long flags; 7008c2ecf20Sopenharmony_ci unsigned d; 7018c2ecf20Sopenharmony_ci int retval = 0; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * The following code is lifted from the original OSS driver, 7078c2ecf20Sopenharmony_ci * and as I don't have a datasheet I cannot really comment 7088c2ecf20Sopenharmony_ci * on what it is doing... 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci if ((inb(HOST_CTRL_IO(s->io_base)) & 0x78) != 0) 7118c2ecf20Sopenharmony_ci goto _done; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci d = inb(ODIE_ADDR_IO(s->io_base)) & 0xf0; 7148c2ecf20Sopenharmony_ci if ((d & 0x80) != 0) 7158c2ecf20Sopenharmony_ci goto _done; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (d == 0) 7188c2ecf20Sopenharmony_ci s->ic_type = IC_ODIE; 7198c2ecf20Sopenharmony_ci else if ((d & 0x60) != 0) 7208c2ecf20Sopenharmony_ci s->ic_type = IC_OPUS; 7218c2ecf20Sopenharmony_ci else 7228c2ecf20Sopenharmony_ci goto _done; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci outb(0xfa, ODIE_ADDR_IO(s->io_base)); 7258c2ecf20Sopenharmony_ci if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0a) 7268c2ecf20Sopenharmony_ci goto _done; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci outb(0xfe, ODIE_ADDR_IO(s->io_base)); 7298c2ecf20Sopenharmony_ci if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0e) 7308c2ecf20Sopenharmony_ci goto _done; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci outb(0xfe, ODIE_ADDR_IO(s->io_base)); 7338c2ecf20Sopenharmony_ci d = inb(ODIE_DATA_IO(s->io_base)); 7348c2ecf20Sopenharmony_ci if (s->type != SSCAPE_VIVO && (d & 0x9f) != 0x0e) 7358c2ecf20Sopenharmony_ci goto _done; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (s->ic_type == IC_OPUS) 7388c2ecf20Sopenharmony_ci activate_ad1845_unsafe(s->io_base); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (s->type == SSCAPE_VIVO) 7418c2ecf20Sopenharmony_ci wss_io += 4; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG); 7448c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* wait for WSS codec */ 7478c2ecf20Sopenharmony_ci for (d = 0; d < 500; d++) { 7488c2ecf20Sopenharmony_ci if ((inb(wss_io) & 0x80) == 0) 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 7518c2ecf20Sopenharmony_ci msleep(1); 7528c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if ((inb(wss_io) & 0x80) != 0) 7568c2ecf20Sopenharmony_ci goto _done; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (inb(wss_io + 2) == 0xff) 7598c2ecf20Sopenharmony_ci goto _done; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f; 7628c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if ((inb(wss_io) & 0x80) != 0) 7658c2ecf20Sopenharmony_ci s->type = MEDIA_FX; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG); 7688c2ecf20Sopenharmony_ci sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0); 7698c2ecf20Sopenharmony_ci /* wait for WSS codec */ 7708c2ecf20Sopenharmony_ci for (d = 0; d < 500; d++) { 7718c2ecf20Sopenharmony_ci if ((inb(wss_io) & 0x80) == 0) 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 7748c2ecf20Sopenharmony_ci msleep(1); 7758c2ecf20Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* 7798c2ecf20Sopenharmony_ci * SoundScape successfully detected! 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_ci retval = 1; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci_done: 7848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 7858c2ecf20Sopenharmony_ci return retval; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci/* 7898c2ecf20Sopenharmony_ci * ALSA callback function, called when attempting to open the MIDI device. 7908c2ecf20Sopenharmony_ci * Check that the MIDI firmware has been loaded, because we don't want 7918c2ecf20Sopenharmony_ci * to crash the machine. Also check that someone isn't using the hardware 7928c2ecf20Sopenharmony_ci * IOCTL device. 7938c2ecf20Sopenharmony_ci */ 7948c2ecf20Sopenharmony_cistatic int mpu401_open(struct snd_mpu401 *mpu) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci if (!verify_mpu401(mpu)) { 7978c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: MIDI disabled, " 7988c2ecf20Sopenharmony_ci "please load firmware\n"); 7998c2ecf20Sopenharmony_ci return -ENODEV; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci/* 8068c2ecf20Sopenharmony_ci * Initialise an MPU-401 subdevice for MIDI support on the SoundScape. 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_cistatic int create_mpu401(struct snd_card *card, int devnum, 8098c2ecf20Sopenharmony_ci unsigned long port, int irq) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct soundscape *sscape = get_card_soundscape(card); 8128c2ecf20Sopenharmony_ci struct snd_rawmidi *rawmidi; 8138c2ecf20Sopenharmony_ci int err; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port, 8168c2ecf20Sopenharmony_ci MPU401_INFO_INTEGRATED, irq, &rawmidi); 8178c2ecf20Sopenharmony_ci if (err == 0) { 8188c2ecf20Sopenharmony_ci struct snd_mpu401 *mpu = rawmidi->private_data; 8198c2ecf20Sopenharmony_ci mpu->open_input = mpu401_open; 8208c2ecf20Sopenharmony_ci mpu->open_output = mpu401_open; 8218c2ecf20Sopenharmony_ci mpu->private_data = sscape; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci initialise_mpu401(mpu); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return err; 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/* 8318c2ecf20Sopenharmony_ci * Create an AD1845 PCM subdevice on the SoundScape. The AD1845 8328c2ecf20Sopenharmony_ci * is very much like a CS4231, with a few extra bits. We will 8338c2ecf20Sopenharmony_ci * try to support at least some of the extra bits by overriding 8348c2ecf20Sopenharmony_ci * some of the CS4231 callback. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_cistatic int create_ad1845(struct snd_card *card, unsigned port, 8378c2ecf20Sopenharmony_ci int irq, int dma1, int dma2) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci register struct soundscape *sscape = get_card_soundscape(card); 8408c2ecf20Sopenharmony_ci struct snd_wss *chip; 8418c2ecf20Sopenharmony_ci int err; 8428c2ecf20Sopenharmony_ci int codec_type = WSS_HW_DETECT; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci switch (sscape->type) { 8458c2ecf20Sopenharmony_ci case MEDIA_FX: 8468c2ecf20Sopenharmony_ci case SSCAPE: 8478c2ecf20Sopenharmony_ci /* 8488c2ecf20Sopenharmony_ci * There are some freak examples of early Soundscape cards 8498c2ecf20Sopenharmony_ci * with CS4231 instead of AD1848/CS4248. Unfortunately, the 8508c2ecf20Sopenharmony_ci * CS4231 works only in CS4248 compatibility mode on 8518c2ecf20Sopenharmony_ci * these cards so force it. 8528c2ecf20Sopenharmony_ci */ 8538c2ecf20Sopenharmony_ci if (sscape->ic_type != IC_OPUS) 8548c2ecf20Sopenharmony_ci codec_type = WSS_HW_AD1848; 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci case SSCAPE_VIVO: 8588c2ecf20Sopenharmony_ci port += 4; 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci default: 8618c2ecf20Sopenharmony_ci break; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci err = snd_wss_create(card, port, -1, irq, dma1, dma2, 8658c2ecf20Sopenharmony_ci codec_type, WSS_HWSHARE_DMA1, &chip); 8668c2ecf20Sopenharmony_ci if (!err) { 8678c2ecf20Sopenharmony_ci unsigned long flags; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (sscape->type != SSCAPE_VIVO) { 8708c2ecf20Sopenharmony_ci /* 8718c2ecf20Sopenharmony_ci * The input clock frequency on the SoundScape must 8728c2ecf20Sopenharmony_ci * be 14.31818 MHz, because we must set this register 8738c2ecf20Sopenharmony_ci * to get the playback to sound correct ... 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_ci snd_wss_mce_up(chip); 8768c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 8778c2ecf20Sopenharmony_ci snd_wss_out(chip, AD1845_CLOCK, 0x20); 8788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 8798c2ecf20Sopenharmony_ci snd_wss_mce_down(chip); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci err = snd_wss_pcm(chip, 0); 8848c2ecf20Sopenharmony_ci if (err < 0) { 8858c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: No PCM device " 8868c2ecf20Sopenharmony_ci "for AD1845 chip\n"); 8878c2ecf20Sopenharmony_ci goto _error; 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci err = snd_wss_mixer(chip); 8918c2ecf20Sopenharmony_ci if (err < 0) { 8928c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: No mixer device " 8938c2ecf20Sopenharmony_ci "for AD1845 chip\n"); 8948c2ecf20Sopenharmony_ci goto _error; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci if (chip->hardware != WSS_HW_AD1848) { 8978c2ecf20Sopenharmony_ci err = snd_wss_timer(chip, 0); 8988c2ecf20Sopenharmony_ci if (err < 0) { 8998c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: No timer device " 9008c2ecf20Sopenharmony_ci "for AD1845 chip\n"); 9018c2ecf20Sopenharmony_ci goto _error; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci if (sscape->type != SSCAPE_VIVO) { 9068c2ecf20Sopenharmony_ci err = snd_ctl_add(card, 9078c2ecf20Sopenharmony_ci snd_ctl_new1(&midi_mixer_ctl, chip)); 9088c2ecf20Sopenharmony_ci if (err < 0) { 9098c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Could not create " 9108c2ecf20Sopenharmony_ci "MIDI mixer control\n"); 9118c2ecf20Sopenharmony_ci goto _error; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci sscape->chip = chip; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci_error: 9198c2ecf20Sopenharmony_ci return err; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci/* 9248c2ecf20Sopenharmony_ci * Create an ALSA soundcard entry for the SoundScape, using 9258c2ecf20Sopenharmony_ci * the given list of port, IRQ and DMA resources. 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_cistatic int create_sscape(int dev, struct snd_card *card) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct soundscape *sscape = get_card_soundscape(card); 9308c2ecf20Sopenharmony_ci unsigned dma_cfg; 9318c2ecf20Sopenharmony_ci unsigned irq_cfg; 9328c2ecf20Sopenharmony_ci unsigned mpu_irq_cfg; 9338c2ecf20Sopenharmony_ci struct resource *io_res; 9348c2ecf20Sopenharmony_ci struct resource *wss_res; 9358c2ecf20Sopenharmony_ci unsigned long flags; 9368c2ecf20Sopenharmony_ci int err; 9378c2ecf20Sopenharmony_ci int val; 9388c2ecf20Sopenharmony_ci const char *name; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* 9418c2ecf20Sopenharmony_ci * Grab IO ports that we will need to probe so that we 9428c2ecf20Sopenharmony_ci * can detect and control this hardware ... 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci io_res = request_region(port[dev], 8, "SoundScape"); 9458c2ecf20Sopenharmony_ci if (!io_res) { 9468c2ecf20Sopenharmony_ci snd_printk(KERN_ERR 9478c2ecf20Sopenharmony_ci "sscape: can't grab port 0x%lx\n", port[dev]); 9488c2ecf20Sopenharmony_ci return -EBUSY; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci wss_res = NULL; 9518c2ecf20Sopenharmony_ci if (sscape->type == SSCAPE_VIVO) { 9528c2ecf20Sopenharmony_ci wss_res = request_region(wss_port[dev], 4, "SoundScape"); 9538c2ecf20Sopenharmony_ci if (!wss_res) { 9548c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: can't grab port 0x%lx\n", 9558c2ecf20Sopenharmony_ci wss_port[dev]); 9568c2ecf20Sopenharmony_ci err = -EBUSY; 9578c2ecf20Sopenharmony_ci goto _release_region; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* 9628c2ecf20Sopenharmony_ci * Grab one DMA channel ... 9638c2ecf20Sopenharmony_ci */ 9648c2ecf20Sopenharmony_ci err = request_dma(dma[dev], "SoundScape"); 9658c2ecf20Sopenharmony_ci if (err < 0) { 9668c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: can't grab DMA %d\n", dma[dev]); 9678c2ecf20Sopenharmony_ci goto _release_region; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci spin_lock_init(&sscape->lock); 9718c2ecf20Sopenharmony_ci sscape->io_res = io_res; 9728c2ecf20Sopenharmony_ci sscape->wss_res = wss_res; 9738c2ecf20Sopenharmony_ci sscape->io_base = port[dev]; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (!detect_sscape(sscape, wss_port[dev])) { 9768c2ecf20Sopenharmony_ci printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", 9778c2ecf20Sopenharmony_ci sscape->io_base); 9788c2ecf20Sopenharmony_ci err = -ENODEV; 9798c2ecf20Sopenharmony_ci goto _release_dma; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci switch (sscape->type) { 9838c2ecf20Sopenharmony_ci case MEDIA_FX: 9848c2ecf20Sopenharmony_ci name = "MediaFX/SoundFX"; 9858c2ecf20Sopenharmony_ci break; 9868c2ecf20Sopenharmony_ci case SSCAPE: 9878c2ecf20Sopenharmony_ci name = "Soundscape"; 9888c2ecf20Sopenharmony_ci break; 9898c2ecf20Sopenharmony_ci case SSCAPE_PNP: 9908c2ecf20Sopenharmony_ci name = "Soundscape PnP"; 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci case SSCAPE_VIVO: 9938c2ecf20Sopenharmony_ci name = "Soundscape VIVO"; 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci default: 9968c2ecf20Sopenharmony_ci name = "unknown Soundscape"; 9978c2ecf20Sopenharmony_ci break; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n", 10018c2ecf20Sopenharmony_ci name, sscape->io_base, irq[dev], dma[dev]); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* 10048c2ecf20Sopenharmony_ci * Check that the user didn't pass us garbage data ... 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_ci irq_cfg = get_irq_config(sscape->type, irq[dev]); 10078c2ecf20Sopenharmony_ci if (irq_cfg == INVALID_IRQ) { 10088c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]); 10098c2ecf20Sopenharmony_ci err = -ENXIO; 10108c2ecf20Sopenharmony_ci goto _release_dma; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]); 10148c2ecf20Sopenharmony_ci if (mpu_irq_cfg == INVALID_IRQ) { 10158c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]); 10168c2ecf20Sopenharmony_ci err = -ENXIO; 10178c2ecf20Sopenharmony_ci goto _release_dma; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* 10218c2ecf20Sopenharmony_ci * Tell the on-board devices where their resources are (I think - 10228c2ecf20Sopenharmony_ci * I can't be sure without a datasheet ... So many magic values!) 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ci spin_lock_irqsave(&sscape->lock, flags); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e); 10278c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* 10308c2ecf20Sopenharmony_ci * Enable and configure the DMA channels ... 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50); 10338c2ecf20Sopenharmony_ci dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70); 10348c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg); 10358c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci mpu_irq_cfg |= mpu_irq_cfg << 2; 10388c2ecf20Sopenharmony_ci val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7; 10398c2ecf20Sopenharmony_ci if (joystick[dev]) 10408c2ecf20Sopenharmony_ci val |= 8; 10418c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10); 10428c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg); 10438c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, 10448c2ecf20Sopenharmony_ci GA_CDCFG_REG, 0x09 | DMA_8BIT 10458c2ecf20Sopenharmony_ci | (dma[dev] << 4) | (irq_cfg << 1)); 10468c2ecf20Sopenharmony_ci /* 10478c2ecf20Sopenharmony_ci * Enable the master IRQ ... 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sscape->lock, flags); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* 10548c2ecf20Sopenharmony_ci * We have now enabled the codec chip, and so we should 10558c2ecf20Sopenharmony_ci * detect the AD1845 device ... 10568c2ecf20Sopenharmony_ci */ 10578c2ecf20Sopenharmony_ci err = create_ad1845(card, wss_port[dev], irq[dev], 10588c2ecf20Sopenharmony_ci dma[dev], dma2[dev]); 10598c2ecf20Sopenharmony_ci if (err < 0) { 10608c2ecf20Sopenharmony_ci snd_printk(KERN_ERR 10618c2ecf20Sopenharmony_ci "sscape: No AD1845 device at 0x%lx, IRQ %d\n", 10628c2ecf20Sopenharmony_ci wss_port[dev], irq[dev]); 10638c2ecf20Sopenharmony_ci goto _release_dma; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci strcpy(card->driver, "SoundScape"); 10668c2ecf20Sopenharmony_ci strcpy(card->shortname, name); 10678c2ecf20Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 10688c2ecf20Sopenharmony_ci "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n", 10698c2ecf20Sopenharmony_ci name, sscape->chip->port, sscape->chip->irq, 10708c2ecf20Sopenharmony_ci sscape->chip->dma1, sscape->chip->dma2); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci#define MIDI_DEVNUM 0 10738c2ecf20Sopenharmony_ci if (sscape->type != SSCAPE_VIVO) { 10748c2ecf20Sopenharmony_ci err = sscape_upload_bootblock(card); 10758c2ecf20Sopenharmony_ci if (err >= 0) 10768c2ecf20Sopenharmony_ci err = sscape_upload_microcode(card, err); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (err == 0) { 10798c2ecf20Sopenharmony_ci err = create_mpu401(card, MIDI_DEVNUM, port[dev], 10808c2ecf20Sopenharmony_ci mpu_irq[dev]); 10818c2ecf20Sopenharmony_ci if (err < 0) { 10828c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Failed to create " 10838c2ecf20Sopenharmony_ci "MPU-401 device at 0x%lx\n", 10848c2ecf20Sopenharmony_ci port[dev]); 10858c2ecf20Sopenharmony_ci goto _release_dma; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* 10898c2ecf20Sopenharmony_ci * Initialize mixer 10908c2ecf20Sopenharmony_ci */ 10918c2ecf20Sopenharmony_ci spin_lock_irqsave(&sscape->lock, flags); 10928c2ecf20Sopenharmony_ci sscape->midi_vol = 0; 10938c2ecf20Sopenharmony_ci host_write_ctrl_unsafe(sscape->io_base, 10948c2ecf20Sopenharmony_ci CMD_SET_MIDI_VOL, 100); 10958c2ecf20Sopenharmony_ci host_write_ctrl_unsafe(sscape->io_base, 10968c2ecf20Sopenharmony_ci sscape->midi_vol, 100); 10978c2ecf20Sopenharmony_ci host_write_ctrl_unsafe(sscape->io_base, 10988c2ecf20Sopenharmony_ci CMD_XXX_MIDI_VOL, 100); 10998c2ecf20Sopenharmony_ci host_write_ctrl_unsafe(sscape->io_base, 11008c2ecf20Sopenharmony_ci sscape->midi_vol, 100); 11018c2ecf20Sopenharmony_ci host_write_ctrl_unsafe(sscape->io_base, 11028c2ecf20Sopenharmony_ci CMD_SET_EXTMIDI, 100); 11038c2ecf20Sopenharmony_ci host_write_ctrl_unsafe(sscape->io_base, 11048c2ecf20Sopenharmony_ci 0, 100); 11058c2ecf20Sopenharmony_ci host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci set_midi_mode_unsafe(sscape->io_base); 11088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sscape->lock, flags); 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* 11138c2ecf20Sopenharmony_ci * Now that we have successfully created this sound card, 11148c2ecf20Sopenharmony_ci * it is safe to store the pointer. 11158c2ecf20Sopenharmony_ci * NOTE: we only register the sound card's "destructor" 11168c2ecf20Sopenharmony_ci * function now that our "constructor" has completed. 11178c2ecf20Sopenharmony_ci */ 11188c2ecf20Sopenharmony_ci card->private_free = soundscape_free; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci return 0; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci_release_dma: 11238c2ecf20Sopenharmony_ci free_dma(dma[dev]); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci_release_region: 11268c2ecf20Sopenharmony_ci release_and_free_resource(wss_res); 11278c2ecf20Sopenharmony_ci release_and_free_resource(io_res); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return err; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic int snd_sscape_match(struct device *pdev, unsigned int i) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * Make sure we were given ALL of the other parameters. 11378c2ecf20Sopenharmony_ci */ 11388c2ecf20Sopenharmony_ci if (port[i] == SNDRV_AUTO_PORT) 11398c2ecf20Sopenharmony_ci return 0; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (irq[i] == SNDRV_AUTO_IRQ || 11428c2ecf20Sopenharmony_ci mpu_irq[i] == SNDRV_AUTO_IRQ || 11438c2ecf20Sopenharmony_ci dma[i] == SNDRV_AUTO_DMA) { 11448c2ecf20Sopenharmony_ci printk(KERN_INFO 11458c2ecf20Sopenharmony_ci "sscape: insufficient parameters, " 11468c2ecf20Sopenharmony_ci "need IO, IRQ, MPU-IRQ and DMA\n"); 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci return 1; 11518c2ecf20Sopenharmony_ci} 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic int snd_sscape_probe(struct device *pdev, unsigned int dev) 11548c2ecf20Sopenharmony_ci{ 11558c2ecf20Sopenharmony_ci struct snd_card *card; 11568c2ecf20Sopenharmony_ci struct soundscape *sscape; 11578c2ecf20Sopenharmony_ci int ret; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci ret = snd_card_new(pdev, index[dev], id[dev], THIS_MODULE, 11608c2ecf20Sopenharmony_ci sizeof(struct soundscape), &card); 11618c2ecf20Sopenharmony_ci if (ret < 0) 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci sscape = get_card_soundscape(card); 11658c2ecf20Sopenharmony_ci sscape->type = SSCAPE; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci dma[dev] &= 0x03; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci ret = create_sscape(dev, card); 11708c2ecf20Sopenharmony_ci if (ret < 0) 11718c2ecf20Sopenharmony_ci goto _release_card; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ret = snd_card_register(card); 11748c2ecf20Sopenharmony_ci if (ret < 0) { 11758c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Failed to register sound card\n"); 11768c2ecf20Sopenharmony_ci goto _release_card; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci dev_set_drvdata(pdev, card); 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci_release_card: 11828c2ecf20Sopenharmony_ci snd_card_free(card); 11838c2ecf20Sopenharmony_ci return ret; 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic int snd_sscape_remove(struct device *devptr, unsigned int dev) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci snd_card_free(dev_get_drvdata(devptr)); 11898c2ecf20Sopenharmony_ci return 0; 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci#define DEV_NAME "sscape" 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic struct isa_driver snd_sscape_driver = { 11958c2ecf20Sopenharmony_ci .match = snd_sscape_match, 11968c2ecf20Sopenharmony_ci .probe = snd_sscape_probe, 11978c2ecf20Sopenharmony_ci .remove = snd_sscape_remove, 11988c2ecf20Sopenharmony_ci /* FIXME: suspend/resume */ 11998c2ecf20Sopenharmony_ci .driver = { 12008c2ecf20Sopenharmony_ci .name = DEV_NAME 12018c2ecf20Sopenharmony_ci }, 12028c2ecf20Sopenharmony_ci}; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 12058c2ecf20Sopenharmony_cistatic inline int get_next_autoindex(int i) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci while (i < SNDRV_CARDS && port[i] != SNDRV_AUTO_PORT) 12088c2ecf20Sopenharmony_ci ++i; 12098c2ecf20Sopenharmony_ci return i; 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic int sscape_pnp_detect(struct pnp_card_link *pcard, 12148c2ecf20Sopenharmony_ci const struct pnp_card_device_id *pid) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci static int idx = 0; 12178c2ecf20Sopenharmony_ci struct pnp_dev *dev; 12188c2ecf20Sopenharmony_ci struct snd_card *card; 12198c2ecf20Sopenharmony_ci struct soundscape *sscape; 12208c2ecf20Sopenharmony_ci int ret; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* 12238c2ecf20Sopenharmony_ci * Allow this function to fail *quietly* if all the ISA PnP 12248c2ecf20Sopenharmony_ci * devices were configured using module parameters instead. 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_ci idx = get_next_autoindex(idx); 12278c2ecf20Sopenharmony_ci if (idx >= SNDRV_CARDS) 12288c2ecf20Sopenharmony_ci return -ENOSPC; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* 12318c2ecf20Sopenharmony_ci * Check that we still have room for another sound card ... 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_ci dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL); 12348c2ecf20Sopenharmony_ci if (!dev) 12358c2ecf20Sopenharmony_ci return -ENODEV; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (!pnp_is_active(dev)) { 12388c2ecf20Sopenharmony_ci if (pnp_activate_dev(dev) < 0) { 12398c2ecf20Sopenharmony_ci snd_printk(KERN_INFO "sscape: device is inactive\n"); 12408c2ecf20Sopenharmony_ci return -EBUSY; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* 12458c2ecf20Sopenharmony_ci * Create a new ALSA sound card entry, in anticipation 12468c2ecf20Sopenharmony_ci * of detecting our hardware ... 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_ci ret = snd_card_new(&pcard->card->dev, 12498c2ecf20Sopenharmony_ci index[idx], id[idx], THIS_MODULE, 12508c2ecf20Sopenharmony_ci sizeof(struct soundscape), &card); 12518c2ecf20Sopenharmony_ci if (ret < 0) 12528c2ecf20Sopenharmony_ci return ret; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci sscape = get_card_soundscape(card); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* 12578c2ecf20Sopenharmony_ci * Identify card model ... 12588c2ecf20Sopenharmony_ci */ 12598c2ecf20Sopenharmony_ci if (!strncmp("ENS4081", pid->id, 7)) 12608c2ecf20Sopenharmony_ci sscape->type = SSCAPE_VIVO; 12618c2ecf20Sopenharmony_ci else 12628c2ecf20Sopenharmony_ci sscape->type = SSCAPE_PNP; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci /* 12658c2ecf20Sopenharmony_ci * Read the correct parameters off the ISA PnP bus ... 12668c2ecf20Sopenharmony_ci */ 12678c2ecf20Sopenharmony_ci port[idx] = pnp_port_start(dev, 0); 12688c2ecf20Sopenharmony_ci irq[idx] = pnp_irq(dev, 0); 12698c2ecf20Sopenharmony_ci mpu_irq[idx] = pnp_irq(dev, 1); 12708c2ecf20Sopenharmony_ci dma[idx] = pnp_dma(dev, 0) & 0x03; 12718c2ecf20Sopenharmony_ci if (sscape->type == SSCAPE_PNP) { 12728c2ecf20Sopenharmony_ci dma2[idx] = dma[idx]; 12738c2ecf20Sopenharmony_ci wss_port[idx] = CODEC_IO(port[idx]); 12748c2ecf20Sopenharmony_ci } else { 12758c2ecf20Sopenharmony_ci wss_port[idx] = pnp_port_start(dev, 1); 12768c2ecf20Sopenharmony_ci dma2[idx] = pnp_dma(dev, 1); 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci ret = create_sscape(idx, card); 12808c2ecf20Sopenharmony_ci if (ret < 0) 12818c2ecf20Sopenharmony_ci goto _release_card; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci ret = snd_card_register(card); 12848c2ecf20Sopenharmony_ci if (ret < 0) { 12858c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sscape: Failed to register sound card\n"); 12868c2ecf20Sopenharmony_ci goto _release_card; 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 12908c2ecf20Sopenharmony_ci ++idx; 12918c2ecf20Sopenharmony_ci return 0; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci_release_card: 12948c2ecf20Sopenharmony_ci snd_card_free(card); 12958c2ecf20Sopenharmony_ci return ret; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic void sscape_pnp_remove(struct pnp_card_link *pcard) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci snd_card_free(pnp_get_card_drvdata(pcard)); 13018c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, NULL); 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic struct pnp_card_driver sscape_pnpc_driver = { 13058c2ecf20Sopenharmony_ci .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, 13068c2ecf20Sopenharmony_ci .name = "sscape", 13078c2ecf20Sopenharmony_ci .id_table = sscape_pnpids, 13088c2ecf20Sopenharmony_ci .probe = sscape_pnp_detect, 13098c2ecf20Sopenharmony_ci .remove = sscape_pnp_remove, 13108c2ecf20Sopenharmony_ci}; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci#endif /* CONFIG_PNP */ 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic int __init sscape_init(void) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci int err; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci err = isa_register_driver(&snd_sscape_driver, SNDRV_CARDS); 13198c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 13208c2ecf20Sopenharmony_ci if (!err) 13218c2ecf20Sopenharmony_ci isa_registered = 1; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci err = pnp_register_card_driver(&sscape_pnpc_driver); 13248c2ecf20Sopenharmony_ci if (!err) 13258c2ecf20Sopenharmony_ci pnp_registered = 1; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (isa_registered) 13288c2ecf20Sopenharmony_ci err = 0; 13298c2ecf20Sopenharmony_ci#endif 13308c2ecf20Sopenharmony_ci return err; 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cistatic void __exit sscape_exit(void) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 13368c2ecf20Sopenharmony_ci if (pnp_registered) 13378c2ecf20Sopenharmony_ci pnp_unregister_card_driver(&sscape_pnpc_driver); 13388c2ecf20Sopenharmony_ci if (isa_registered) 13398c2ecf20Sopenharmony_ci#endif 13408c2ecf20Sopenharmony_ci isa_unregister_driver(&snd_sscape_driver); 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cimodule_init(sscape_init); 13448c2ecf20Sopenharmony_cimodule_exit(sscape_exit); 1345