1d0ce9946SClemens Ladisch /* 2d0ce9946SClemens Ladisch * C-Media CMI8788 driver - helper functions 3d0ce9946SClemens Ladisch * 4d0ce9946SClemens Ladisch * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 5d0ce9946SClemens Ladisch * 6d0ce9946SClemens Ladisch * 7d0ce9946SClemens Ladisch * This driver is free software; you can redistribute it and/or modify 8d0ce9946SClemens Ladisch * it under the terms of the GNU General Public License, version 2. 9d0ce9946SClemens Ladisch * 10d0ce9946SClemens Ladisch * This driver is distributed in the hope that it will be useful, 11d0ce9946SClemens Ladisch * but WITHOUT ANY WARRANTY; without even the implied warranty of 12d0ce9946SClemens Ladisch * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13d0ce9946SClemens Ladisch * GNU General Public License for more details. 14d0ce9946SClemens Ladisch * 15d0ce9946SClemens Ladisch * You should have received a copy of the GNU General Public License 16d0ce9946SClemens Ladisch * along with this driver; if not, write to the Free Software 17d0ce9946SClemens Ladisch * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18d0ce9946SClemens Ladisch */ 19d0ce9946SClemens Ladisch 20d0ce9946SClemens Ladisch #include <linux/delay.h> 21d0ce9946SClemens Ladisch #include <linux/sched.h> 22d81a6d71SPaul Gortmaker #include <linux/export.h> 23d0ce9946SClemens Ladisch #include <sound/core.h> 24397b1dccSClemens Ladisch #include <sound/mpu401.h> 25d0ce9946SClemens Ladisch #include <asm/io.h> 26d0ce9946SClemens Ladisch #include "oxygen.h" 27d0ce9946SClemens Ladisch 28d0ce9946SClemens Ladisch u8 oxygen_read8(struct oxygen *chip, unsigned int reg) 29d0ce9946SClemens Ladisch { 30d0ce9946SClemens Ladisch return inb(chip->addr + reg); 31d0ce9946SClemens Ladisch } 32d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_read8); 33d0ce9946SClemens Ladisch 34d0ce9946SClemens Ladisch u16 oxygen_read16(struct oxygen *chip, unsigned int reg) 35d0ce9946SClemens Ladisch { 36d0ce9946SClemens Ladisch return inw(chip->addr + reg); 37d0ce9946SClemens Ladisch } 38d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_read16); 39d0ce9946SClemens Ladisch 40d0ce9946SClemens Ladisch u32 oxygen_read32(struct oxygen *chip, unsigned int reg) 41d0ce9946SClemens Ladisch { 42d0ce9946SClemens Ladisch return inl(chip->addr + reg); 43d0ce9946SClemens Ladisch } 44d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_read32); 45d0ce9946SClemens Ladisch 46d0ce9946SClemens Ladisch void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value) 47d0ce9946SClemens Ladisch { 48d0ce9946SClemens Ladisch outb(value, chip->addr + reg); 49e58aee95SClemens Ladisch chip->saved_registers._8[reg] = value; 50d0ce9946SClemens Ladisch } 51d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write8); 52d0ce9946SClemens Ladisch 53d0ce9946SClemens Ladisch void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value) 54d0ce9946SClemens Ladisch { 55d0ce9946SClemens Ladisch outw(value, chip->addr + reg); 56e58aee95SClemens Ladisch chip->saved_registers._16[reg / 2] = cpu_to_le16(value); 57d0ce9946SClemens Ladisch } 58d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write16); 59d0ce9946SClemens Ladisch 60d0ce9946SClemens Ladisch void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value) 61d0ce9946SClemens Ladisch { 62d0ce9946SClemens Ladisch outl(value, chip->addr + reg); 63e58aee95SClemens Ladisch chip->saved_registers._32[reg / 4] = cpu_to_le32(value); 64d0ce9946SClemens Ladisch } 65d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write32); 66d0ce9946SClemens Ladisch 67d0ce9946SClemens Ladisch void oxygen_write8_masked(struct oxygen *chip, unsigned int reg, 68d0ce9946SClemens Ladisch u8 value, u8 mask) 69d0ce9946SClemens Ladisch { 70d0ce9946SClemens Ladisch u8 tmp = inb(chip->addr + reg); 71e58aee95SClemens Ladisch tmp &= ~mask; 72e58aee95SClemens Ladisch tmp |= value & mask; 73e58aee95SClemens Ladisch outb(tmp, chip->addr + reg); 74e58aee95SClemens Ladisch chip->saved_registers._8[reg] = tmp; 75d0ce9946SClemens Ladisch } 76d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write8_masked); 77d0ce9946SClemens Ladisch 78d0ce9946SClemens Ladisch void oxygen_write16_masked(struct oxygen *chip, unsigned int reg, 79d0ce9946SClemens Ladisch u16 value, u16 mask) 80d0ce9946SClemens Ladisch { 81d0ce9946SClemens Ladisch u16 tmp = inw(chip->addr + reg); 82e58aee95SClemens Ladisch tmp &= ~mask; 83e58aee95SClemens Ladisch tmp |= value & mask; 84e58aee95SClemens Ladisch outw(tmp, chip->addr + reg); 85e58aee95SClemens Ladisch chip->saved_registers._16[reg / 2] = cpu_to_le16(tmp); 86d0ce9946SClemens Ladisch } 87d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write16_masked); 88d0ce9946SClemens Ladisch 89d0ce9946SClemens Ladisch void oxygen_write32_masked(struct oxygen *chip, unsigned int reg, 90d0ce9946SClemens Ladisch u32 value, u32 mask) 91d0ce9946SClemens Ladisch { 92d0ce9946SClemens Ladisch u32 tmp = inl(chip->addr + reg); 93e58aee95SClemens Ladisch tmp &= ~mask; 94e58aee95SClemens Ladisch tmp |= value & mask; 95e58aee95SClemens Ladisch outl(tmp, chip->addr + reg); 96e58aee95SClemens Ladisch chip->saved_registers._32[reg / 4] = cpu_to_le32(tmp); 97d0ce9946SClemens Ladisch } 98d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write32_masked); 99d0ce9946SClemens Ladisch 100d0ce9946SClemens Ladisch static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask) 101d0ce9946SClemens Ladisch { 1021e821dd2SClemens Ladisch u8 status = 0; 1031e821dd2SClemens Ladisch 1041e821dd2SClemens Ladisch /* 1051e821dd2SClemens Ladisch * Reading the status register also clears the bits, so we have to save 1061e821dd2SClemens Ladisch * the read bits in status. 1071e821dd2SClemens Ladisch */ 1081e821dd2SClemens Ladisch wait_event_timeout(chip->ac97_waitqueue, 1091e821dd2SClemens Ladisch ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); 1101e821dd2SClemens Ladisch status & mask; }), 1111e821dd2SClemens Ladisch msecs_to_jiffies(1) + 1); 1121e821dd2SClemens Ladisch /* 1131e821dd2SClemens Ladisch * Check even after a timeout because this function should not require 1141e821dd2SClemens Ladisch * the AC'97 interrupt to be enabled. 1151e821dd2SClemens Ladisch */ 1161e821dd2SClemens Ladisch status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); 1171e821dd2SClemens Ladisch return status & mask ? 0 : -EIO; 118d0ce9946SClemens Ladisch } 119d0ce9946SClemens Ladisch 120d0ce9946SClemens Ladisch /* 121d0ce9946SClemens Ladisch * About 10% of AC'97 register reads or writes fail to complete, but even those 122d0ce9946SClemens Ladisch * where the controller indicates completion aren't guaranteed to have actually 123d0ce9946SClemens Ladisch * happened. 124d0ce9946SClemens Ladisch * 125d0ce9946SClemens Ladisch * It's hard to assign blame to either the controller or the codec because both 126d0ce9946SClemens Ladisch * were made by C-Media ... 127d0ce9946SClemens Ladisch */ 128d0ce9946SClemens Ladisch 129d0ce9946SClemens Ladisch void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, 130d0ce9946SClemens Ladisch unsigned int index, u16 data) 131d0ce9946SClemens Ladisch { 132d0ce9946SClemens Ladisch unsigned int count, succeeded; 133d0ce9946SClemens Ladisch u32 reg; 134d0ce9946SClemens Ladisch 135d0ce9946SClemens Ladisch reg = data; 136d0ce9946SClemens Ladisch reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT; 137d0ce9946SClemens Ladisch reg |= OXYGEN_AC97_REG_DIR_WRITE; 138d0ce9946SClemens Ladisch reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; 139d0ce9946SClemens Ladisch succeeded = 0; 140d0ce9946SClemens Ladisch for (count = 5; count > 0; --count) { 141d0ce9946SClemens Ladisch udelay(5); 142d0ce9946SClemens Ladisch oxygen_write32(chip, OXYGEN_AC97_REGS, reg); 143d0ce9946SClemens Ladisch /* require two "completed" writes, just to be sure */ 144c2353a08SClemens Ladisch if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 && 145e58aee95SClemens Ladisch ++succeeded >= 2) { 146e58aee95SClemens Ladisch chip->saved_ac97_registers[codec][index / 2] = data; 147d0ce9946SClemens Ladisch return; 148d0ce9946SClemens Ladisch } 149e58aee95SClemens Ladisch } 150d0ce9946SClemens Ladisch snd_printk(KERN_ERR "AC'97 write timeout\n"); 151d0ce9946SClemens Ladisch } 152d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write_ac97); 153d0ce9946SClemens Ladisch 154d0ce9946SClemens Ladisch u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, 155d0ce9946SClemens Ladisch unsigned int index) 156d0ce9946SClemens Ladisch { 157d0ce9946SClemens Ladisch unsigned int count; 158d0ce9946SClemens Ladisch unsigned int last_read = UINT_MAX; 159d0ce9946SClemens Ladisch u32 reg; 160d0ce9946SClemens Ladisch 161d0ce9946SClemens Ladisch reg = index << OXYGEN_AC97_REG_ADDR_SHIFT; 162d0ce9946SClemens Ladisch reg |= OXYGEN_AC97_REG_DIR_READ; 163d0ce9946SClemens Ladisch reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; 164d0ce9946SClemens Ladisch for (count = 5; count > 0; --count) { 165d0ce9946SClemens Ladisch udelay(5); 166d0ce9946SClemens Ladisch oxygen_write32(chip, OXYGEN_AC97_REGS, reg); 167d0ce9946SClemens Ladisch udelay(10); 168c2353a08SClemens Ladisch if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) { 169d0ce9946SClemens Ladisch u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS); 170d0ce9946SClemens Ladisch /* we require two consecutive reads of the same value */ 171d0ce9946SClemens Ladisch if (value == last_read) 172d0ce9946SClemens Ladisch return value; 173d0ce9946SClemens Ladisch last_read = value; 174d0ce9946SClemens Ladisch /* 175d0ce9946SClemens Ladisch * Invert the register value bits to make sure that two 176d0ce9946SClemens Ladisch * consecutive unsuccessful reads do not return the same 177d0ce9946SClemens Ladisch * value. 178d0ce9946SClemens Ladisch */ 179d0ce9946SClemens Ladisch reg ^= 0xffff; 180d0ce9946SClemens Ladisch } 181d0ce9946SClemens Ladisch } 182d0ce9946SClemens Ladisch snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec); 183d0ce9946SClemens Ladisch return 0; 184d0ce9946SClemens Ladisch } 185d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_read_ac97); 186d0ce9946SClemens Ladisch 187d0ce9946SClemens Ladisch void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, 188d0ce9946SClemens Ladisch unsigned int index, u16 data, u16 mask) 189d0ce9946SClemens Ladisch { 190d0ce9946SClemens Ladisch u16 value = oxygen_read_ac97(chip, codec, index); 191d0ce9946SClemens Ladisch value &= ~mask; 192d0ce9946SClemens Ladisch value |= data & mask; 193d0ce9946SClemens Ladisch oxygen_write_ac97(chip, codec, index, value); 194d0ce9946SClemens Ladisch } 195d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write_ac97_masked); 196d0ce9946SClemens Ladisch 197d0ce9946SClemens Ladisch void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) 198d0ce9946SClemens Ladisch { 199d0ce9946SClemens Ladisch unsigned int count; 200d0ce9946SClemens Ladisch 2015ea310ffSClemens Ladisch /* should not need more than 30.72 us (24 * 1.28 us) */ 202d0ce9946SClemens Ladisch count = 10; 203d0ce9946SClemens Ladisch while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) 204d0ce9946SClemens Ladisch && count > 0) { 2055ea310ffSClemens Ladisch udelay(4); 206d0ce9946SClemens Ladisch --count; 207d0ce9946SClemens Ladisch } 208d0ce9946SClemens Ladisch 209d0ce9946SClemens Ladisch oxygen_write8(chip, OXYGEN_SPI_DATA1, data); 210d0ce9946SClemens Ladisch oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); 211d0ce9946SClemens Ladisch if (control & OXYGEN_SPI_DATA_LENGTH_3) 212d0ce9946SClemens Ladisch oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); 213d0ce9946SClemens Ladisch oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); 214d0ce9946SClemens Ladisch } 215d0ce9946SClemens Ladisch EXPORT_SYMBOL(oxygen_write_spi); 21610e6d5f9SClemens Ladisch 21710e6d5f9SClemens Ladisch void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data) 21810e6d5f9SClemens Ladisch { 21910e6d5f9SClemens Ladisch /* should not need more than about 300 us */ 220f1bc07afSClemens Ladisch msleep(1); 22110e6d5f9SClemens Ladisch 22210e6d5f9SClemens Ladisch oxygen_write8(chip, OXYGEN_2WIRE_MAP, map); 22310e6d5f9SClemens Ladisch oxygen_write8(chip, OXYGEN_2WIRE_DATA, data); 22410e6d5f9SClemens Ladisch oxygen_write8(chip, OXYGEN_2WIRE_CONTROL, 22510e6d5f9SClemens Ladisch device | OXYGEN_2WIRE_DIR_WRITE); 22610e6d5f9SClemens Ladisch } 22710e6d5f9SClemens Ladisch EXPORT_SYMBOL(oxygen_write_i2c); 228397b1dccSClemens Ladisch 229397b1dccSClemens Ladisch static void _write_uart(struct oxygen *chip, unsigned int port, u8 data) 230397b1dccSClemens Ladisch { 231397b1dccSClemens Ladisch if (oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_TX_FULL) 232397b1dccSClemens Ladisch msleep(1); 233397b1dccSClemens Ladisch oxygen_write8(chip, OXYGEN_MPU401 + port, data); 234397b1dccSClemens Ladisch } 235397b1dccSClemens Ladisch 236397b1dccSClemens Ladisch void oxygen_reset_uart(struct oxygen *chip) 237397b1dccSClemens Ladisch { 238397b1dccSClemens Ladisch _write_uart(chip, 1, MPU401_RESET); 239cdad5b8dSClemens Ladisch msleep(1); /* wait for ACK */ 240397b1dccSClemens Ladisch _write_uart(chip, 1, MPU401_ENTER_UART); 241397b1dccSClemens Ladisch } 242397b1dccSClemens Ladisch EXPORT_SYMBOL(oxygen_reset_uart); 243397b1dccSClemens Ladisch 244397b1dccSClemens Ladisch void oxygen_write_uart(struct oxygen *chip, u8 data) 245397b1dccSClemens Ladisch { 246397b1dccSClemens Ladisch _write_uart(chip, 0, data); 247397b1dccSClemens Ladisch } 248397b1dccSClemens Ladisch EXPORT_SYMBOL(oxygen_write_uart); 24930459d7bSClemens Ladisch 25030459d7bSClemens Ladisch u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index) 25130459d7bSClemens Ladisch { 25230459d7bSClemens Ladisch unsigned int timeout; 25330459d7bSClemens Ladisch 25430459d7bSClemens Ladisch oxygen_write8(chip, OXYGEN_EEPROM_CONTROL, 25530459d7bSClemens Ladisch index | OXYGEN_EEPROM_DIR_READ); 25630459d7bSClemens Ladisch for (timeout = 0; timeout < 100; ++timeout) { 25730459d7bSClemens Ladisch udelay(1); 25830459d7bSClemens Ladisch if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS) 25930459d7bSClemens Ladisch & OXYGEN_EEPROM_BUSY)) 26030459d7bSClemens Ladisch break; 26130459d7bSClemens Ladisch } 26230459d7bSClemens Ladisch return oxygen_read16(chip, OXYGEN_EEPROM_DATA); 26330459d7bSClemens Ladisch } 2641275d6f6SClemens Ladisch 2651275d6f6SClemens Ladisch void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value) 2661275d6f6SClemens Ladisch { 2671275d6f6SClemens Ladisch unsigned int timeout; 2681275d6f6SClemens Ladisch 2691275d6f6SClemens Ladisch oxygen_write16(chip, OXYGEN_EEPROM_DATA, value); 2701275d6f6SClemens Ladisch oxygen_write8(chip, OXYGEN_EEPROM_CONTROL, 2711275d6f6SClemens Ladisch index | OXYGEN_EEPROM_DIR_WRITE); 2721275d6f6SClemens Ladisch for (timeout = 0; timeout < 10; ++timeout) { 2731275d6f6SClemens Ladisch msleep(1); 2741275d6f6SClemens Ladisch if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS) 2751275d6f6SClemens Ladisch & OXYGEN_EEPROM_BUSY)) 2761275d6f6SClemens Ladisch return; 2771275d6f6SClemens Ladisch } 2781275d6f6SClemens Ladisch snd_printk(KERN_ERR "EEPROM write timeout\n"); 2791275d6f6SClemens Ladisch } 280