Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Driver for Sound Core PDAudioCF soundcard | |
3 | * | |
c1017a4c | 4 | * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | ||
1da177e4 | 21 | #include <linux/delay.h> |
5a0e3ad6 | 22 | #include <linux/slab.h> |
1da177e4 LT |
23 | #include <sound/core.h> |
24 | #include <sound/info.h> | |
25 | #include "pdaudiocf.h" | |
26 | #include <sound/initval.h> | |
27 | ||
28 | /* | |
29 | * | |
30 | */ | |
31 | static unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg) | |
32 | { | |
db131548 | 33 | struct snd_pdacf *chip = private_data; |
1da177e4 LT |
34 | unsigned long timeout; |
35 | unsigned long flags; | |
36 | unsigned char res; | |
37 | ||
38 | spin_lock_irqsave(&chip->ak4117_lock, flags); | |
39 | timeout = 1000; | |
40 | while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { | |
41 | udelay(5); | |
42 | if (--timeout == 0) { | |
43 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | |
44 | snd_printk(KERN_ERR "AK4117 ready timeout (read)\n"); | |
45 | return 0; | |
46 | } | |
47 | } | |
48 | pdacf_reg_write(chip, PDAUDIOCF_REG_AK_IFR, (u16)reg << 8); | |
49 | timeout = 1000; | |
50 | while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { | |
51 | udelay(5); | |
52 | if (--timeout == 0) { | |
53 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | |
54 | snd_printk(KERN_ERR "AK4117 read timeout (read2)\n"); | |
55 | return 0; | |
56 | } | |
57 | } | |
58 | res = (unsigned char)pdacf_reg_read(chip, PDAUDIOCF_REG_AK_IFR); | |
59 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | |
60 | return res; | |
61 | } | |
62 | ||
63 | static void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val) | |
64 | { | |
db131548 | 65 | struct snd_pdacf *chip = private_data; |
1da177e4 LT |
66 | unsigned long timeout; |
67 | unsigned long flags; | |
68 | ||
69 | spin_lock_irqsave(&chip->ak4117_lock, flags); | |
70 | timeout = 1000; | |
71 | while (inw(chip->port + PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { | |
72 | udelay(5); | |
73 | if (--timeout == 0) { | |
74 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | |
75 | snd_printk(KERN_ERR "AK4117 ready timeout (write)\n"); | |
76 | return; | |
77 | } | |
78 | } | |
79 | outw((u16)reg << 8 | val | (1<<13), chip->port + PDAUDIOCF_REG_AK_IFR); | |
80 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | |
81 | } | |
82 | ||
83 | #if 0 | |
db131548 | 84 | void pdacf_dump(struct snd_pdacf *chip) |
1da177e4 | 85 | { |
2ebfb8ee TI |
86 | printk(KERN_DEBUG "PDAUDIOCF DUMP (0x%lx):\n", chip->port); |
87 | printk(KERN_DEBUG "WPD : 0x%x\n", | |
88 | inw(chip->port + PDAUDIOCF_REG_WDP)); | |
89 | printk(KERN_DEBUG "RDP : 0x%x\n", | |
90 | inw(chip->port + PDAUDIOCF_REG_RDP)); | |
91 | printk(KERN_DEBUG "TCR : 0x%x\n", | |
92 | inw(chip->port + PDAUDIOCF_REG_TCR)); | |
93 | printk(KERN_DEBUG "SCR : 0x%x\n", | |
94 | inw(chip->port + PDAUDIOCF_REG_SCR)); | |
95 | printk(KERN_DEBUG "ISR : 0x%x\n", | |
96 | inw(chip->port + PDAUDIOCF_REG_ISR)); | |
97 | printk(KERN_DEBUG "IER : 0x%x\n", | |
98 | inw(chip->port + PDAUDIOCF_REG_IER)); | |
99 | printk(KERN_DEBUG "AK_IFR : 0x%x\n", | |
100 | inw(chip->port + PDAUDIOCF_REG_AK_IFR)); | |
1da177e4 LT |
101 | } |
102 | #endif | |
103 | ||
db131548 | 104 | static int pdacf_reset(struct snd_pdacf *chip, int powerdown) |
1da177e4 LT |
105 | { |
106 | u16 val; | |
107 | ||
108 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
109 | val |= PDAUDIOCF_PDN; | |
110 | val &= ~PDAUDIOCF_RECORD; /* for sure */ | |
111 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | |
112 | udelay(5); | |
113 | val |= PDAUDIOCF_RST; | |
114 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | |
115 | udelay(200); | |
116 | val &= ~PDAUDIOCF_RST; | |
117 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | |
118 | udelay(5); | |
119 | if (!powerdown) { | |
120 | val &= ~PDAUDIOCF_PDN; | |
121 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | |
122 | udelay(200); | |
123 | } | |
124 | return 0; | |
125 | } | |
126 | ||
db131548 | 127 | void pdacf_reinit(struct snd_pdacf *chip, int resume) |
1da177e4 LT |
128 | { |
129 | pdacf_reset(chip, 0); | |
130 | if (resume) | |
131 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, chip->suspend_reg_scr); | |
132 | snd_ak4117_reinit(chip->ak4117); | |
133 | pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, chip->regmap[PDAUDIOCF_REG_TCR>>1]); | |
134 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, chip->regmap[PDAUDIOCF_REG_IER>>1]); | |
135 | } | |
136 | ||
db131548 TI |
137 | static void pdacf_proc_read(struct snd_info_entry * entry, |
138 | struct snd_info_buffer *buffer) | |
1da177e4 | 139 | { |
db131548 | 140 | struct snd_pdacf *chip = entry->private_data; |
1da177e4 LT |
141 | u16 tmp; |
142 | ||
143 | snd_iprintf(buffer, "PDAudioCF\n\n"); | |
144 | tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
145 | snd_iprintf(buffer, "FPGA revision : 0x%x\n", PDAUDIOCF_FPGAREV(tmp)); | |
146 | ||
147 | } | |
148 | ||
db131548 | 149 | static void pdacf_proc_init(struct snd_pdacf *chip) |
1da177e4 | 150 | { |
db131548 | 151 | struct snd_info_entry *entry; |
1da177e4 LT |
152 | |
153 | if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry)) | |
bf850204 | 154 | snd_info_set_text_ops(entry, chip, pdacf_proc_read); |
1da177e4 LT |
155 | } |
156 | ||
db131548 | 157 | struct snd_pdacf *snd_pdacf_create(struct snd_card *card) |
1da177e4 | 158 | { |
db131548 | 159 | struct snd_pdacf *chip; |
1da177e4 | 160 | |
561b220a | 161 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); |
1da177e4 LT |
162 | if (chip == NULL) |
163 | return NULL; | |
164 | chip->card = card; | |
165 | spin_lock_init(&chip->reg_lock); | |
166 | spin_lock_init(&chip->ak4117_lock); | |
167 | tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip); | |
168 | card->private_data = chip; | |
169 | ||
170 | pdacf_proc_init(chip); | |
171 | return chip; | |
172 | } | |
173 | ||
db131548 | 174 | static void snd_pdacf_ak4117_change(struct ak4117 *ak4117, unsigned char c0, unsigned char c1) |
1da177e4 | 175 | { |
db131548 | 176 | struct snd_pdacf *chip = ak4117->change_callback_private; |
1da177e4 LT |
177 | unsigned long flags; |
178 | u16 val; | |
179 | ||
180 | if (!(c0 & AK4117_UNLCK)) | |
181 | return; | |
182 | spin_lock_irqsave(&chip->reg_lock, flags); | |
183 | val = chip->regmap[PDAUDIOCF_REG_SCR>>1]; | |
184 | if (ak4117->rcs0 & AK4117_UNLCK) | |
185 | val |= PDAUDIOCF_BLUE_LED_OFF; | |
186 | else | |
187 | val &= ~PDAUDIOCF_BLUE_LED_OFF; | |
188 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | |
189 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
190 | } | |
191 | ||
db131548 | 192 | int snd_pdacf_ak4117_create(struct snd_pdacf *chip) |
1da177e4 LT |
193 | { |
194 | int err; | |
195 | u16 val; | |
196 | /* design note: if we unmask PLL unlock, parity, valid, audio or auto bit interrupts */ | |
197 | /* from AK4117 then INT1 pin from AK4117 will be high all time, because PCMCIA interrupts are */ | |
198 | /* egde based and FPGA does logical OR for all interrupt sources, we cannot use these */ | |
199 | /* high-rate sources */ | |
200 | static unsigned char pgm[5] = { | |
201 | AK4117_XTL_24_576M | AK4117_EXCT, /* AK4117_REG_PWRDN */ | |
202 | AK4117_CM_PLL_XTAL | AK4117_PKCS_128fs | AK4117_XCKS_128fs, /* AK4117_REQ_CLOCK */ | |
203 | AK4117_EFH_1024LRCLK | AK4117_DIF_24R | AK4117_IPS, /* AK4117_REG_IO */ | |
204 | 0xff, /* AK4117_REG_INT0_MASK */ | |
205 | AK4117_MAUTO | AK4117_MAUD | AK4117_MULK | AK4117_MPAR | AK4117_MV, /* AK4117_REG_INT1_MASK */ | |
206 | }; | |
207 | ||
208 | err = pdacf_reset(chip, 0); | |
209 | if (err < 0) | |
210 | return err; | |
211 | err = snd_ak4117_create(chip->card, pdacf_ak4117_read, pdacf_ak4117_write, pgm, chip, &chip->ak4117); | |
212 | if (err < 0) | |
213 | return err; | |
214 | ||
215 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_TCR); | |
216 | #if 1 /* normal operation */ | |
217 | val &= ~(PDAUDIOCF_ELIMAKMBIT|PDAUDIOCF_TESTDATASEL); | |
218 | #else /* debug */ | |
219 | val |= PDAUDIOCF_ELIMAKMBIT; | |
220 | val &= ~PDAUDIOCF_TESTDATASEL; | |
221 | #endif | |
222 | pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, val); | |
223 | ||
224 | /* setup the FPGA to match AK4117 setup */ | |
225 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
226 | val &= ~(PDAUDIOCF_CLKDIV0 | PDAUDIOCF_CLKDIV1); /* use 24.576Mhz clock */ | |
227 | val &= ~(PDAUDIOCF_RED_LED_OFF|PDAUDIOCF_BLUE_LED_OFF); | |
228 | val |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; /* 24-bit data */ | |
229 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | |
230 | ||
231 | /* setup LEDs and IRQ */ | |
232 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); | |
233 | val &= ~(PDAUDIOCF_IRQLVLEN0 | PDAUDIOCF_IRQLVLEN1); | |
234 | val &= ~(PDAUDIOCF_BLUEDUTY0 | PDAUDIOCF_REDDUTY0 | PDAUDIOCF_REDDUTY1); | |
235 | val |= PDAUDIOCF_BLUEDUTY1 | PDAUDIOCF_HALFRATE; | |
236 | val |= PDAUDIOCF_IRQOVREN | PDAUDIOCF_IRQAKMEN; | |
237 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); | |
238 | ||
239 | chip->ak4117->change_callback_private = chip; | |
240 | chip->ak4117->change_callback = snd_pdacf_ak4117_change; | |
241 | ||
242 | /* update LED status */ | |
243 | snd_pdacf_ak4117_change(chip->ak4117, AK4117_UNLCK, 0); | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
db131548 | 248 | void snd_pdacf_powerdown(struct snd_pdacf *chip) |
1da177e4 LT |
249 | { |
250 | u16 val; | |
251 | ||
252 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
253 | chip->suspend_reg_scr = val; | |
254 | val |= PDAUDIOCF_RED_LED_OFF | PDAUDIOCF_BLUE_LED_OFF; | |
255 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | |
256 | /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ | |
257 | val = inw(chip->port + PDAUDIOCF_REG_IER); | |
258 | val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); | |
259 | outw(val, chip->port + PDAUDIOCF_REG_IER); | |
260 | pdacf_reset(chip, 1); | |
261 | } | |
262 | ||
263 | #ifdef CONFIG_PM | |
264 | ||
2cb1331d | 265 | int snd_pdacf_suspend(struct snd_pdacf *chip) |
1da177e4 | 266 | { |
1da177e4 LT |
267 | u16 val; |
268 | ||
e4f163d9 | 269 | snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); |
1da177e4 LT |
270 | snd_pcm_suspend_all(chip->pcm); |
271 | /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ | |
272 | val = inw(chip->port + PDAUDIOCF_REG_IER); | |
273 | val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); | |
274 | outw(val, chip->port + PDAUDIOCF_REG_IER); | |
275 | chip->chip_status |= PDAUDIOCF_STAT_IS_SUSPENDED; /* ignore interrupts from now */ | |
276 | snd_pdacf_powerdown(chip); | |
277 | return 0; | |
278 | } | |
279 | ||
db131548 | 280 | static inline int check_signal(struct snd_pdacf *chip) |
1da177e4 LT |
281 | { |
282 | return (chip->ak4117->rcs0 & AK4117_UNLCK) == 0; | |
283 | } | |
284 | ||
e4f163d9 | 285 | int snd_pdacf_resume(struct snd_pdacf *chip) |
1da177e4 | 286 | { |
1da177e4 LT |
287 | int timeout = 40; |
288 | ||
289 | pdacf_reinit(chip, 1); | |
290 | /* wait for AK4117's PLL */ | |
291 | while (timeout-- > 0 && | |
292 | (snd_ak4117_external_rate(chip->ak4117) <= 0 || !check_signal(chip))) | |
293 | mdelay(1); | |
294 | chip->chip_status &= ~PDAUDIOCF_STAT_IS_SUSPENDED; | |
e4f163d9 | 295 | snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); |
1da177e4 LT |
296 | return 0; |
297 | } | |
298 | #endif |