2 * C-Media CMI8788 driver - helper functions
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License, version 2.
10 * This driver is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this driver; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <linux/delay.h>
21 #include <linux/sched.h>
22 #include <sound/core.h>
26 u8
oxygen_read8(struct oxygen
*chip
, unsigned int reg
)
28 return inb(chip
->addr
+ reg
);
30 EXPORT_SYMBOL(oxygen_read8
);
32 u16
oxygen_read16(struct oxygen
*chip
, unsigned int reg
)
34 return inw(chip
->addr
+ reg
);
36 EXPORT_SYMBOL(oxygen_read16
);
38 u32
oxygen_read32(struct oxygen
*chip
, unsigned int reg
)
40 return inl(chip
->addr
+ reg
);
42 EXPORT_SYMBOL(oxygen_read32
);
44 void oxygen_write8(struct oxygen
*chip
, unsigned int reg
, u8 value
)
46 outb(value
, chip
->addr
+ reg
);
47 chip
->saved_registers
._8
[reg
] = value
;
49 EXPORT_SYMBOL(oxygen_write8
);
51 void oxygen_write16(struct oxygen
*chip
, unsigned int reg
, u16 value
)
53 outw(value
, chip
->addr
+ reg
);
54 chip
->saved_registers
._16
[reg
/ 2] = cpu_to_le16(value
);
56 EXPORT_SYMBOL(oxygen_write16
);
58 void oxygen_write32(struct oxygen
*chip
, unsigned int reg
, u32 value
)
60 outl(value
, chip
->addr
+ reg
);
61 chip
->saved_registers
._32
[reg
/ 4] = cpu_to_le32(value
);
63 EXPORT_SYMBOL(oxygen_write32
);
65 void oxygen_write8_masked(struct oxygen
*chip
, unsigned int reg
,
68 u8 tmp
= inb(chip
->addr
+ reg
);
71 outb(tmp
, chip
->addr
+ reg
);
72 chip
->saved_registers
._8
[reg
] = tmp
;
74 EXPORT_SYMBOL(oxygen_write8_masked
);
76 void oxygen_write16_masked(struct oxygen
*chip
, unsigned int reg
,
79 u16 tmp
= inw(chip
->addr
+ reg
);
82 outw(tmp
, chip
->addr
+ reg
);
83 chip
->saved_registers
._16
[reg
/ 2] = cpu_to_le16(tmp
);
85 EXPORT_SYMBOL(oxygen_write16_masked
);
87 void oxygen_write32_masked(struct oxygen
*chip
, unsigned int reg
,
90 u32 tmp
= inl(chip
->addr
+ reg
);
93 outl(tmp
, chip
->addr
+ reg
);
94 chip
->saved_registers
._32
[reg
/ 4] = cpu_to_le32(tmp
);
96 EXPORT_SYMBOL(oxygen_write32_masked
);
98 static int oxygen_ac97_wait(struct oxygen
*chip
, unsigned int mask
)
103 * Reading the status register also clears the bits, so we have to save
104 * the read bits in status.
106 wait_event_timeout(chip
->ac97_waitqueue
,
107 ({ status
|= oxygen_read8(chip
, OXYGEN_AC97_INTERRUPT_STATUS
);
109 msecs_to_jiffies(1) + 1);
111 * Check even after a timeout because this function should not require
112 * the AC'97 interrupt to be enabled.
114 status
|= oxygen_read8(chip
, OXYGEN_AC97_INTERRUPT_STATUS
);
115 return status
& mask
? 0 : -EIO
;
119 * About 10% of AC'97 register reads or writes fail to complete, but even those
120 * where the controller indicates completion aren't guaranteed to have actually
123 * It's hard to assign blame to either the controller or the codec because both
124 * were made by C-Media ...
127 void oxygen_write_ac97(struct oxygen
*chip
, unsigned int codec
,
128 unsigned int index
, u16 data
)
130 unsigned int count
, succeeded
;
134 reg
|= index
<< OXYGEN_AC97_REG_ADDR_SHIFT
;
135 reg
|= OXYGEN_AC97_REG_DIR_WRITE
;
136 reg
|= codec
<< OXYGEN_AC97_REG_CODEC_SHIFT
;
138 for (count
= 5; count
> 0; --count
) {
140 oxygen_write32(chip
, OXYGEN_AC97_REGS
, reg
);
141 /* require two "completed" writes, just to be sure */
142 if (oxygen_ac97_wait(chip
, OXYGEN_AC97_INT_WRITE_DONE
) >= 0 &&
144 chip
->saved_ac97_registers
[codec
][index
/ 2] = data
;
148 snd_printk(KERN_ERR
"AC'97 write timeout\n");
150 EXPORT_SYMBOL(oxygen_write_ac97
);
152 u16
oxygen_read_ac97(struct oxygen
*chip
, unsigned int codec
,
156 unsigned int last_read
= UINT_MAX
;
159 reg
= index
<< OXYGEN_AC97_REG_ADDR_SHIFT
;
160 reg
|= OXYGEN_AC97_REG_DIR_READ
;
161 reg
|= codec
<< OXYGEN_AC97_REG_CODEC_SHIFT
;
162 for (count
= 5; count
> 0; --count
) {
164 oxygen_write32(chip
, OXYGEN_AC97_REGS
, reg
);
166 if (oxygen_ac97_wait(chip
, OXYGEN_AC97_INT_READ_DONE
) >= 0) {
167 u16 value
= oxygen_read16(chip
, OXYGEN_AC97_REGS
);
168 /* we require two consecutive reads of the same value */
169 if (value
== last_read
)
173 * Invert the register value bits to make sure that two
174 * consecutive unsuccessful reads do not return the same
180 snd_printk(KERN_ERR
"AC'97 read timeout on codec %u\n", codec
);
183 EXPORT_SYMBOL(oxygen_read_ac97
);
185 void oxygen_write_ac97_masked(struct oxygen
*chip
, unsigned int codec
,
186 unsigned int index
, u16 data
, u16 mask
)
188 u16 value
= oxygen_read_ac97(chip
, codec
, index
);
190 value
|= data
& mask
;
191 oxygen_write_ac97(chip
, codec
, index
, value
);
193 EXPORT_SYMBOL(oxygen_write_ac97_masked
);
195 void oxygen_write_spi(struct oxygen
*chip
, u8 control
, unsigned int data
)
199 /* should not need more than 7.68 us (24 * 320 ns) */
201 while ((oxygen_read8(chip
, OXYGEN_SPI_CONTROL
) & OXYGEN_SPI_BUSY
)
207 oxygen_write8(chip
, OXYGEN_SPI_DATA1
, data
);
208 oxygen_write8(chip
, OXYGEN_SPI_DATA2
, data
>> 8);
209 if (control
& OXYGEN_SPI_DATA_LENGTH_3
)
210 oxygen_write8(chip
, OXYGEN_SPI_DATA3
, data
>> 16);
211 oxygen_write8(chip
, OXYGEN_SPI_CONTROL
, control
);
213 EXPORT_SYMBOL(oxygen_write_spi
);
215 void oxygen_write_i2c(struct oxygen
*chip
, u8 device
, u8 map
, u8 data
)
217 unsigned long timeout
;
219 /* should not need more than about 300 us */
220 timeout
= jiffies
+ msecs_to_jiffies(1);
222 if (!(oxygen_read16(chip
, OXYGEN_2WIRE_BUS_STATUS
)
223 & OXYGEN_2WIRE_BUSY
))
227 } while (time_after_eq(timeout
, jiffies
));
229 oxygen_write8(chip
, OXYGEN_2WIRE_MAP
, map
);
230 oxygen_write8(chip
, OXYGEN_2WIRE_DATA
, data
);
231 oxygen_write8(chip
, OXYGEN_2WIRE_CONTROL
,
232 device
| OXYGEN_2WIRE_DIR_WRITE
);
234 EXPORT_SYMBOL(oxygen_write_i2c
);