📄 pss.c
字号:
/* * sound/pss.c * * The low level driver for the Personal Sound System (ECHO ESC614). * * * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. * * * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) * Alan Cox modularisation, clean up. * * 98-02-21: Vladimir Michl <vladimir.michl@upol.cz> * Added mixer device for Beethoven ADSP-16 (master volume, * bass, treble, synth), only for speakers. * Fixed bug in pss_write (exchange parameters) * Fixed config port of SB * Requested two regions for PSS (PSS mixer, PSS config) * Modified pss_download_boot * To probe_pss_mss added test for initialize AD1848 * 98-05-28: Vladimir Michl <vladimir.michl@upol.cz> * Fixed computation of mixer volumes * 04-05-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu> * Added code that allows the user to enable his cdrom and/or * joystick through the module parameters pss_cdrom_port and * pss_enable_joystick. pss_cdrom_port takes a port address as its * argument. pss_enable_joystick takes either a 0 or a non-0 as its * argument. * 04-06-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu> * Separated some code into new functions for easier reuse. * Cleaned up and streamlined new code. Added code to allow a user * to only use this driver for enabling non-sound components * through the new module parameter pss_no_sound (flag). Added * code that would allow a user to decide whether the driver should * reset the configured hardware settings for the PSS board through * the module parameter pss_keep_settings (flag). This flag will * allow a user to free up resources in use by this card if needbe, * furthermore it allows him to use this driver to just enable the * emulations and then be unloaded as it is no longer needed. Both * new settings are only available to this driver if compiled as a * module. The default settings of all new parameters are set to * load the driver as it did in previous versions. * 04-07-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu> * Added module parameter pss_firmware to allow the user to tell * the driver where the fireware file is located. The default * setting is the previous hardcoded setting "/etc/sound/pss_synth". * 00-03-03: Christoph Hellwig <chhellwig@gmx.net> * Adapted to module_init/module_exit * 11-10-2000: Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> * Added __init to probe_pss(), attach_pss() and probe_pss_mpu() */#include <linux/config.h>#include <linux/init.h>#include <linux/module.h>#include "sound_config.h"#include "sound_firmware.h"#include "ad1848.h"#include "mpu401.h"/* * PSS registers. */#define REG(x) (devc->base+x)#define PSS_DATA 0#define PSS_STATUS 2#define PSS_CONTROL 2#define PSS_ID 4#define PSS_IRQACK 4#define PSS_PIO 0x1a/* * Config registers */#define CONF_PSS 0x10#define CONF_WSS 0x12#define CONF_SB 0x14#define CONF_CDROM 0x16#define CONF_MIDI 0x18/* * Status bits. */#define PSS_FLAG3 0x0800#define PSS_FLAG2 0x0400#define PSS_FLAG1 0x1000#define PSS_FLAG0 0x0800#define PSS_WRITE_EMPTY 0x8000#define PSS_READ_FULL 0x4000/* * WSS registers */#define WSS_INDEX 4#define WSS_DATA 5/* * WSS status bits */#define WSS_INITIALIZING 0x80#define WSS_AUTOCALIBRATION 0x20#define NO_WSS_MIXER -1#include "coproc.h"#include "pss_boot.h"/* If compiled into kernel, it enable or disable pss mixer */#ifdef CONFIG_PSS_MIXERstatic unsigned char pss_mixer = 1;#elsestatic unsigned char pss_mixer = 0;#endiftypedef struct pss_mixerdata { unsigned int volume_l; unsigned int volume_r; unsigned int bass; unsigned int treble; unsigned int synth;} pss_mixerdata;typedef struct pss_confdata { int base; int irq; int dma; int *osp; pss_mixerdata mixer; int ad_mixer_dev;} pss_confdata; static pss_confdata pss_data;static pss_confdata *devc = &pss_data;static int pss_initialized = 0;static int nonstandard_microcode = 0;static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */static int pss_enable_joystick = 0;/* Parameter for enabling the joystick */static void pss_write(pss_confdata *devc, int data){ int i, limit; limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */ /* * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes * called while interrupts are disabled. This means that the timer is * disabled also. However the timeout situation is a abnormal condition. * Normally the DSP should be ready to accept commands after just couple of * loops. */ for (i = 0; i < 5000000 && time_before(jiffies, limit); i++) { if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY) { outw(data, REG(PSS_DATA)); return; } } printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data);}int __init probe_pss(struct address_info *hw_config){ unsigned short id; int irq, dma; devc->base = hw_config->io_base; irq = devc->irq = hw_config->irq; dma = devc->dma = hw_config->dma; devc->osp = hw_config->osp; if (devc->base != 0x220 && devc->base != 0x240) if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ return 0; if (check_region(devc->base, 0x19 /*16*/)) { printk(KERN_ERR "PSS: I/O port conflict\n"); return 0; } id = inw(REG(PSS_ID)); if ((id >> 8) != 'E') { printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); return 0; } return 1;}static int set_irq(pss_confdata * devc, int dev, int irq){ static unsigned short irq_bits[16] = { 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x0010, 0x0000, 0x0018, 0x0000, 0x0020, 0x0028, 0x0030, 0x0038, 0x0000, 0x0000, 0x0000 }; unsigned short tmp, bits; if (irq < 0 || irq > 15) return 0; tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ if ((bits = irq_bits[irq]) == 0 && irq != 0) { printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq); return 0; } outw(tmp | bits, REG(dev)); return 1;}static int set_io_base(pss_confdata * devc, int dev, int base){ unsigned short tmp = inw(REG(dev)) & 0x003f; unsigned short bits = (base & 0x0ffc) << 4; outw(bits | tmp, REG(dev)); return 1;}static int set_dma(pss_confdata * devc, int dev, int dma){ static unsigned short dma_bits[8] = { 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0006, 0x0007 }; unsigned short tmp, bits; if (dma < 0 || dma > 7) return 0; tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ if ((bits = dma_bits[dma]) == 0 && dma != 4) { printk(KERN_ERR "PSS: Invalid DMA %d\n", dma); return 0; } outw(tmp | bits, REG(dev)); return 1;}static int pss_reset_dsp(pss_confdata * devc){ unsigned long i, limit = jiffies + HZ/10; outw(0x2000, REG(PSS_CONTROL)); for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) inw(REG(PSS_CONTROL)); outw(0x0000, REG(PSS_CONTROL)); return 1;}static int pss_put_dspword(pss_confdata * devc, unsigned short word){ int i, val; for (i = 0; i < 327680; i++) { val = inw(REG(PSS_STATUS)); if (val & PSS_WRITE_EMPTY) { outw(word, REG(PSS_DATA)); return 1; } } return 0;}static int pss_get_dspword(pss_confdata * devc, unsigned short *word){ int i, val; for (i = 0; i < 327680; i++) { val = inw(REG(PSS_STATUS)); if (val & PSS_READ_FULL) { *word = inw(REG(PSS_DATA)); return 1; } } return 0;}static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags){ int i, limit, val, count; if (flags & CPF_FIRST) {/*_____ Warn DSP software that a boot is coming */ outw(0x00fe, REG(PSS_DATA)); limit = jiffies + HZ/10; for (i = 0; i < 32768 && time_before(jiffies, limit); i++) if (inw(REG(PSS_DATA)) == 0x5500) break; outw(*block++, REG(PSS_DATA)); pss_reset_dsp(devc); } count = 1; while ((flags&CPF_LAST) || count<size ) { int j; for (j = 0; j < 327670; j++) {/*_____ Wait for BG to appear */ if (inw(REG(PSS_STATUS)) & PSS_FLAG3) break; } if (j == 327670) { /* It's ok we timed out when the file was empty */ if (count >= size && flags & CPF_LAST) break; else { printk("\n"); printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size); return 0; } }/*_____ Send the next byte */ if (count >= size) { /* If not data in block send 0xffff */ outw (0xffff, REG (PSS_DATA)); } else { /*_____ Send the next byte */ outw (*block++, REG (PSS_DATA)); }; count++; } if (flags & CPF_LAST) {/*_____ Why */ outw(0, REG(PSS_DATA)); limit = jiffies + HZ/10; for (i = 0; i < 32768 && (limit - jiffies >= 0); i++) val = inw(REG(PSS_STATUS)); limit = jiffies + HZ/10; for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) { val = inw(REG(PSS_STATUS)); if (val & 0x4000) break; } /* now read the version */ for (i = 0; i < 32000; i++) { val = inw(REG(PSS_STATUS)); if (val & PSS_READ_FULL) break; } if (i == 32000) return 0; val = inw(REG(PSS_DATA)); /* printk( "<PSS: microcode version %d.%d loaded>", val/16, val % 16); */ } return 1;}/* Mixer */static void set_master_volume(pss_confdata *devc, int left, int right){ static unsigned char log_scale[101] = { 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff }; pss_write(devc, 0x0010); pss_write(devc, log_scale[left] | 0x0000); pss_write(devc, 0x0010); pss_write(devc, log_scale[right] | 0x0100);}static void set_synth_volume(pss_confdata *devc, int volume){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -