Re: Trouble with sound/mips/au1x00.c AC97 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux