Hi Jaroslav, Please find attached a patch that fixes some known problems currently part of the au1x00 ALSA audio driver. Signed-off-by: Freddy Spierenburg <freddy@xxxxxxxxxxxxxxx> Below is a small time explanation of why the patch is needed. On Mon, Mar 12, 2007 at 07:04:19PM -0400, Charles Eidsness wrote: > I wonder if the AC'97 Controller has to be up for at least one > frame before issuing the cold reset. Each frame is 20.8us, you > could try setting that delay to 25us instead of 500ms. The 10us delay indeed is not enough. 25us gives way better results. Combining it with... > Sergei on the mailing list had a good suggestion as well. You could try > replacing every udelay with an au_sync_udelay, and each mdelay with an > au_sync_delay. ...Sergei his suggestion, and more importantly... > I think that spin_lock_irqsave stuff might fix the problem that Freddy > has ...replacing spin_lock() by spin_lock_irqsave() gives me good results. Still not perfectly, but way better than it was. Using the Python audio-script I've attached to this email I've loaded and unloaded the driver 4033 times and it failed 6 times on me. Failing means one control is missing or a control features the wrong settings. If this happens the Python script tries to load the driver again and in all of my tests succeeds right away. -- $ cat ~/.signature Freddy Spierenburg <freddy@xxxxxxxxxxxxxxx> http://freddy.snarl.nl/ GnuPG: 0x7941D1E1=C948 5851 26D2 FA5C 39F1 E588 6F17 FD5D 7941 D1E1 $ # Please read http://www.ietf.org/rfc/rfc2015.txt before complain!
diff -Naur linux-2.6.20.orig/sound/mips/au1x00.c linux-2.6.20/sound/mips/au1x00.c --- linux-2.6.20.orig/sound/mips/au1x00.c 2007-02-04 20:22:45.000000000 +0000 +++ linux-2.6.20/sound/mips/au1x00.c 2007-04-16 14:04:39.000000000 +0000 @@ -496,8 +496,9 @@ u32 volatile cmd; u16 volatile data; int i; + unsigned long flags; - spin_lock(&au1000->ac97_lock); + spin_lock_irqsave(&au1000->ac97_lock, flags); /* would rather use the interupt than this polling but it works and I can't get the interupt driven case to work efficiently */ for (i = 0; i < 0x5000; i++) @@ -520,7 +521,7 @@ } data = au1000->ac97_ioport->cmd & 0xffff; - spin_unlock(&au1000->ac97_lock); + spin_unlock_irqrestore(&au1000->ac97_lock, flags); return data; @@ -533,8 +534,9 @@ struct snd_au1000 *au1000 = ac97->private_data; u32 cmd; int i; + unsigned long flags; - spin_lock(&au1000->ac97_lock); + spin_lock_irqsave(&au1000->ac97_lock, flags); /* would rather use the interupt than this polling but it works and I can't get the interupt driven case to work efficiently */ for (i = 0; i < 0x5000; i++) @@ -547,7 +549,7 @@ cmd &= ~AC97C_READ; cmd |= ((u32) val << AC97C_WD_BIT); au1000->ac97_ioport->cmd = cmd; - spin_unlock(&au1000->ac97_lock); + spin_unlock_irqrestore(&au1000->ac97_lock, flags); } static int __devinit @@ -577,15 +579,15 @@ /* Initialise Au1000's AC'97 Control Block */ au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE; - udelay(10); + au_sync_udelay(10); au1000->ac97_ioport->cntrl = AC97C_CE; - udelay(10); + au_sync_udelay(25); /* Initialise External CODEC -- cold reset */ au1000->ac97_ioport->config = AC97C_RESET; - udelay(10); + au_sync_udelay(10); au1000->ac97_ioport->config = 0x0; - mdelay(5); + au_sync_delay(5); /* Initialise AC97 middle-layer */ if ((err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus)) < 0)
#!/usr/bin/python # # Nice script to load/unload the audio kernel driver. It checks as much # controls as possible. It does this by comparing the configured values to the # values restored after loading the driver. It reads the configured values from # /var/lib/alsa/asound.state and tries to load the driver again if any control # does not hold it's configured value or is even missing. # # Usage: audio start|stop # # Freddy Spierenburg <freddy@xxxxxxxxxxxxxxx> Mon, 16 Apr 2007 16:39:45 +0200 import sys, os, re, string, alsaaudio # This is the class definition for the base control type. All derived controls # share this information. class Control: # The class constructor method. def __init__(self, storage): # Get the name for this control. self.name = storage['name'].replace('\'', '') # Initialize the value list. self.value = [] # Does this control has more than one value? if int(storage['comment.count']) > 1: # Yes, run through all the values. for index in range(int(storage['comment.count'])): # Save every value in our value list. self.value.append(storage['value.' + str(index)]) else: # No, just store this one value in the list. self.value.append(storage['value']) # A nice debug information method. def show(self): # Show the name for this control. print self.name, ':' # Run through all the values for this control. for v in self.value: # Show every value. print 'value =', v # This is the class definition for the boolean control type. class BooleanFactory: def Create(self,storage): return Boolean(storage) class Boolean(Control): # The class constructor method. def __init__(self, storage): # Start the constructor for the base class. Control.__init__(self, storage) # The test method to see if the control is initialized correctly. We test to see # if the current boolean value is equal to the one configured. def error(self): # Get the real name for the control. All control names should be stripped from the # named strings, except the one holding a '-' character. control = self.name if control.count('-') == 0: control = control.replace('Playback Switch', '').replace('Switch', '') control = control.strip() try: # Get a reference to the control mixer = alsaaudio.Mixer(control) # Find the mute state for this control. mute = mixer.getmute() # Return true if the current boolean value is not equal to the configured value. return not ((mute[0] == 0 and self.value[0] == 'true') or (mute[0] == 1 and self.value[0] == 'false')) except alsaaudio.ALSAAudioError: # An error probably indicates a missing control. This is bad! return True # This is the class definition for the integer control type. class IntegerFactory: def Create(self,storage): return Integer(storage) class Integer(Control): # The class constructor method. def __init__(self, storage): # Start the constructor for the base class. Control.__init__(self, storage) # An integer type holds a range. Split it into a minimum and # maximum field. field = string.split(re.sub("'", '', storage['comment.range']), '-') # Save the minimum value. self.minimum = int(field[0]) # Save the maximum value. self.maximum = int(field[1]) # A nice debug information method. def show(self): # Show the information for the base class. Control.show(self) # Show the configured minimum and maximum value for the range. print 'minimum =', self.minimum, 'maximum =', self.maximum # The test method to see if the control is initialized correctly. We test to see # if the range value is like expected and the current volume setting is like it # is configured. def error(self): # Get the real name for the control. control = self.name.replace('Playback Volume', '').replace('Volume', '').strip() try: # Get a reference to the control. mixer = alsaaudio.Mixer(control) except alsaaudio.ALSAAudioError: # An error indicates a missing control. This is bad! return True # Find the range for this control. rng = mixer.getrange() # Check if the current range value is like expected. rangeOK = (rng[0] == self.minimum and rng[1] == self.maximum) # Find the current volume value. volume = mixer.getvolume() # Expect this volume value to be good by default. volumeOK = True # Run through the found volume values. for index in range(len(volume)): # We get the percentage volume value from the ALSA layer, but # we need the index value. Calculate the index value from the # percentage. volume[index] = round(volume[index] * self.maximum / 100.0) # Is the current volume value not equal to the configured value? if volume[index] != int(self.value[index]): # Yes, unfortunately there is something wrong. Set our flag! volumeOK = False # And stop checking right away. We're out of luck anyway! break # Return true if the range or volume is not ok! return not (rangeOK and volumeOK) # This is the class definition for the enumerated control type. class EnumeratedFactory: def Create(self,storage): return Enumerated(storage) class Enumerated(Control): # The class constructor method. def __init__(self, storage): # Start the constructor for the base class. Control.__init__(self, storage) # A nice debug information method. def show(self): # Show only the information for the base class. Control.show(self) # The test method to see if the control is initialized correctly. We test to # see if the current enumerated type value for the control is equal to the # configured value. def error(self): try: # Get a reference to the control. mixer = alsaaudio.Mixer(self.name) # Find the enumerated type for this control. enum = mixer.getenum() # Return true if the current enumerated type is not equal to the # configured one. return not (enum[0] == self.value[0].replace('\'', '')) except alsaaudio.ALSAAudioError: # An error probably indicates a missing control. This is bad! return True # We do not yet do much with the IEC958 control type. class IEC958Factory: def Create(self,storage): return IEC958(storage) class IEC958(Control): def __init__(self, storage): Control.__init__(self, storage) # The factories is a dictionary of control types we recognize and handle. factories = { 'BOOLEAN': BooleanFactory(), 'INTEGER': IntegerFactory(), 'ENUMERATED': EnumeratedFactory(), 'IEC958': IEC958Factory() } # The list of all the controls that should be available. If we need to # check them they hold the value true, false if we choose to ignore them. controlsAvailableList = { "'Master Playback Switch'": True, "'Master Playback Volume'": True, "'Headphone Playback Switch'": True, "'Headphone Playback Volume'": True, "'Master Mono Playback Switch'": True, "'Master Mono Playback Volume'": True, "'PC Speaker Playback Switch'": True, "'PC Speaker Playback Volume'": True, "'Phone Playback Switch'": True, "'Phone Playback Volume'": True, "'Mic Playback Switch'": True, "'Mic Playback Volume'": True, "'Mic Boost (+20dB)'": True, "'Line Playback Switch'": True, "'Line Playback Volume'": True, "'CD Playback Switch'": True, "'CD Playback Volume'": True, "'Video Playback Switch'": True, "'Aux Playback Switch'": True, "'PCM Playback Switch'": True, "'PCM Playback Volume'": True, "'Capture Source'": False, "'Capture Switch'": False, "'Capture Volume'": True, "'PCM Out Path & Mute'": True, "'3D Control - Switch'": True, "'Mono Output Select'": True, "'Mic Select'": True, "'3D Control - Center'": True, "'3D Control - Depth'": True, "'IEC958 Playback Con Mask'": False, "'IEC958 Playback Pro Mask'": False, "'IEC958 Playback Default'": False, "'IEC958 Playback Switch'": False, "'IEC958 Playback AC97-SPSA'": False, "'Front Playback Volume'": True, "'Front Playback Switch'": True, "'External Amplifier'": True } # The routine to stop the audio driver. def driverStop(): os.system('/sbin/rmmod snd-au1x00 > /dev/null 2>&1') # The routine to start the audio driver. def driverStart(): # The start of a control block is marked by the next reg-exp. start = re.compile('control') # The end of a control block is marked by the next reg-exp. end = re.compile('\s+}$') # We only add stuff to our storage when we are inside a control block. process = False # Initialize our temporary storage to be empty at the start. storage = {} # Initialize our list of controls to be empty at the start.. controls = [] # Open the ALSA configuration file. file=open('/var/lib/alsa/asound.state', 'r') # Process every line read from the file. for line in file.readlines(): # What kind of line is this? if start.search(line): # It marks the start of a control block. Start processing. process = True # Clear our temporary storage before we begin. storage.clear() elif end.search(line): # It marks the end of a control block. Stop processing. process = False # Is this one of the controls we would like to check? if controlsAvailableList.has_key(storage['name']) and controlsAvailableList[storage['name']]: # Yes! Find out by means of the control type, what kind of object to create. factory = factories[storage['comment.type']] # Add the object type to our list of controls. controls.append(factory.Create(storage)) elif process: # We are in the middle of processing and this line is data we need to # store in our object. First split the line in a name and value pair. The # first space is seen as the separator. field = string.split(string.strip(line), ' ', 1) # Put the name and value pair inside our temporary storage. storage[field[0]]=field[1] # Close the file. file.close() # To see if the driver is loaded is marked by the next reg-exp. driver = re.compile('^snd_au1x00') # By default we expect our driver not to load. loaded = False # Continue to load the driver untill it is loaded. while not loaded: # Run the command to find out which kernel drivers are loaded. stdout = os.popen('/bin/lsmod') # Read the output from the previous command. output = stdout.read() # Is our driver loaded? if driver.search(output): # Yes, mark that we've loaded the driver. loaded = True else: # No, so try to load it ourselves. loaded = not os.system('(/sbin/modprobe snd-au1x00 && /etc/init.d/alsa-utils start) > /dev/null 2>&1') # If we're loaded we are going to check if everything is ok. ok = loaded # Is everything ok? if ok: # Yes, run our command to find out how many control we have got. stdout = os.popen('/usr/bin/amixer controls') # See if we have all the expected controls. Are we missing some? if len(stdout.readlines()) != len(controlsAvailableList): # Yes, mark that we are not ok anymore. ok = False # Is everything still ok? if ok: # Yes, process all the found controls. for control in controls: # Does this control object has a error method? if hasattr(control, 'error'): # Yes, it has. Now run it, to see if an error in the control exist. Does it? if control.error(): # Yes, mark that we are not ok anymore. ok = False # Stop processing, since it does not make sense anymore break # Did anything go wrong and was the driver loaded? if not ok and loaded: # Yes, so mark that we still have not loaded the driver successfully. loaded = False # Stop the driver. driverStop() # A nice usage routine. def usage(): # Write the usage of this program to stdout. print 'Usage: %s {start|stop}' % (sys.argv[0]) # Quit right away. sys.exit(1) # Do we have received enough command line arguments? if len(sys.argv) >= 2: # Yes, what was the first argument received? if sys.argv[1] == 'start': # It was our command to start the driver. Start it! driverStart() elif sys.argv[1] == 'stop': # It was our command to stop the driver. Stop it! driverStop() else: # It was an unknown command. Display our usage message. usage() else: # No, display our usage message. usage() # Exit cleanly. sys.exit(0)
Attachment:
signature.asc
Description: Digital signature