diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-01-31 18:16:28 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-01-31 18:16:28 -0500 |
commit | e1a9c9872dd004617555dff079b357a6ffd945e9 (patch) | |
tree | c34779e59712ff345f8e4ee97e74086a85b34974 /sound/pci/oxygen/oxygen_io.c | |
parent | fcc3ff4f9d695a80dc6e6058e0d631a3026ed4c3 (diff) | |
parent | 2ecba4ffbbc6c85fce8c3878514be415edace413 (diff) |
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa
* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa: (299 commits)
[ALSA] version 1.0.16rc2
[ALSA] hda: fix Mic in as output
[ALSA] emu10k1 - Another EMU0404 Board ID
[ALSA] emu10k1 - Fix kthread handling at resume
[ALSA] emu10k1: General cleanup, add new locks, fix alsa bug#3501, kernel bug#9304.
[ALSA] emu10k1 - Use enum for emu_model types
[ALSA] emu10k1 - Don't create emu1010 controls for non-emu boards
[ALSA] emu10k1 - 1616(M) cardbus improvements
[ALSA] snd:emu10k1: E-Mu updates. Fixes to firmware loading and support for 0404.
[ALSA] emu10k1: Add comments regarding E-Mu ins and outs.
[ALSA] oxygen: revert SPI clock frequency change for AK4396/WM8785
[ALSA] es1938 - improve capture hw pointer reads
[ALSA] HDA-Intel - Add support for Intel SCH
[ALSA] hda: Add GPIO mute support to STAC9205
[ALSA] hda-codec - Add Dell T3400 support
[ALSA] hda-codec - Add model for HP DV9553EG laptop
[ALSA] hda-codec - Control SPDIF as slave
[ALSA] hda_intel: ALSA HD Audio patch for Intel ICH10 DeviceID's
[ALSA] Fix Oops with PCM OSS sync
[ALSA] hda-codec - Add speaker automute to ALC262 HP models
...
Diffstat (limited to 'sound/pci/oxygen/oxygen_io.c')
-rw-r--r-- | sound/pci/oxygen/oxygen_io.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c new file mode 100644 index 000000000000..74e23ef9c946 --- /dev/null +++ b/sound/pci/oxygen/oxygen_io.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* | ||
2 | * C-Media CMI8788 driver - helper functions | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * | ||
6 | * | ||
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. | ||
9 | * | ||
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. | ||
14 | * | ||
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 | ||
18 | */ | ||
19 | |||
20 | #include <linux/delay.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <asm/io.h> | ||
24 | #include "oxygen.h" | ||
25 | |||
26 | u8 oxygen_read8(struct oxygen *chip, unsigned int reg) | ||
27 | { | ||
28 | return inb(chip->addr + reg); | ||
29 | } | ||
30 | EXPORT_SYMBOL(oxygen_read8); | ||
31 | |||
32 | u16 oxygen_read16(struct oxygen *chip, unsigned int reg) | ||
33 | { | ||
34 | return inw(chip->addr + reg); | ||
35 | } | ||
36 | EXPORT_SYMBOL(oxygen_read16); | ||
37 | |||
38 | u32 oxygen_read32(struct oxygen *chip, unsigned int reg) | ||
39 | { | ||
40 | return inl(chip->addr + reg); | ||
41 | } | ||
42 | EXPORT_SYMBOL(oxygen_read32); | ||
43 | |||
44 | void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value) | ||
45 | { | ||
46 | outb(value, chip->addr + reg); | ||
47 | } | ||
48 | EXPORT_SYMBOL(oxygen_write8); | ||
49 | |||
50 | void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value) | ||
51 | { | ||
52 | outw(value, chip->addr + reg); | ||
53 | } | ||
54 | EXPORT_SYMBOL(oxygen_write16); | ||
55 | |||
56 | void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value) | ||
57 | { | ||
58 | outl(value, chip->addr + reg); | ||
59 | } | ||
60 | EXPORT_SYMBOL(oxygen_write32); | ||
61 | |||
62 | void oxygen_write8_masked(struct oxygen *chip, unsigned int reg, | ||
63 | u8 value, u8 mask) | ||
64 | { | ||
65 | u8 tmp = inb(chip->addr + reg); | ||
66 | outb((tmp & ~mask) | (value & mask), chip->addr + reg); | ||
67 | } | ||
68 | EXPORT_SYMBOL(oxygen_write8_masked); | ||
69 | |||
70 | void oxygen_write16_masked(struct oxygen *chip, unsigned int reg, | ||
71 | u16 value, u16 mask) | ||
72 | { | ||
73 | u16 tmp = inw(chip->addr + reg); | ||
74 | outw((tmp & ~mask) | (value & mask), chip->addr + reg); | ||
75 | } | ||
76 | EXPORT_SYMBOL(oxygen_write16_masked); | ||
77 | |||
78 | void oxygen_write32_masked(struct oxygen *chip, unsigned int reg, | ||
79 | u32 value, u32 mask) | ||
80 | { | ||
81 | u32 tmp = inl(chip->addr + reg); | ||
82 | outl((tmp & ~mask) | (value & mask), chip->addr + reg); | ||
83 | } | ||
84 | EXPORT_SYMBOL(oxygen_write32_masked); | ||
85 | |||
86 | static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask) | ||
87 | { | ||
88 | u8 status = 0; | ||
89 | |||
90 | /* | ||
91 | * Reading the status register also clears the bits, so we have to save | ||
92 | * the read bits in status. | ||
93 | */ | ||
94 | wait_event_timeout(chip->ac97_waitqueue, | ||
95 | ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); | ||
96 | status & mask; }), | ||
97 | msecs_to_jiffies(1) + 1); | ||
98 | /* | ||
99 | * Check even after a timeout because this function should not require | ||
100 | * the AC'97 interrupt to be enabled. | ||
101 | */ | ||
102 | status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); | ||
103 | return status & mask ? 0 : -EIO; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * About 10% of AC'97 register reads or writes fail to complete, but even those | ||
108 | * where the controller indicates completion aren't guaranteed to have actually | ||
109 | * happened. | ||
110 | * | ||
111 | * It's hard to assign blame to either the controller or the codec because both | ||
112 | * were made by C-Media ... | ||
113 | */ | ||
114 | |||
115 | void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, | ||
116 | unsigned int index, u16 data) | ||
117 | { | ||
118 | unsigned int count, succeeded; | ||
119 | u32 reg; | ||
120 | |||
121 | reg = data; | ||
122 | reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT; | ||
123 | reg |= OXYGEN_AC97_REG_DIR_WRITE; | ||
124 | reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; | ||
125 | succeeded = 0; | ||
126 | for (count = 5; count > 0; --count) { | ||
127 | udelay(5); | ||
128 | oxygen_write32(chip, OXYGEN_AC97_REGS, reg); | ||
129 | /* require two "completed" writes, just to be sure */ | ||
130 | if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 && | ||
131 | ++succeeded >= 2) | ||
132 | return; | ||
133 | } | ||
134 | snd_printk(KERN_ERR "AC'97 write timeout\n"); | ||
135 | } | ||
136 | EXPORT_SYMBOL(oxygen_write_ac97); | ||
137 | |||
138 | u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, | ||
139 | unsigned int index) | ||
140 | { | ||
141 | unsigned int count; | ||
142 | unsigned int last_read = UINT_MAX; | ||
143 | u32 reg; | ||
144 | |||
145 | reg = index << OXYGEN_AC97_REG_ADDR_SHIFT; | ||
146 | reg |= OXYGEN_AC97_REG_DIR_READ; | ||
147 | reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; | ||
148 | for (count = 5; count > 0; --count) { | ||
149 | udelay(5); | ||
150 | oxygen_write32(chip, OXYGEN_AC97_REGS, reg); | ||
151 | udelay(10); | ||
152 | if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) { | ||
153 | u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS); | ||
154 | /* we require two consecutive reads of the same value */ | ||
155 | if (value == last_read) | ||
156 | return value; | ||
157 | last_read = value; | ||
158 | /* | ||
159 | * Invert the register value bits to make sure that two | ||
160 | * consecutive unsuccessful reads do not return the same | ||
161 | * value. | ||
162 | */ | ||
163 | reg ^= 0xffff; | ||
164 | } | ||
165 | } | ||
166 | snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec); | ||
167 | return 0; | ||
168 | } | ||
169 | EXPORT_SYMBOL(oxygen_read_ac97); | ||
170 | |||
171 | void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, | ||
172 | unsigned int index, u16 data, u16 mask) | ||
173 | { | ||
174 | u16 value = oxygen_read_ac97(chip, codec, index); | ||
175 | value &= ~mask; | ||
176 | value |= data & mask; | ||
177 | oxygen_write_ac97(chip, codec, index, value); | ||
178 | } | ||
179 | EXPORT_SYMBOL(oxygen_write_ac97_masked); | ||
180 | |||
181 | void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) | ||
182 | { | ||
183 | unsigned int count; | ||
184 | |||
185 | /* should not need more than 7.68 us (24 * 320 ns) */ | ||
186 | count = 10; | ||
187 | while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) | ||
188 | && count > 0) { | ||
189 | udelay(1); | ||
190 | --count; | ||
191 | } | ||
192 | |||
193 | spin_lock_irq(&chip->reg_lock); | ||
194 | oxygen_write8(chip, OXYGEN_SPI_DATA1, data); | ||
195 | oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); | ||
196 | if (control & OXYGEN_SPI_DATA_LENGTH_3) | ||
197 | oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); | ||
198 | oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); | ||
199 | spin_unlock_irq(&chip->reg_lock); | ||
200 | } | ||
201 | EXPORT_SYMBOL(oxygen_write_spi); | ||