/* AWE sound card RAM block device * Written by Claudio Matsuoka , * Uses large portions of Takashi Iwai's awe_wave.c * * A driver to access AWE32/64 RAM as a block device, similar to the * ZorroII block device for Amiga. AWERAM uses major ?? and minor ??. * * Fri Dec 24 11:18:14 EDT 1999 Claudio Matsuoka * First version for kernel 2.3.34 */ /* Currently /dev/aweram should have major 242 and minor 0 */ #define MAJOR_NR 242 /* 240--254 local/experimental range */ #include #include #include #include #include #include #include #include "../linux/drivers/sound/awe_hw.h" #define DEVICE_NAME "AWE RAM block device" #define DEVICE_REQUEST do_aweram_request #define DEVICE_NR(device) (MINOR(device)) #define DEVICE_ON(device) #define DEVICE_OFF(device) #include #define INLINE inline #define AWERAM_MINOR 0 static int aweram_blocksize[1]; static int aweram_size[1]; static int current_device = -1; #ifndef AWE_DEFAULT_BASE_ADDR #define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */ #endif #ifndef AWE_DEFAULT_MEM_SIZE #define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */ #endif #ifdef MODULE /* replace awe_port variable with exported variable */ #define awe_port io #define awe_mem_size memsize int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */ int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */ #endif static int awe_present; /* awe device present? */ /*================================================================ * EMU register access *================================================================*/ /* select a given AWE32 pointer */ static int awe_ports[5]; static int port_setuped; static int awe_cur_cmd = -1; #define awe_set_cmd(cmd) \ if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; } /* store values to i/o port array */ static void setup_ports(int port1, int port2, int port3) { awe_ports[0] = port1; if (port2 == 0) port2 = port1 + 0x400; awe_ports[1] = port2; awe_ports[2] = port2 + 2; if (port3 == 0) port3 = port1 + 0x800; awe_ports[3] = port3; awe_ports[4] = port3 + 2; port_setuped = 1; } /* write 16bit data */ INLINE static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data) { awe_set_cmd (cmd); outw (data, awe_ports[port]); } /* write 32bit data */ INLINE static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data) { unsigned short addr = awe_ports[port]; awe_set_cmd(cmd); outw(data, addr); /* write lower 16 bits */ outw(data >> 16, addr + 2); /* write higher 16 bits */ } /* read 16bit data */ INLINE static unsigned short awe_peek(unsigned short cmd, unsigned short port) { unsigned short k; awe_set_cmd(cmd); k = inw(awe_ports[port]); return k; } /* read 32bit data */ INLINE static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port) { unsigned int k1, k2; unsigned short addr = awe_ports[port]; awe_set_cmd(cmd); k1 = inw(addr); k2 = inw(addr + 2); k1 |= k2 << 16; return k1; } /*---------------------------------------------------------------- * voice table *----------------------------------------------------------------*/ #define MAX_LAYERS AWE_MAX_VOICES /* effects table */ typedef struct FX_Rec { /* channel effects */ unsigned char flags[AWE_FX_END]; short val[AWE_FX_END]; } FX_Rec; /* channel parameters */ typedef struct _awe_chan_info { int channel; /* channel number */ int bank; /* current tone bank */ int instr; /* current program */ int bender; /* midi pitchbend (-8192 - 8192) */ int bender_range; /* midi bender range (x100) */ int panning; /* panning (0-127) */ int main_vol; /* channel volume (0-127) */ int expression_vol; /* midi expression (0-127) */ int chan_press; /* channel pressure */ int vrec; /* instrument list */ int def_vrec; /* default instrument list */ int sustained; /* sustain status in MIDI */ FX_Rec fx; /* effects */ FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */ } awe_chan_info; /* voice parameters */ typedef struct _voice_info { int state; #define AWE_ST_OFF (1<<0) /* no sound */ #define AWE_ST_ON (1<<1) /* playing */ #define AWE_ST_STANDBY (1<<2) /* stand by for playing */ #define AWE_ST_SUSTAINED (1<<3) /* sustained */ #define AWE_ST_MARK (1<<4) /* marked for allocation */ #define AWE_ST_DRAM (1<<5) /* DRAM read/write */ #define AWE_ST_FM (1<<6) /* reserved for FM */ #define AWE_ST_RELEASED (1<<7) /* released */ int ch; /* midi channel */ int key; /* internal key for search */ int layer; /* layer number (for channel mode only) */ int time; /* allocated time */ awe_chan_info *cinfo; /* channel info */ int note; /* midi key (0-127) */ int velocity; /* midi velocity (0-127) */ int sostenuto; /* sostenuto on/off */ awe_voice_info *sample; /* assigned voice */ /* EMU8000 parameters */ int apitch; /* pitch parameter */ int avol; /* volume parameter */ int apan; /* panning parameter */ int acutoff; /* cutoff parameter */ short aaux; /* aux word */ } voice_info; /* voice information */ static voice_info voices[AWE_MAX_VOICES]; /* * AWE32 DRAM access routines */ static void awe_wait(unsigned short delay) { current->state = TASK_INTERRUPTIBLE; schedule_timeout ((HZ*(unsigned long)delay + 44099)/44100); } /* read a word data */ INLINE static void awe_read_dram_buffer (int offset, char *c, int len) { int odd = 0; unsigned short t; awe_poke_dw (AWE_SMALW, offset >> 1); awe_peek (AWE_SMLD); /* Discard first reading */ if ((offset + len) & 1) { odd = 1; len--; } if (offset & 1) { t = awe_peek (AWE_SMLD); *c++ = t & 0x00ff; len--; } for (awe_peek (AWE_SMLD); len--; ) *(unsigned short *)c++ = awe_peek (AWE_SMLD); if (odd) { t = awe_peek (AWE_SMLD); *c = t >> 8; } } /* write a word data */ INLINE static void awe_write_dram_buffer (int offset, char *c, int len) { int odd = 0; unsigned short t; awe_poke_dw(AWE_SMALW, offset >> 1); if ((offset + len) & 1) { odd = 1; len--; } if (offset & 1) { t = awe_peek (AWE_SMLD); awe_poke (AWE_SMLD, (t & 0xff00) | *c++); len--; } for (; len--; c += 2) awe_poke (AWE_SMLD, *(unsigned short *)c); if (odd) { t = awe_peek (AWE_SMLD); awe_poke (AWE_SMLD, (t & 0x00ff) | ((int)*c << 8)); } } /* open DRAM write accessing mode */ static int awe_open_dram_for_write (void) { int channels = 32; int i; /* use all channels for DMA transfer */ for (i = 0; i < channels; i++) { if (i < 0) continue; awe_poke (AWE_DCYSUSV(i), 0x80); awe_poke_dw (AWE_VTFT(i), 0); awe_poke_dw (AWE_CVCF(i), 0); awe_poke_dw (AWE_PTRX(i), 0x40000000); awe_poke_dw (AWE_CPF(i), 0x40000000); awe_poke_dw (AWE_PSST(i), 0); awe_poke_dw (AWE_CSL(i), 0); awe_poke_dw (AWE_CCCA(i), 0x06000000); voices[i].state = AWE_ST_DRAM; } /* point channels 31 & 32 to ROM samples for DRAM refresh */ awe_poke_dw (AWE_VTFT(30), 0); awe_poke_dw (AWE_PSST(30), 0x1d8); awe_poke_dw (AWE_CSL(30), 0x1e0); awe_poke_dw (AWE_CCCA(30), 0x1d8); awe_poke_dw (AWE_VTFT(31), 0); awe_poke_dw (AWE_PSST(31), 0x1d8); awe_poke_dw (AWE_CSL(31), 0x1e0); awe_poke_dw (AWE_CCCA(31), 0x1d8); voices[30].state = AWE_ST_FM; voices[31].state = AWE_ST_FM; /* if full bit is on, not ready to write on */ if (awe_peek_dw (AWE_SMALW) & 0x80000000) { for (i = 0; i < channels; i++) { awe_poke_dw(AWE_CCCA(i), 0); voices[i].state = AWE_ST_OFF; } return -ENOSPC; } return 0; } /* open DRAM for RAM size detection */ static void awe_open_dram_for_check(void) { int i; for (i = 0; i < AWE_NORMAL_VOICES; i++) { awe_poke (AWE_DCYSUSV(i), 0x80); awe_poke_dw (AWE_VTFT(i), 0); awe_poke_dw (AWE_CVCF(i), 0); awe_poke_dw (AWE_PTRX(i), 0x40000000); awe_poke_dw (AWE_CPF(i), 0x40000000); awe_poke_dw (AWE_PSST(i), 0); awe_poke_dw (AWE_CSL(i), 0); if (i & 1) /* DMA write */ awe_poke_dw(AWE_CCCA(i), 0x06000000); else /* DMA read */ awe_poke_dw(AWE_CCCA(i), 0x04000000); voices[i].state = AWE_ST_DRAM; } } /* close dram access */ static void awe_close_dram(void) { int i; /* wait until FULL bit in SMAxW register be false */ for (i = 0; i < 10000; i++) { if (!(awe_peek_dw(AWE_SMALW) & 0x80000000)) break; awe_wait(10); } for (i = 0; i < AWE_NORMAL_VOICES; i++) { if (voices[i].state == AWE_ST_DRAM) { awe_poke_dw(AWE_CCCA(i), 0); awe_poke(AWE_DCYSUSV(i), 0x807F); voices[i].state = AWE_ST_OFF; } } } /*================================================================ * detect presence of AWE32 and check memory size *================================================================*/ /* detect emu8000 chip on the specified address; from VV's guide */ static int awe_detect_base(int addr) { setup_ports(addr, 0, 0); if ((awe_peek(AWE_U1) & 0x000F) != 0x000C) return 0; if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058) return 0; if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003) return 0; printk (KERN_INFO DEVICE_NAME ": AWE32 found at %x\n", addr); return 1; } static int awe_detect(void) { int base; struct pci_dev *idev = NULL; /* first try ISA-PNP */ idev = isapnp_find_dev(NULL, ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_FUNCTION(0x0045), idev); if (idev) { /* found something, maybe */ int err; err = idev->prepare(idev); if (err & err != -EBUSY) { printk (KERN_ERR DEVICE_NAME ": ISA-PNP AWE32 found but cannot autoconfigure\n"); return 0; } if (!idev->active) { err = idev->activate(idev); if (err < 0) { printk (KERN_ERR DEVICE_NAME ": ISA-PNP AWE32 autoconfigured but cannot activate\n"); idev->deactivate(idev); return 0; } } if (awe_detect_base(idev->resource[0].start)) return 1; printk (KERN_ERR DEVICE_NAME ": ISA-PNP AWE32 not found\n"); return 0; } if (awe_port) /* use default i/o port value */ setup_ports(awe_port, 0, 0); else { /* probe it */ for (base = 0x620; base <= 0x680; base += 0x20) if (awe_detect_base(base)) return 1; printk (KERN_ERR DEVICE_NAME ": AWE32 not found\n"); return 0; } return 1; } /*================================================================ * check dram size on AWE board *================================================================*/ /* any three numbers you like */ #define UNIQUE_ID1 0x1234 #define UNIQUE_ID2 0x4321 #define UNIQUE_ID3 0xFFFF static void awe_check_dram(void) { if (awe_present) /* already initialized */ return; if (awe_mem_size >= 0) { /* given by config file or module option */ awe_mem_size *= 1024; /* convert to Kbytes */ return; } awe_open_dram_for_check(); awe_mem_size = 0; /* set up unique two id numbers */ awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET); awe_poke(AWE_SMLD, UNIQUE_ID1); awe_poke(AWE_SMLD, UNIQUE_ID2); while (awe_mem_size < AWE_MAX_DRAM_SIZE) { awe_wait(5); /* read a data on the DRAM start address */ awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET); awe_peek(AWE_SMLD); /* discard stale data */ if (awe_peek(AWE_SMLD) != UNIQUE_ID1) break; if (awe_peek(AWE_SMLD) != UNIQUE_ID2) break; awe_mem_size += 512; /* increment 512kbytes */ /* Write a unique data on the test address; * if the address is out of range, the data is written on * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are * broken by this data. */ awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L); awe_poke(AWE_SMLD, UNIQUE_ID3); awe_wait(5); /* read a data on the just written DRAM address */ awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L); awe_peek(AWE_SMLD); /* discard stale data */ if (awe_peek(AWE_SMLD) != UNIQUE_ID3) break; } awe_close_dram(); printk (KERN_INFO DEVICE_NAME ": %d Kb memory detected\n", awe_mem_size); /* convert to Kbytes */ awe_mem_size *= 1024; } #if 0 /* initialize DMA address */ static void awe_init_dma(void) { awe_poke_dw(AWE_SMALR, 0); awe_poke_dw(AWE_SMARR, 0); awe_poke_dw(AWE_SMALW, 0); awe_poke_dw(AWE_SMARW, 0); } #endif static void awe_initialize(void) { /* initialize hardware configuration */ awe_poke(AWE_HWCF1, 0x0059); awe_poke(AWE_HWCF2, 0x0020); /* disable audio; this seems to reduce a clicking noise a bit.. */ awe_poke(AWE_HWCF3, 0); /* initialize audio channels */ /* awe_init_audio(); */ /* initialize DMA */ /* awe_init_dma(); */ /* initialize init array */ /* awe_init_array(); */ /* check DRAM memory size */ awe_check_dram(); /* initialize the FM section of the AWE32 */ /* awe_init_fm(); */ /* set up voice envelopes */ /* awe_tweak(); */ /* enable audio */ /* awe_poke(AWE_HWCF3, 0x0004); */ /* set default values */ /* awe_init_ctrl_parms(1); */ /* set equalizer */ /* awe_update_equalizer(); */ /* set reverb & chorus modes */ /* awe_update_reverb_mode(); */ /* awe_update_chorus_mode(); */ } static void do_aweram_request (request_queue_t *q) { u_long start, len; while (1) { INIT_REQUEST; start = CURRENT->sector << 9; len = CURRENT->current_nr_sectors << 9; if ((start + len) > awe_mem_size) { printk (KERN_ERR DEVICE_NAME ": bad access: block=%ld, count=%ld\n", CURRENT->sector, CURRENT->current_nr_sectors); end_request (0); continue; } if ((CURRENT->cmd != READ) && (CURRENT->cmd != WRITE)) { printk (KERN_ERR DEVICE_NAME ": bad command: %d\n", CURRENT->cmd); end_request (0); continue; } if (CURRENT->cmd == READ) awe_read_dram_buffer (start, CURRENT->buffer, len); else awe_write_dram_buffer (start, CURRENT->buffer, len); end_request (1); } } static int aweram_open (struct inode *inode, struct file *filp) { int device; device = DEVICE_NR (inode->i_rdev); if (current_device != -1 && current_device != device) return -EBUSY; if (current_device == -1) { switch (device) { case AWERAM_MINOR: if (awe_mem_size != 0) printk (KERN_INFO DEVICE_NAME ": using %i bytes of AWE RAM\n", awe_mem_size); break; default: return -ENODEV; } if (awe_mem_size == 0) { printk (KERN_NOTICE DEVICE_NAME ": no AWE RAM found\n"); return -ENOMEM; } current_device = device; blk_size[MAJOR_NR] = aweram_size; } MOD_INC_USE_COUNT; awe_open_dram_for_write (); return 0; } static int aweram_release (struct inode *inode, struct file *filp) { if (current_device == -1) return 0; sync_dev (inode->i_rdev); awe_close_dram (); MOD_DEC_USE_COUNT; return 0; } static struct block_device_operations aweram_fops = { open: aweram_open, release: aweram_release, }; static int __init aweram_init (void) { if (!awe_detect()) return -ENXIO; if (register_blkdev (MAJOR_NR, DEVICE_NAME, &aweram_fops)) { printk (KERN_ERR DEVICE_NAME ": Unable to get major %d\n", MAJOR_NR); return -EBUSY; } awe_initialize(); aweram_blocksize[0] = 1 << 10; aweram_size[0] = awe_mem_size >> 10; printk (KERN_INFO DEVICE_NAME ": size = %d\n", aweram_size[0]); blk_init_queue (BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); blksize_size[MAJOR_NR] = aweram_blocksize; blk_size[MAJOR_NR] = aweram_size; awe_present = 1; return 0; } static void __exit aweram_exit (void) { if (unregister_blkdev (MAJOR_NR, DEVICE_NAME)) printk (KERN_ERR DEVICE_NAME ": unregister of device failed\n"); } module_init(aweram_init) module_exit(aweram_exit)