/* * sound/wavnc.c * * The low level driver for the RWA010 Rockwell Wave Artist codec chip * used in the Corel Computer NetWinder. * */ /* * Copyright (C) by Corel Computer 1998 * * RWA010 specs received under NDA from Rockwell * * 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. */ #include #define DEB(x) #define DDB(x) #define DEB1(x) #include "sound_config.h" #ifdef CONFIG_WA_VNC #include "wa_vnc.h" #define VERSION "0.82" #define WA_TIMER_PERIOD HZ/4 //check slider 4 times/sec #define PCM_NON 0 #define PCM_DAC 2 #define PCM_ADC 1 #define MIXER_PRIVATE3_RESET 0x53570000 #define MIXER_PRIVATE3_READ 0x53570001 #define MIXER_PRIVATE3_WRITE 0x53570002 #define VNC_INTERNAL_SPKR 0x01 //the sw mute on/off control bit #define VNC_INTERNAL_MIC 0x10 //the hw internal/handset mic bit #define VNC_LINE_OUT_MUTE 0x40 //the sw mute of line-out bit #define VNC_HANDSET_SW_ONLY 0x80 //software in total control of handset! // use RECSRC = speaker to mark the internal microphone //Some cheating involved here: there is no way to relay to the system, which //microphone in in use (left = handset, or right = internal) //So while I do not flag SPEAKER in the Recording Devices Mask, when on internal // mike - I set the speaker bit hi. Some mixers can be confused a bit... #define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE |\ SOUND_MASK_MIC |\ SOUND_MASK_LINE1)//Line1 = analog phone #define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ SOUND_MASK_PCM |\ SOUND_MASK_MIC |\ SOUND_MASK_LINE |\ SOUND_MASK_LINE1 |\ SOUND_MASK_RECLEV |\ SOUND_MASK_IMIX |\ SOUND_MASK_VOLUME) extern int getLED(void); extern int setLED(int leds, int programID); extern int set_CPLD(unsigned int bitset, unsigned int bitmask); typedef struct { int base; int irq; int dma1, dma2; int dual_dma; /* 1, when two DMA channels allocated */ int debug_flag; int audio_flags; int record_dev, playback_dev; int xfer_count; int audio_mode; int open_mode; int intr_active; char *chip_name, *name; int *wa_osp; /* Mixer parameters */ int recmask; //currently enabled recording device! int supported_devices; //SUPPORTED_MIXER_DEVICES int rec_devices; //POSSIBLE_RECORDING_DEVICES int dev_no; int irq_ok; int handset_state; int mute_state; int soft_mute_flag; //1 - handset mic/spkr + mute in sw int soft_volume_flag; //1 - volume controlled by mixer prog int hw_volume; //remember old setting of hw vol int triggerflag; //bit 0=write trigger,bit 1=read trigger } wavnc_info; static unsigned short levels[SOUND_MIXER_NRDEVICES] = { 0x5555, /* Master Volume */ 0x0000, /* Bass */ 0x0000, /* Treble */ 0x5555, /* FM */ 0x4b4b, /* PCM */ 0x0000, /* PC Speaker */ 0x0000, /* Ext Line */ 0x0000, /* Mic */ 0x0000, /* CD */ 0x0000, /* Recording monitor */ 0x0000, /* SB PCM */ 0x0000, /* Recording level */ 0x0000, /* Input gain */ 0x0000, /* Output gain */ 0x0000, /* Aux1 */ 0x0000, /* Aux2 */ 0x0000 /* Aux3 */ }; typedef struct wavnc_port_info { int open_mode; int speed; int channels; int audio_format; } wavnc_port_info; static int nr_wavnc_devs = 0; static wavnc_info adev_info; static struct timer_list wa_timer; static int wavnc_open (int dev, int mode); static void wavnc_close (int dev); static int wavnc_ioctl (int dev, unsigned int cmd, caddr_t arg); static void wavnc_output_block (int dev, unsigned long buf, int count, int intrflag); static void wavnc_start_input (int dev, unsigned long buf, int count, int intrflag); static int wavnc_prepare_for_output (int dev, int bsize, int bcount); static int wavnc_prepare_for_input (int dev, int bsize, int bcount); static void wavnc_halt (int dev); static void wavnc_halt_input (int dev); static void wavnc_halt_output (int dev); static void wavnc_trigger (int dev, int bits); void waintr (int irq, void *dev_id, struct pt_regs *dummy); int vnc_slider(wavnc_info * devc); static void wa_slider(unsigned long data); int wa_sendcmd (unsigned int cmd); int wa_writecmd (unsigned int cmd, unsigned int arg); void delay10ms(void); static int mixer_output (int right_vol, int left_vol, int div, int bits, int mixer1, int mixer2, int shift); static int wa_mixer_set (wavnc_info * devc, int whichDev, unsigned int level); static void wavnc_mixer_reset (wavnc_info * devc); void mute_mono(wavnc_info * devc, int mute); void mute_lout(wavnc_info * devc, int mute); void wa_mute(wavnc_info * devc, int mute); int reverse = 0; //can be set from cmd line to revers slider void delay10ms(void) { //wait 10 ms current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + 1; //wait 10 ms schedule(); } static void wa_slider(unsigned long data) { if (vnc_slider(&adev_info)) { wa_timer.expires = jiffies + 5; //mixer reported change } else { wa_timer.expires = jiffies + WA_TIMER_PERIOD; } add_timer(&wa_timer); } int wa_sendcmd (unsigned int cmd) { int count; DEB1(printk("wa_sendcmd: cmd=0x%04X...", cmd)); tenmicrosec(adev_info.wa_osp); if(inb(STATR) & CMD_RF) { count = inw(CMDR); //flush the port printk("", count); //useless, but produces a needed delay... DEB1(printk("\nwa_sendcmd: flushed old response: %04X.\n",count)); } count = 5000; //preset timeout at 5000 loops.... while(!(inb(STATR) & CMD_WE) && count--) {}; //wait till bit HI if(count) //ready BEFORE timeout? { outw(cmd, CMDR); //output the command DEB1(printk(" Done OK.\n")); tenmicrosec(adev_info.wa_osp); return 1; } else { DEB1(printk(" Error!\n")); return 0; } } int wa_writecmd (unsigned int cmd, unsigned int arg) { int count; DEB1(printk("wa_writecmd: cmd=0x%04X, arg=0x%04X ...", cmd, arg)); tenmicrosec(adev_info.wa_osp); if(inb(STATR) & CMD_RF) { count = inw(CMDR); //flush the port DEB1(printk("\nwa_writecmd: flushed %04X response.\n",count)); } count = 5000; //preset timeout at 5000 loops.... while(!(inb(STATR) & CMD_WE) && count--) {}; //wait till bit HI if(count) //ready BEFORE timeout? { //start with writing the command word outw(cmd, CMDR); //output the command count = 5000; //preset timeout at 5000 loops.... while(!(inb(STATR) & CMD_WE) && count--) {}; //wait till bit HI if(!count) { DEB1(printk(" Timeout 1!.\n")); return(0); } else { //write the parameter outw(arg,CMDR); tenmicrosec(adev_info.wa_osp); //Rockwell answers in 5 us max. if(!(inb(STATR) & CMD_RF)) tenmicrosec(adev_info.wa_osp); count = inw(CMDR); DEB1(printk("Done=%04X.\n",count)); if (!count) tenmicrosec(adev_info.wa_osp); return(count); //0 = error, 1 = OK } } else { DEB1(printk(" Timeout 2!.\n")); return 0; } } //----------------------------------------------------------------------- //This function is suitable to update settings in all "symmetrical" registers, //namely 1..8. Registers 9 and 10 are not adhering to the left-right concept... //Parameters: right volume, left volume, scale of volume (0..max), // bitmask in the register, left reg, right reg, the LSB of mask static int mixer_output (int right_vol, int left_vol, int div, int bits, int mixer1, int mixer2, int shift)/* Mixer registers to touch */ { int left = left_vol * div / 100; int right = right_vol * div / 100; int left1 = 0; int right1 = 0; int left2 = 0; int right2 = 0; left1 = (mixer1-1)<<8; // get ready for command nn30H right1= (mixer2-1)<<8; //first read current values wa_sendcmd(left1+0x30); while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... left2 = inw(CMDR); wa_sendcmd(right1+0x30); while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... right2 = inw(CMDR); DDB(printk("wa_mixer: Current left=%04X, right=%04X.\n",left2,right2)); //now that both current values are in - update bits... if (shift) left <<= shift; left2 &= ~bits; left2 |= left; if (shift) right <<= shift; right2 &= ~bits; right2 |= right; //and finally - write the reg pair back.... wa_sendcmd(0x32); wa_sendcmd(left2); wa_sendcmd(right2); return (left_vol | (right_vol << 8)); } static int wa_mixer_set (wavnc_info * devc, int whichDev, unsigned int level) { unsigned int left, right, devmask, changed, i, mixer = 0; unsigned int left1, right1; left = level & 0x7f; right = (level & 0x7f00) >> 8; if (whichDev < SOUND_MIXER_NRDEVICES) { if ((1 << whichDev) & devc->rec_devices) { mixer = 0x20; } else { mixer = 0x00; } } DDB (printk ("wa_mixer_set(dev = %d, level = %X, mixer = %X.)\n", whichDev, level, mixer)); switch (whichDev) { // We have 3 bits on the Left/Right Mixer Gain, bits 3,2,1 on 3 and 7 case SOUND_MIXER_VOLUME: /* Master volume (0-7) */ levels[whichDev] = mixer_output (right, left, 7, 0x000E, 3, 7, 1); break; case SOUND_MIXER_MIC: /* Mono microphone (0-3) mute,0db,10db,20db */ //this control is usable only if mic is hooked to a mixer, instead ADC // levels[whichDev] = mixer_output (right,left,31,0x07C0,3,7,6); levels[whichDev] = mixer_output (right,left,3,0x0030,3,7,4); /* //we do not need to mute volume of an unused mic - it is simply unused... if (devc->handset_state & VNC_INTERNAL_MIC)// handset not plugged in? levels[whichDev] = mixer_output (right,0,3,0x0030,3,7,4); else levels[whichDev] = mixer_output (0,left,3,0x0030,3,7,4); */ break; // line and analog phone volumes are fake - present to enable selecting it // as recoreding devices. The real volume is controlled by RECLEV // use LOUT/ROUT bits 10...6, reg 1 and 5 case SOUND_MIXER_LINE: /* External line (0-31) */ // levels[whichDev] = mixer_output (right,left,31,0x07C0,1,5,6); levels[whichDev] = level; break; // use LINE1 bits 5...1, reg 1 and 5 case SOUND_MIXER_LINE1: /* Mono External Aux1 (0-31) */ // levels[whichDev] = mixer_output (right,left,31,0x003E,1,5,1); levels[whichDev] = level; break; case SOUND_MIXER_RECLEV: /* Recording level (0-7) */ // levels[whichDev] = mixer_output (right,left,7,0x0007,4,8,0); // actually - convert it into adjustment of 16 levels by use of // the RX_Filter_Gain bit... levels[whichDev] = mixer_output (right,left,15,0x000F,4,8,0); break; case SOUND_MIXER_PCM: /* WA PCM (0-7FFFH) */ left1 = left * 0x7FFF /100; right1= right* 0x7FFF /100; wa_sendcmd(0x0031); //change left and right PCM wa_sendcmd(left1); wa_sendcmd(right1); //we cannot store full 15 bits in 2*8 bits - so store only upper bytes levels[whichDev] = left | (right <<8); break; case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */ left1 = left * 0x7FFF /100; right1= right* 0x7FFF /100; wa_sendcmd(0x0131); //change left and right FM wa_sendcmd(left1); wa_sendcmd(right1); //we cannot store full 15 bits in 2*8 bits - so store only upper bytes levels[whichDev] = left | (right <<8); break; case SOUND_MIXER_RECSRC: devmask = level & POSSIBLE_RECORDING_DEVICES; changed = devmask ^ devc->recmask; devc->recmask = devmask; DDB(printk("RECSRC: recmask: 0x%02X, changed: 0x%02X.\n",devmask,changed)); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (changed & (1 << i)) { wa_mixer_set (devc, i, levels[i]); } //new mixer programming - switch recording source using R/L_ADC_Mux_Select //we are playing with left/right mux bit fields in reg 9. // can not switch Mux_Select while recording!!! // so for microphones - enable both left and right, play with levels only! // unfortunately, we need to select the src of mono recording (left or right) // before starting the recording - so can not dynamically switch between // handset amd internal microphones... wa_sendcmd(0x0830); //get current reg 9 while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... left1 = inw(CMDR); wa_sendcmd(0x0930); // and reg 10 - just to write it back... while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... right1 = inw(CMDR); DDB(printk("RECSRC: old left: 0x%04X, old right: 0x%04X.\n", left1&0x07, (left1>>3)&0x07)); left1 &= ~0x03F; //kill current left/right mux input select if (devmask & SOUND_MASK_MIC) // handset or internal mic { // wa_sendcmd(0x16); //PAUSE the ADC - no desired effect if (devc->handset_state & VNC_INTERNAL_MIC)// handset not plugged in? { wa_sendcmd(0x0134); //set mono recording from right mic // left1 |= 0x0028; //right=mic, left=none // devc->rec_devices |= SOUND_MASK_SPEAKER; //pretend int mic } else { wa_sendcmd(0x0034); //set mono rec from left mic // left1 |= 0x0005; // right=none, left=mic // devc->rec_devices &= ~SOUND_MASK_SPEAKER; //show no int mic } left1 |= 0x002D; // right=mic, left=mic // wa_sendcmd(0x18); //RESUME the ADC } else if (devmask & SOUND_MASK_LINE) { wa_sendcmd(0x0034); //set mono rec from left (default) left1 |= 0x0012; // right=Line, left=Line; } else if (devmask & SOUND_MASK_LINE1) { wa_sendcmd(0x0034); //set mono rec from left aux1 left1 |= 0x0004; // right=none, left=Aux1; } DDB(printk("RECSRC level %X, left=0x%04X, right=0x%04X.\n",level, left1&0x07, (left1>>3)&0x07)); //and finally - write the reg pair back.... wa_sendcmd(0x32); wa_sendcmd(left1); wa_sendcmd(right1); //re-kick the recmon, if monitoring is on... if (levels[9]) wa_mixer_set (devc, 9, levels[9]); return devc->recmask; //do not save in "levels",return current setting break; case SOUND_MIXER_IMIX: /* Recording monitor */ //this part is good, if input connected to a mixer... //so can be used for record-only modes... wa_sendcmd(0x0330); //get current reg 4 while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... left1 = inw(CMDR); wa_sendcmd(0x0730); // and reg 8 while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... right1 = inw(CMDR); DDB(printk("IMIX: old left: 0x%04X, old right: 0x%04X, recmask=%X.\n",left1, right1,devc->recmask)); left1 &= ~0x03F0; //kill current left/right mux input select right1 &= ~0x03F0; // except for the DSP-out (Tx-filter = bit 10) if (devc->recmask & SOUND_MASK_MIC) // handset or internal mic { if (devc->handset_state & VNC_INTERNAL_MIC)// handset not plugged in? { left1 |= 0x0D00; // == cross-channel mic; right1 |= 0x0C80; // == right mic is internal mic; } else { left1 |= 0x0C80; // == left mic is handset mic right1 |= 0x0D00; // == cross-channel mic; } mixer_output (right,left,31,0x07C0,3,7,6); } if (devc->recmask & SOUND_MASK_LINE1) { left1 |= 0x0C40; // == Aux1; right1 |= 0x0C00; // == none; mixer_output (right,left,31,0x003E,1,5,1); } if (devc->recmask & SOUND_MASK_LINE) { left1 |= 0x0C10; // == Line; right1 |= 0x0C10; // == Line; mixer_output (right,left,31,0x07C0,1,5,6); } DDB(printk("IMIX %X: left(4) 0x%04X, right(8) 0x%04X.\n",level,left1,right1)); //and finally - write the reg pair back.... wa_sendcmd(0x32); wa_sendcmd(left1); wa_sendcmd(right1); levels[whichDev] = level; break; default: return -(EINVAL); } return (levels[whichDev]); } int vnc_slider(wavnc_info * devc) { unsigned int volume, temp, hw_vol; long unsigned flags; unsigned int start_time; #if 0 //experimentaly add here checking of the orange "reset" button //if detected - change LED to orange... //this space is used, to avoid installing of a separate timer callback... printk("%02X%02X ",cShadow338,inb(0x338)); temp = inb(0x338); if (temp & 0x01) //if pressed { printk("Orange pressed, cShadow338 = 0x%02X...\n",cShadow338); if (!(cShadow338 & 0x80)) //red LED not on yet? { printk("Orange 1 pressed, calling setLED 3...\n"); setLED(3,(int)&vnc_slider); printk("setLED done, cShadow338 = 0x%02X, getLED = %X...\n",cShadow338,getLED()); } else { printk("Orange 2 pressed, calling setLED 1...\n"); setLED(1,(int)&vnc_slider); printk("setLED done, cShadow338 = 0x%02X, getLED = %X...\n",cShadow338,getLED()); } } #endif /* // joystick stuff - called from a timer callback... */ //read the "buttons" state. Bit 0 = handset present, bit 1 = offhook // the state is "querable" via a private2 IOCTL call temp = inb(0x201) & 0x30; //if NOT under exclusive sw control, any change in handset plugged in/out? if ((!(devc->soft_mute_flag & VNC_HANDSET_SW_ONLY)) && ((temp&VNC_INTERNAL_MIC) != (devc->handset_state&VNC_INTERNAL_MIC))) { //DEB(printk("wa_mixer: handset: old = %02X, new = %02X.\n",devc->handset_state, temp)); devc->handset_state = temp; //DEB(printk("wa_mixer: soft_mute: old = %02X, new = 0.\n",devc->soft_mute_flag)); devc->soft_mute_flag = 0x00; //handset on (bit=0)? enable handset mic, disable internal mic //only if current setting is for the other mic (not the line...) // actually changed: switch to apriopriate microphone if (!(temp&VNC_INTERNAL_MIC)) { mute_mono(devc,1); //cut - off speaker mute_lout(devc,1); //cut off the line-out as well /* // copy right mic volume to left mic volume temp = (levels[SOUND_MIXER_MIC])>>8; temp &= 0x7F; levels[SOUND_MIXER_MIC] = mixer_output (0,temp,15,0x000F,4,8,0); */ } else { //speaker on, internal mic on mute_mono(devc,0); mute_lout(devc,0); //reenable line-out as well /* // copy left mic volume to right mic volume temp = (levels[SOUND_MIXER_MIC])&0x7F; levels[SOUND_MIXER_MIC] = mixer_output (temp,0,15,0x000F,4,8,0); */ } if (devc->recmask & SOUND_MASK_MIC) // handset or internal mic { wa_mixer_set(devc, SOUND_MIXER_RECSRC, devc->recmask); } } volume = 0xFF; save_flags(flags); cli(); outb(0x1FF, 0x201); //fire joystick timer start_time = *(volatile unsigned int*)(0xE1000304); while (volume && (inb(0x201) & 0x01)) { volume--; } hw_vol = *(volatile unsigned int*)(0xE1000304); restore_flags(flags); hw_vol = start_time - hw_vol; // occasionally we get a hw timer wrap condition in which case the // hw_vol is negative. In such case just wait for a next callback... // Also, if joystick bit does not respond - ignore volume setting... if ((hw_vol & 0x80000000) || !volume) return(0); //no change volume = hw_vol; //save it for debug display if (!reverse) { volume >>= 5; volume = 154-volume; } else { volume >>= 6; volume -= 25; } if(volume > 0x80000000) volume = 0; //if negative - make it 0 if(volume > 100) volume = 100; temp = levels[SOUND_MIXER_VOLUME] & 0xFF; //use only left channel /* volume = (volume + old_slider_volume + temp)/3;//average the new volume if (volume>=24) volume += 3; //fudge factor... old_slider_volume = volume; */ /* if (hw_vol != old_slider_volume) { temp=vncdebug; vncdebug=1; debprintf("Start_time: 0x%X, end: 0x%X, diff=%d.\n", start_time, hw_vol, start_time-hw_vol); vncdebug=temp; old_slider_volume=hw_vol; } */ //slider quite often reads +-8, so debounce this random noise if ((volume != temp) && ((volumetemp+7))) { // extern void putstr(char *s); // char szTemp[48]; // sprintf(szTemp,"Slider read: %d, volume: %d.\n", hw_vol, volume); // putstring(szTemp); DEB(printk("Slider read: %d, volume: %d.\n", hw_vol, volume)); // dbprintf("Slider read: %d, volume: %d.\n", hw_vol, volume); if (devc->soft_volume_flag) // in software mode??? { // if so - check if the current reading varies by more then 15 // from the read at the moment we switch to soft_volume. if (devc->hw_volume > volume) { if (devc->hw_volume - volume > 20) //take over? { //printk("waMixer: to hw vol, hw_vol = %d, cur vol = %d.\n",devc->hw_volume, volume); devc->soft_volume_flag = 0; wa_mixer_set(devc, SOUND_MIXER_VOLUME, (volume<<8)+volume); } } else { if (volume - devc->hw_volume > 20) //take over? { //printk("waMixer: to hw vol, hw_vol = %d, cur vol = %d.\n",devc->hw_volume, volume); devc->soft_volume_flag = 0; wa_mixer_set(devc, SOUND_MIXER_VOLUME, (volume<<8)+volume); } } } else { wa_mixer_set(devc, SOUND_MIXER_VOLUME, (volume<<8)+volume); } return (1); } else return(0); //no change } /*****/ static void wavnc_mixer_reset (wavnc_info * devc) { // char name[32]; int foo; DDB (printk ("wa_mixer: wa_mixer_reset()\n")); // sprintf (name, "%s_%d", devc->chip_name, nr_wavnc_devs); wa_sendcmd(0x33); //reset mixer cmd // set input for ADC to come from a mux (left and right) == reg 9,initially none wa_sendcmd(0x32); wa_sendcmd(0x9800); wa_sendcmd(0xA836); /* //we do not need this anymore, because we hook the mics directly to ADC // set input gain for mics: left (handset = 10dB, right (internal) = 20dB wa_sendcmd(0x32); wa_sendcmd(0x382C); wa_sendcmd(0x783C); */ // set mixer input select to none, RX filter gains 0 db wa_sendcmd(0x32); wa_sendcmd(0x4C00); wa_sendcmd(0x8C00); // set bit 0 reg 2 to 1 - unmute MonoOut wa_sendcmd(0x32); //set mixer pair wa_sendcmd(0x2801); //reg 2 - unmute MonoOut wa_sendcmd(0x6800); //reg 6 for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) wa_mixer_set (devc, foo, levels[foo]); //set default input device = internal mic devc->recmask = 0; //current recording device = none devc->handset_state = VNC_INTERNAL_MIC; //assume no handset // wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); devc->soft_volume_flag = 0; //start from enabling the hw setting vnc_slider(devc); //adjust master volume as per slider devc->supported_devices = SUPPORTED_MIXER_DEVICES; devc->rec_devices = POSSIBLE_RECORDING_DEVICES; } //----------------------------------------------------------------------- void wa_mute(wavnc_info * devc, int mute) { } //----------------------------------------------------------------------- void mute_mono(wavnc_info * devc, int mute) { DDB(printk ("wa_vnc: mute=%d.\n",mute)); if (mute) { set_CPLD(0,2); //reset bit } else { set_CPLD(2,2); //set bit } devc->mute_state = mute; //remember the current setting... } //----------------------------------------------------------------------- void mute_lout(wavnc_info * devc, int mute) { unsigned int left1, right1; DDB(printk ("wa_vnc: mute_lout=%d.\n",mute)); wa_sendcmd(0x0030); //get current reg 1 while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... left1 = inw(CMDR); wa_sendcmd(0x0430); // and reg 5 while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... right1 = inw(CMDR); if (mute) { left1 &= ~0x01; //mute line out right1 &= ~0x01; } else { left1 |= 0x01; //unmute line out right1 |= 0x01; } //and finally - write the reg pair back.... wa_sendcmd(0x32); wa_sendcmd(left1); wa_sendcmd(right1); // devc->mute_state = mute; //remember the current setting... } static int wavnc_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) { // wavnc_info *devc = mixer_devs[dev]->devc; wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; //use this call to override the automatic handset behaviour - ignore handset //bit 0x80 = total control over handset - do not react to plug/unplug //bit 0x40 = 1 == mute line-out output, otherwise unmute //bit 0x10 = 1 == internal mic, otherwise handset mic //bit 0x01 = 1 == mute internal speaker, otherwise unmute if (cmd == SOUND_MIXER_PRIVATE1) { int val, temp; val = *(int *) arg; DDB(printk("MIXER_PRIVATE1: passed parameter = 0x%X.\n",val)); if (val & ~(VNC_HANDSET_SW_ONLY|VNC_INTERNAL_SPKR| VNC_INTERNAL_MIC|VNC_LINE_OUT_MUTE)) return (-EINVAL); //check if parameter is logical... devc->soft_mute_flag = val; temp = val & VNC_INTERNAL_SPKR; if(temp != devc->mute_state) { DDB(printk("MIXER_PRIVATE1: mute_mono(0x%X).\n",temp)); mute_mono(devc, temp); } temp = val & VNC_LINE_OUT_MUTE; //for now we are not saving the lout_mute info... // if(temp != devc->mute_state) { DDB(printk("MIXER_PRIVATE1: mute_line_out(0x%X).\n",temp)); mute_lout(devc, temp); } // temp = devc->handset_state; // do not check if it is not already in the right setting, since we // are laying about the current state... // if ((val & VNC_INTERNAL_MIC) != temp) { devc->handset_state = val & VNC_INTERNAL_MIC; if (devc->recmask == SOUND_MASK_MIC) { DDB(printk("MIXER_PRIVATE1: mixer_set(0x%X).\n",devc->handset_state)); wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); } // devc->handset_state = temp; } return(0); } //get current PRIVATE setting... if (cmd == SOUND_MIXER_PRIVATE2) { *(int *) arg = devc->handset_state | devc->soft_mute_flag; DDB(printk("MIXER_PRIVATE2: returning 0x%X.\n", *(int*)arg)); return(0); } /* if (cmd == SOUND_MIXER_PRIVATE2) { #define VNC_SOUND_PAUSE 0x53 //to pause the DSP #define VNC_SOUND_RESUME 0x57 //to unpause the DSP int val; val = *(int *) arg; printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val); if (val == VNC_SOUND_PAUSE) { wa_sendcmd(0x16); //PAUSE the ADC } else if (val == VNC_SOUND_RESUME) { wa_sendcmd(0x18); //RESUME the ADC } else { return (-EINVAL); //invalid parameters... } return(0); } */ if (cmd == SOUND_MIXER_PRIVATE3) { long unsigned flags; int mixer_reg[15]; //reg 14 is actually a command: read,write,reset int val; int i; val = *(int *) arg; if (verify_area(VERIFY_READ, (void*)val, sizeof(mixer_reg) == -EFAULT)) return(-EFAULT); memcpy_fromfs(&mixer_reg, (void*)val, sizeof(mixer_reg)); if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) //reset command?? { wavnc_mixer_reset (devc); return(0); } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE)//write command?? { // printk("WaveArtist Mixer: Private write command.\n"); wa_sendcmd(0x32); //Pair1 - word 1 and 5 wa_sendcmd(mixer_reg[0]); wa_sendcmd(mixer_reg[4]); wa_sendcmd(0x32); //Pair2 - word 2 and 6 wa_sendcmd(mixer_reg[1]); wa_sendcmd(mixer_reg[5]); wa_sendcmd(0x32); //Pair3 - word 3 and 7 wa_sendcmd(mixer_reg[2]); wa_sendcmd(mixer_reg[6]); wa_sendcmd(0x32); //Pair4 - word 4 and 8 wa_sendcmd(mixer_reg[3]); wa_sendcmd(mixer_reg[7]); wa_sendcmd(0x32); //Pair5 - word 9 and 10 wa_sendcmd(mixer_reg[8]); wa_sendcmd(mixer_reg[9]); wa_sendcmd(0x0031); //set left and right PCM wa_sendcmd(mixer_reg[0x0A]); wa_sendcmd(mixer_reg[0x0B]); wa_sendcmd(0x0131); //set left and right FM wa_sendcmd(mixer_reg[0x0C]); wa_sendcmd(mixer_reg[0x0D]); return(0); } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ)//read command? { // printk("WaveArtist Mixer: Private read command.\n"); //first read all current values... save_flags(flags); cli(); for (i=0; i<14; i++) { wa_sendcmd((i<<8)+0x30); // get ready for command nn30H while (! (inb(STATR) & CMD_RF)) {}; //wait for response ready... mixer_reg[i] = inw(CMDR); } restore_flags(flags); if (verify_area(VERIFY_WRITE, (void*)val, sizeof(mixer_reg) == -EFAULT)) return(-EFAULT); memcpy_tofs((void*)val, &mixer_reg, sizeof(mixer_reg)); return(0); } else return (-EINVAL); } if (((cmd >> 8) & 0xff) == 'M') { int val; if (_SIOC_DIR (cmd) & _SIOC_WRITE) { val = *(int *) arg; // special case for master volume: if we received this call - switch from // hw volume control to a software volume control, till the hw volume // is modified to signal that user wants to be back in hardware... if ((cmd &0xff) == SOUND_MIXER_VOLUME) /* Master volume */ { if (!devc->soft_volume_flag) //the switching call? { devc->soft_volume_flag = 1;//mark going to soft volume // remember this hw setting to compare devc->hw_volume = levels[SOUND_MIXER_VOLUME]&0xFF; // printk("waMixer: soft vol, curr vol = %d.\n",devc->hw_volume); } // if already in soft volume, just set the volume } return (*(int *) arg = wa_mixer_set (devc, cmd & 0xff, val)); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: if (devc->handset_state & VNC_INTERNAL_MIC) // handset not plugged in? return (*(int *) arg = devc->recmask|SOUND_MASK_SPEAKER); else return (*(int *) arg = devc->recmask); break; case SOUND_MIXER_DEVMASK: return (*(int *) arg = devc->supported_devices); break; case SOUND_MIXER_STEREODEVS: return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_LINE1)); break; case SOUND_MIXER_RECMASK: return (*(int *) arg = devc->rec_devices); break; case SOUND_MIXER_CAPS: return (*(int *) arg = SOUND_CAP_EXCL_INPUT); break; default: return (*(int *) arg = levels[cmd & 0xff]); } } else { DDB(printk("mixer: invalid IOCTL %X.\n",cmd)); return -EINVAL; } } static int wavnc_set_speed (int dev, int arg) { // wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; if (arg <= 0) return portc->speed; if (arg < 5000) arg = 5000; if (arg > 44100) arg = 44100; portc->speed = arg; return portc->speed; } static short wavnc_set_channels (int dev, short arg) { wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; if (arg != 1 && arg != 2) return portc->channels; portc->channels = arg; return arg; } static unsigned int wavnc_set_bits (int dev, unsigned int arg) { // wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; if (arg == 0) return portc->audio_format; if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8)) arg = AFMT_U8; portc->audio_format = arg; return arg; } static struct audio_driver wavnc_audio_driver = { wavnc_open, wavnc_close, wavnc_output_block, wavnc_start_input, wavnc_ioctl, wavnc_prepare_for_input, wavnc_prepare_for_output, wavnc_halt, NULL, NULL, wavnc_halt_input, wavnc_halt_output, wavnc_trigger, wavnc_set_speed, wavnc_set_bits, wavnc_set_channels }; static struct mixer_operations wavnc_mixer_operations = { "WaveArtist", "WaveArtist NetWinder", wavnc_mixer_ioctl }; static int wavnc_open (int dev, int mode) { wavnc_info *devc = NULL; wavnc_port_info *portc; unsigned long flags; if (dev < 0 || dev >= num_audiodevs) return -ENXIO; devc = (wavnc_info *) audio_devs[dev]->devc; portc = (wavnc_port_info *) audio_devs[dev]->portc; save_flags (flags); cli (); if (portc->open_mode || (devc->open_mode & mode)) { restore_flags (flags); return -EBUSY; } devc->dual_dma = 0; if (audio_devs[dev]->flags & DMA_DUPLEX) { devc->dual_dma = 1; } devc->intr_active = 0; devc->audio_mode = 0; devc->triggerflag = 0; devc->open_mode |= mode; portc->open_mode = mode; wavnc_trigger (dev, 0); if (mode & OPEN_READ) devc->record_dev = dev; if (mode & OPEN_WRITE) devc->playback_dev = dev; restore_flags (flags); /* * Mute output until the playback really starts. This decreases clicking (hope so). */ wa_mute (devc,1); return 0; } static void wavnc_close (int dev) { unsigned long flags; wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; // DEB (printk ("wavnc_close(void)\n")); save_flags (flags); cli (); devc->intr_active = 0; wavnc_halt (dev); devc->audio_mode = 0; devc->open_mode &= ~portc->open_mode; portc->open_mode = 0; wa_mute (devc,1); restore_flags (flags); } static int wavnc_ioctl (int dev, unsigned int cmd, caddr_t arg) { return -EINVAL; } static void wavnc_output_block (int dev, unsigned long buf, int count, int intrflag) { unsigned long flags, cnt; wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; DEB1(printk("wavnc: output block, count = %X...\n",count)); cnt = count; if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; if (portc->channels > 1) cnt >>= 1; cnt--; if (devc->audio_mode & PCM_ENABLE_OUTPUT && audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->audio_mode |= PCM_ENABLE_OUTPUT; devc->intr_active = 1; return; /* * Auto DMA mode on. No need to react */ } save_flags (flags); cli (); wa_writecmd(0x24,cnt); devc->xfer_count = cnt; devc->audio_mode |= PCM_ENABLE_OUTPUT; devc->intr_active = 1; restore_flags (flags); } static void wavnc_start_input (int dev, unsigned long buf, int count, int intrflag) { unsigned long flags, cnt; wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; DEB1(printk("wavnc: start input, buf=0x%X, count=0x%X...\n",buf,count)); cnt = count; if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; if (portc->channels > 1) cnt >>= 1; cnt--; if (devc->audio_mode & PCM_ENABLE_INPUT && audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->audio_mode |= PCM_ENABLE_INPUT; devc->intr_active = 1; return; /* * Auto DMA mode on. No need to react */ } save_flags (flags); cli (); wa_writecmd(0x14,cnt); //set sample count wa_mute (devc,0); devc->xfer_count = cnt; devc->audio_mode |= PCM_ENABLE_INPUT; devc->intr_active = 1; restore_flags (flags); } static int wavnc_prepare_for_output (int dev, int bsize, int bcount) { unsigned int tmp, wa_speed, wa_bits; unsigned long flags; wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; //DEB1(printk("wavnc: prepare for output...\n")); //program the speed, channels, bits if (portc->speed == 8000) wa_speed = 0x2E71; else if (portc->speed == 11025) wa_speed = 0x4000; else if (portc->speed == 22050) wa_speed = 0x8000; else if (portc->speed == 44100) wa_speed = 0x0; else { tmp = portc->speed * 0x10000; wa_speed = tmp / 44100; wa_speed &= 0xFFFF; } save_flags (flags); cli (); tmp = wa_writecmd(0x22, wa_speed); //write cmd SetSampleSpeedTimeConstant if (tmp != 1) { //lets retry... tmp = wa_writecmd(0x22, wa_speed); if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the playback speed to %dHz.\n", portc->speed); } tmp = wa_writecmd(0x21,portc->channels); if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the playback MONO/STEREO to 0x%X...\n", portc->channels); if (portc->audio_format == AFMT_S16_LE) wa_bits = 1; else if (portc->audio_format == AFMT_S8) wa_bits = 0; else wa_bits = 2; //default AFMT_U8 tmp = wa_writecmd(0x20,wa_bits); if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the playback format to 0x%X...\n", portc->audio_format); restore_flags (flags); devc->xfer_count = 0; wavnc_halt_output (dev); return 0; } static int wavnc_prepare_for_input (int dev, int bsize, int bcount) { unsigned int tmp, wa_speed, wa_bits; unsigned long flags; wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; if (devc->audio_mode) return 0; //program the speed, channels, bits if (portc->speed == 8000) wa_speed = 0x2E71; else if (portc->speed == 11025) wa_speed = 0x4000; else if (portc->speed == 22050) wa_speed = 0x8000; else if (portc->speed == 44100) wa_speed = 0x0; else { tmp = portc->speed * 0x10000; //non-standard - calculate wa_speed = tmp / 44100; wa_speed &= 0xFFFF; } if (portc->audio_format == AFMT_S16_LE) wa_bits = 1; else if (portc->audio_format == AFMT_S8) wa_bits = 0; else wa_bits = 2; //default AFMT_U8 save_flags (flags); cli (); tmp = wa_writecmd(0x10,wa_bits); if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the record format to 0x%X...\n", portc->audio_format); tmp = wa_writecmd(0x11,portc->channels); if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the record MONO/STEREO to 0x%X...\n", portc->channels); tmp = wa_writecmd(0x12, wa_speed); //write cmd SetSampleSpeedTimeConstant if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the record speed to %dHz.\n", portc->speed); tmp = wa_writecmd(0x13,1); if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the record data path to 0x%X...\n", 1); tmp = wa_writecmd(0x10,wa_bits); if (tmp != 1) printk(KERN_DEBUG "WaveArtist: error setting the record format to 0x%X...\n", portc->audio_format); restore_flags (flags); devc->xfer_count = 0; wavnc_halt_input (dev); //DEB(printk("WA CTLR reg: 0x%02X.\n",inb(CTLR))); //DEB(printk("WA STAT reg: 0x%02X.\n",inb(STATR))); //DEB(printk("WA IRQS reg: 0x%02X.\n",inb(IRQSTAT))); return 0; } static void wavnc_halt (int dev) { wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; if (portc->open_mode & OPEN_WRITE) wavnc_halt_output (dev); if (portc->open_mode & OPEN_READ) wavnc_halt_input (dev); devc->audio_mode = 0; devc->triggerflag = 0; } static void wavnc_halt_input (int dev) { wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; unsigned long flags; save_flags (flags); cli (); wa_mute (devc,1); disable_dma (audio_devs[dev]->dmap_in->dma); wa_sendcmd(0x17); /* Stop capture */ enable_dma (audio_devs[dev]->dmap_in->dma); devc->triggerflag &= ~0x02; devc->audio_mode &= ~PCM_ENABLE_INPUT; if (inb(STATR) & IRQ_REQ) /* Clear interrupt */ { int temp; temp = inb(CTLR); //by toggling the IRQ_ACK bit in CTRL outb(temp|IRQ_ACK,CTLR); outb(temp&(~IRQ_ACK),CTLR); } // devc->audio_mode &= ~PCM_ENABLE_INPUT; restore_flags (flags); } static void wavnc_halt_output (int dev) { wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; unsigned long flags; save_flags (flags); cli (); wa_mute (devc,1); disable_dma (audio_devs[dev]->dmap_out->dma); wa_sendcmd(0x27); enable_dma (audio_devs[dev]->dmap_out->dma); devc->triggerflag &= ~0x01; devc->audio_mode &= ~PCM_ENABLE_OUTPUT; if (inb(STATR) & IRQ_REQ) /* Clear interrupt */ { int temp; temp = inb(CTLR); //by toggling the IRQ_ACK bit in CTRL outb(temp|IRQ_ACK,CTLR); outb(temp&(~IRQ_ACK),CTLR); } // devc->audio_mode &= ~PCM_ENABLE_OUTPUT; restore_flags (flags); } static void wavnc_trigger (int dev, int state) { wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; unsigned long flags; DEB1(printk("wavnc: audio trigger, state=%X...\n",state)); save_flags (flags); cli (); state &= devc->audio_mode; if (portc->open_mode & OPEN_READ) { if (state & PCM_ENABLE_INPUT) { if(!(devc->triggerflag & 0x02)) //is input already triggered? { devc->triggerflag |= 0x02; wa_sendcmd(0x15); //enable ADC Data Transfer to PC } } } if (portc->open_mode & OPEN_WRITE) { if (state & PCM_ENABLE_OUTPUT) { if(!(devc->triggerflag & 0x01)) //is output already triggered? { devc->triggerflag |= 0x01; wa_sendcmd(0x25); } } } wa_mute(devc,0); restore_flags (flags); } static void wavnc_init_hw (wavnc_info * devc) { /* * Initial values for the indirect registers of WaveARtist. */ // wa_mute (devc,1); /* Initialize some variables */ wa_mute (devc,0); /* Leave it unmuted now */ devc->audio_flags |= DMA_DUPLEX; wavnc_mixer_reset (devc); } int wavnc_detect (int io_base, int *ad_flags, int *osp) { wavnc_info *devc = &adev_info; if (nr_wavnc_devs >= MAX_AUDIO_DEV) { printk (KERN_DEBUG "WaveArtist: Too many audio devices\n"); return 0; } devc->base = io_base; devc->irq_ok = 0; devc->irq = 0; devc->open_mode = 0; devc->chip_name = devc->name = "RWA010"; devc->debug_flag = 0; devc->wa_osp = osp; return 1; } void wavnc_init (char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *wa_osp) { /* * NOTE! If irq < 0, there is another driver which has allocated the IRQ * so that this driver doesn't need to allocate/deallocate it. * The actually used IRQ is ABS(irq). */ int my_dev; char dev_name[100]; unsigned int temp,temp1,temp2; wavnc_info *devc = &adev_info; wavnc_port_info *portc = NULL; devc->irq = (irq > 0) ? irq : 0; devc->open_mode = 0; devc->dma1 = dma_playback; devc->dma2 = dma_capture; devc->audio_flags = DMA_AUTOMODE; devc->playback_dev = devc->record_dev = 0; if (name != NULL) devc->name = name; //first reset the WA chip by toggling the RESET bit in CTRL temp2 = 0x100000; outb(RESET,CTLR); temp1 = jiffies + 2; //for at least 10 ms while ((temp1 > jiffies) && temp2) { --temp2; } outb(0x00,CTLR); //toggle it back temp2 = 0x00001000; temp1 = jiffies + 50; //wait up-to .5 sec temp = 0; while ((temp1 > jiffies) && (--temp2) && (temp != 0x55AA)) { temp = jiffies+2; //wait couple miliseconds while (temp != jiffies) {}; temp = 0; if(inb(STATR) & CMD_RF) { temp = inw(CMDR); #if 0 //as the result of reset - we want to see 55AA if (temp != 0x55AA) printk(KERN_DEBUG "WaveArtist reset error! (0x%04X).\n",temp); #endif } } // if (temp != 0x55AA) // printk(KERN_DEBUG "WaveArtist error: timeout on reset! (0x%04X).\n",temp); wa_sendcmd(00); tenmicrosec(wa_osp); // while(!(inb(STATR) & CMD_RF)){};//wait for response temp = inw(CMDR); tenmicrosec(wa_osp); tenmicrosec(wa_osp); // while(!(inb(STATR) & CMD_RF)){};//wait for response inw(CMDR); // discard second word == 0 printk("Sound V.%s: WaveArtist, rev.%c%c...\n", VERSION, temp>>8, temp&0xFF); if (name != NULL && name[0] != 0) { if (!temp) sprintf (dev_name,"%s (%s)", name, devc->chip_name); else sprintf (dev_name,"%s (%s rev.%c%c)", name, devc->chip_name, temp>>8, temp&0xFF); } else sprintf (dev_name, "Generic audio codec (%s)", devc->chip_name); request_region (devc->base, 0x0F, devc->name); conf_printf2 (dev_name, devc->base, devc->irq, dma_playback, dma_capture); if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1) devc->audio_flags &= ~DMA_DUPLEX; else devc->audio_flags |= DMA_DUPLEX; if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION, dev_name, &wavnc_audio_driver, sizeof (struct audio_driver), devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8, devc, dma_playback, dma_capture)) < 0) { return; } portc = (wavnc_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (wavnc_port_info))); sound_mem_sizes[sound_nblocks] = sizeof (wavnc_port_info); if (sound_nblocks < 1024) sound_nblocks++;; audio_devs[my_dev]->portc = portc; memset ((char *) portc, 0, sizeof (*portc)); nr_wavnc_devs++; wavnc_init_hw (devc); if (inb(STATR) & IRQ_REQ) /* Clear interrupt */ { int temp; temp = inb(CTLR); //by toggling the IRQ_ACK bit in CTRL outb(temp|IRQ_ACK,CTLR); outb(temp&(~IRQ_ACK),CTLR); } if (snd_set_irq_handler (devc->irq, waintr, devc->name, NULL) < 0) { printk (KERN_DEBUG "WaveArtist: IRQ in use\n"); } devc->irq_ok = 1; /* Couldn't test. assume it's OK */ //enable WaveArtist DMA interrupts.. outb( (inb(CTLR)|(DMA1_IE | DMA0_IE)), CTLR); init_timer(&wa_timer); wa_timer.function = wa_slider; wa_timer.expires = jiffies + WA_TIMER_PERIOD; add_timer(&wa_timer); if (!share_dma) { if (sound_alloc_dma (dma_playback, devc->name)) printk (KERN_DEBUG "WaveArtist: Can't allocate DMA%d\n", dma_playback); if (dma_capture != dma_playback) if (sound_alloc_dma (dma_capture, devc->name)) printk (KERN_DEBUG "WaveArtist: Can't allocate DMA%d\n", dma_capture); } if (sound_install_mixer (MIXER_DRIVER_VERSION, dev_name, &wavnc_mixer_operations, sizeof (struct mixer_operations), devc) >= 0) { audio_devs[my_dev]->mixer_dev = num_mixers - 1; } } void wavnc_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma) { int dev = 0; wavnc_info *devc = NULL; devc = &adev_info; release_region (devc->base, 0x0F); if (!share_dma) { //disable WaveArtist DMA interrupts.. outb((inb(CTLR) & (~(DMA1_IE | DMA0_IE))),CTLR); if (irq > 0) snd_release_irq (devc->irq); sound_free_dma (audio_devs[dev]->dmap_out->dma); if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma) sound_free_dma (audio_devs[dev]->dmap_in->dma); } del_timer(&wa_timer); } void waintr (int irq, void *dev_id, struct pt_regs *dummy) { int status; int irqstatus; int temp; wavnc_info *devc = &adev_info; irqstatus = inb(IRQSTAT); status = inb(STATR); //DEB(printk("wavnc: interrupt: status=0x%02X, irqstatus=0x%02X...\n",status,irqstatus)); if (status & IRQ_REQ) /* Clear interrupt */ { temp = inb(CTLR); //by toggling the IRQ_ACK bit in CTRL outb(temp|IRQ_ACK,CTLR); outb(temp&(~IRQ_ACK),CTLR); } else { printk(KERN_DEBUG "WaveArtist: unexpected audio interrupt!\n"); } if (irqstatus & 0x01) //WA IRQ { #ifdef CONFIG_AUDIO /* * PCM buffer done */ #if 0 /* * Halt the PCM first. Otherwise we don't have time to start a new * block before the PCM chip proceeds to the next sample */ if (!(devc->flags & DMA_AUTOMODE)) { wa_sendcmd(0x27); } #endif temp = 1; if ((status & DMA0) && (devc->audio_mode & PCM_DAC)) { DMAbuf_outputintr (devc->playback_dev, 1); temp = 0; } if ((status & DMA1) && (devc->audio_mode & PCM_ADC)) { DMAbuf_inputintr (devc->record_dev); temp = 0; } if (temp) //default: { printk (KERN_DEBUG "WaveArtist: Unknown interrupt\n"); } #endif } if (irqstatus & 0x2) //SB IRQ { // We do not use SB mode natively... printk(KERN_DEBUG "WaveArtist: Unexpected SB interrupt...\n"); //#ifdef CONFIG_MIDI // wa_midi_interrupt (); //#endif } } int probe_wa_sound (struct address_info *hw_config) { DDB (printk ("Entered probe_wa_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); if (check_region (hw_config->io_base, 0x0F)) { printk (KERN_DEBUG "WaveArtist: I/O port conflict\n"); return 0; } // if (hw_config->irq > 31 || hw_config->irq < 16 ) if (hw_config->irq !=28 && hw_config->irq != 12 ) { printk (KERN_DEBUG "WaveArtist: Bad IRQ %d\n", hw_config->irq); return 0; } if (hw_config->dma != 3) { printk (KERN_DEBUG "WaveArtist: Bad DMA %d\n", hw_config->dma); return 0; } return wavnc_detect (hw_config->io_base, NULL, hw_config->osp); } void attach_wa_sound (struct address_info *hw_config) { int dma = hw_config->dma; int dma2 = hw_config->dma2; wavnc_init ("WaveArtist NetWinder", hw_config->io_base, hw_config->irq, dma, dma2, 0, hw_config->osp); } void unload_wa_sound (struct address_info *hw_config) { wavnc_unload (hw_config->io_base, hw_config->irq, hw_config->dma, hw_config->dma2, 0); } #endif #if 0 //Source for a wamixer test program capable to change all mixer registers // in real time... //Woody, April 1998 #include #include #include #include #include #include #define MIXER_PRIVATE3_RESET 0x53570000 #define MIXER_PRIVATE3_READ 0x53570001 #define MIXER_PRIVATE3_WRITE 0x53570002 int mixer_fd=-1; unsigned supported; int source; int mixer_regs[15]; main(int argc, char *argv[]) { unsigned int r, d; mixer_fd=open("/dev/mixer",O_RDWR); if(mixer_fd<0){ perror("wamixer: Mixer not found!"); exit(-1); } mixer_regs[0x0E] = MIXER_PRIVATE3_READ; source = &mixer_regs; source = ioctl(mixer_fd,SOUND_MIXER_PRIVATE3,&source); if (source < 0) printf("Mixer Private read returned error 0x%X!\n", source); if (argc == 1) { printf("0001 0002 0003 0004 0005 0006 0007 0008 0009 0010 0011 0012 0013 0014\n"); for (r=0; r<14; r++) printf("%04X ", mixer_regs[r]); printf("\n"); } else if (argc%2 == 1) { while (argc > 1) { r = atoi(argv[1]); sscanf(argv[2], "%i", &d); printf("Register %d <- %04X\n", r, d); mixer_regs[r-1] = d; argc -= 2; argv += 2; } mixer_regs[0x0E] = MIXER_PRIVATE3_WRITE; source = &mixer_regs; source = ioctl(mixer_fd,SOUND_MIXER_PRIVATE3,&source); if (source < 0) printf("Mixer Private write returned error 0x%X!\n", source); } else { printf("usage: wamixer - dump registers\n"); printf(" wamixer reg val [reg val ...] - program WA mixer register\n"); printf(" reg is decimal, value is hex (0x..) or decimal\n"); printf(" For example:-\n"); printf(" wamixer 9 0x982D 4 0x4C08 8 0x8C08\n"); exit(1); } close(mixer_fd); } #endif