From d0ce9946c52e7bdf95afb09553775cf28b752254 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 23 Dec 2007 19:50:57 +0100 Subject: [ALSA] add CMI8788 driver Add the snd-oxygen driver for the C-Media CMI8788 (Oxygen) chip, used on the Asound A-8788, AuzenTech X-Meridian, Bgears b-Enspirer, Club3D Theatron DTS, HT-Omega Claro, Razer Barracuda AC-1, Sondigo Inferno, and TempoTec HIFIER sound cards. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/oxygen/oxygen_io.c | 194 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 sound/pci/oxygen/oxygen_io.c (limited to 'sound/pci/oxygen/oxygen_io.c') diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c new file mode 100644 index 000000000000..5f4feeaf8b3a --- /dev/null +++ b/sound/pci/oxygen/oxygen_io.c @@ -0,0 +1,194 @@ +/* + * C-Media CMI8788 driver - helper functions + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "oxygen.h" + +u8 oxygen_read8(struct oxygen *chip, unsigned int reg) +{ + return inb(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read8); + +u16 oxygen_read16(struct oxygen *chip, unsigned int reg) +{ + return inw(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read16); + +u32 oxygen_read32(struct oxygen *chip, unsigned int reg) +{ + return inl(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read32); + +void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value) +{ + outb(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write8); + +void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value) +{ + outw(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write16); + +void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value) +{ + outl(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write32); + +void oxygen_write8_masked(struct oxygen *chip, unsigned int reg, + u8 value, u8 mask) +{ + u8 tmp = inb(chip->addr + reg); + outb((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write8_masked); + +void oxygen_write16_masked(struct oxygen *chip, unsigned int reg, + u16 value, u16 mask) +{ + u16 tmp = inw(chip->addr + reg); + outw((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write16_masked); + +void oxygen_write32_masked(struct oxygen *chip, unsigned int reg, + u32 value, u32 mask) +{ + u32 tmp = inl(chip->addr + reg); + outl((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write32_masked); + +static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1); + do { + udelay(5); + cond_resched(); + if (oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS) & mask) + return 0; + } while (time_after_eq(timeout, jiffies)); + return -EIO; +} + +/* + * About 10% of AC'97 register reads or writes fail to complete, but even those + * where the controller indicates completion aren't guaranteed to have actually + * happened. + * + * It's hard to assign blame to either the controller or the codec because both + * were made by C-Media ... + */ + +void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data) +{ + unsigned int count, succeeded; + u32 reg; + + reg = data; + reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT; + reg |= OXYGEN_AC97_REG_DIR_WRITE; + reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; + succeeded = 0; + for (count = 5; count > 0; --count) { + udelay(5); + oxygen_write32(chip, OXYGEN_AC97_REGS, reg); + /* require two "completed" writes, just to be sure */ + if (oxygen_ac97_wait(chip, OXYGEN_AC97_WRITE_COMPLETE) >= 0 && + ++succeeded >= 2) + return; + } + snd_printk(KERN_ERR "AC'97 write timeout\n"); +} +EXPORT_SYMBOL(oxygen_write_ac97); + +u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index) +{ + unsigned int count; + unsigned int last_read = UINT_MAX; + u32 reg; + + reg = index << OXYGEN_AC97_REG_ADDR_SHIFT; + reg |= OXYGEN_AC97_REG_DIR_READ; + reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; + for (count = 5; count > 0; --count) { + udelay(5); + oxygen_write32(chip, OXYGEN_AC97_REGS, reg); + udelay(10); + if (oxygen_ac97_wait(chip, OXYGEN_AC97_READ_COMPLETE) >= 0) { + u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS); + /* we require two consecutive reads of the same value */ + if (value == last_read) + return value; + last_read = value; + /* + * Invert the register value bits to make sure that two + * consecutive unsuccessful reads do not return the same + * value. + */ + reg ^= 0xffff; + } + } + snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec); + return 0; +} +EXPORT_SYMBOL(oxygen_read_ac97); + +void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data, u16 mask) +{ + u16 value = oxygen_read_ac97(chip, codec, index); + value &= ~mask; + value |= data & mask; + oxygen_write_ac97(chip, codec, index, value); +} +EXPORT_SYMBOL(oxygen_write_ac97_masked); + +void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) +{ + unsigned int count; + + /* should not need more than 3.84 us (24 * 160 ns) */ + count = 10; + while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) + && count > 0) { + udelay(1); + --count; + } + + spin_lock_irq(&chip->reg_lock); + oxygen_write8(chip, OXYGEN_SPI_DATA1, data); + oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); + if (control & OXYGEN_SPI_DATA_LENGTH_3) + oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); + oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); + spin_unlock_irq(&chip->reg_lock); +} +EXPORT_SYMBOL(oxygen_write_spi); -- cgit v1.2.2 From 9004acc70e8c49c50c4c7b652f906f1e0ed5709d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 Jan 2008 18:13:27 +0100 Subject: [ALSA] Remove sound/driver.h This header file exists only for some hacks to adapt alsa-driver tree. It's useless for building in the kernel. Let's move a few lines in it to sound/core.h and remove it. With this patch, sound/driver.h isn't removed but has just a single compile warning to include it. This should be really killed in future. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/oxygen/oxygen_io.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/pci/oxygen/oxygen_io.c') diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 5f4feeaf8b3a..616087c552e2 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -17,7 +17,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include -- cgit v1.2.2 From c2353a0826d2b8fe9f5c6a6aca99149e4ee7b196 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 18 Jan 2008 09:17:53 +0100 Subject: [ALSA] oxygen: add register definitions Add more symbols for registers and register fields. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/oxygen/oxygen_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/oxygen/oxygen_io.c') diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 616087c552e2..ebafc65dc345 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -119,7 +119,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, udelay(5); oxygen_write32(chip, OXYGEN_AC97_REGS, reg); /* require two "completed" writes, just to be sure */ - if (oxygen_ac97_wait(chip, OXYGEN_AC97_WRITE_COMPLETE) >= 0 && + if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 && ++succeeded >= 2) return; } @@ -141,7 +141,7 @@ u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, udelay(5); oxygen_write32(chip, OXYGEN_AC97_REGS, reg); udelay(10); - if (oxygen_ac97_wait(chip, OXYGEN_AC97_READ_COMPLETE) >= 0) { + if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) { u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS); /* we require two consecutive reads of the same value */ if (value == last_read) -- cgit v1.2.2 From 3b94253bc9c950d2038a2db4f9c804b50f82001a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 21 Jan 2008 08:51:19 +0100 Subject: [ALSA] oxygen: reduce SPI clock frequency for AK4396/WM8785 According to the datasheets, the SPI clock cycle must be at least 200 ns for the AK4396 and the WM8785, so we cannot use the default 160 ns. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/oxygen/oxygen_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/oxygen/oxygen_io.c') diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index ebafc65dc345..d0cdce041dd8 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -174,7 +174,7 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) { unsigned int count; - /* should not need more than 3.84 us (24 * 160 ns) */ + /* should not need more than 7.68 us (24 * 320 ns) */ count = 10; while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) && count > 0) { -- cgit v1.2.2 From 1e821dd2763c97df1a0a451e553d218cb8886cd7 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Jan 2008 08:34:21 +0100 Subject: [ALSA] oxygen: use AC97 interrupt After an AC97 register read or write, use the AC97 interrupt instead of polling to wait for the access to be completed. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/oxygen/oxygen_io.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'sound/pci/oxygen/oxygen_io.c') diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index d0cdce041dd8..74e23ef9c946 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -85,14 +85,22 @@ EXPORT_SYMBOL(oxygen_write32_masked); static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask) { - unsigned long timeout = jiffies + msecs_to_jiffies(1); - do { - udelay(5); - cond_resched(); - if (oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS) & mask) - return 0; - } while (time_after_eq(timeout, jiffies)); - return -EIO; + u8 status = 0; + + /* + * Reading the status register also clears the bits, so we have to save + * the read bits in status. + */ + wait_event_timeout(chip->ac97_waitqueue, + ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); + status & mask; }), + msecs_to_jiffies(1) + 1); + /* + * Check even after a timeout because this function should not require + * the AC'97 interrupt to be enabled. + */ + status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); + return status & mask ? 0 : -EIO; } /* -- cgit v1.2.2