blob: 521db705d179f94a3edcb6963773448e0ded2f09 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Jaroslav Kyselac1017a42007-10-15 09:50:19 +02002 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
4 *
5 * Bugs:
6 * - sometimes record brokes playback with WSS portion of
7 * Yamaha OPL3-SA3 chip
8 * - CS4231 (GUS MAX) - still trouble with occasional noises
9 * - broken initialization?
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <linux/delay.h>
28#include <linux/pm.h>
29#include <linux/init.h>
30#include <linux/interrupt.h>
31#include <linux/slab.h>
32#include <linux/ioport.h>
33#include <sound/core.h>
34#include <sound/cs4231.h>
35#include <sound/pcm_params.h>
36
37#include <asm/io.h>
38#include <asm/dma.h>
39#include <asm/irq.h>
40
Jaroslav Kyselac1017a42007-10-15 09:50:19 +020041MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
Linus Torvalds1da177e2005-04-16 15:20:36 -070042MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips");
43MODULE_LICENSE("GPL");
44
45#if 0
46#define SNDRV_DEBUG_MCE
47#endif
48
49/*
50 * Some variables
51 */
52
53static unsigned char freq_bits[14] = {
54 /* 5510 */ 0x00 | CS4231_XTAL2,
55 /* 6620 */ 0x0E | CS4231_XTAL2,
56 /* 8000 */ 0x00 | CS4231_XTAL1,
57 /* 9600 */ 0x0E | CS4231_XTAL1,
58 /* 11025 */ 0x02 | CS4231_XTAL2,
59 /* 16000 */ 0x02 | CS4231_XTAL1,
60 /* 18900 */ 0x04 | CS4231_XTAL2,
61 /* 22050 */ 0x06 | CS4231_XTAL2,
62 /* 27042 */ 0x04 | CS4231_XTAL1,
63 /* 32000 */ 0x06 | CS4231_XTAL1,
64 /* 33075 */ 0x0C | CS4231_XTAL2,
65 /* 37800 */ 0x08 | CS4231_XTAL2,
66 /* 44100 */ 0x0A | CS4231_XTAL2,
67 /* 48000 */ 0x0C | CS4231_XTAL1
68};
69
70static unsigned int rates[14] = {
71 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
72 27042, 32000, 33075, 37800, 44100, 48000
73};
74
Takashi Iwaiba2375a2005-11-17 14:30:42 +010075static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
Krzysztof Helt6c041b52007-09-06 15:03:59 +020076 .count = ARRAY_SIZE(rates),
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 .list = rates,
78 .mask = 0,
79};
80
Takashi Iwaiba2375a2005-11-17 14:30:42 +010081static int snd_cs4231_xrate(struct snd_pcm_runtime *runtime)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
83 return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
84}
85
86static unsigned char snd_cs4231_original_image[32] =
87{
88 0x00, /* 00/00 - lic */
89 0x00, /* 01/01 - ric */
90 0x9f, /* 02/02 - la1ic */
91 0x9f, /* 03/03 - ra1ic */
92 0x9f, /* 04/04 - la2ic */
93 0x9f, /* 05/05 - ra2ic */
94 0xbf, /* 06/06 - loc */
95 0xbf, /* 07/07 - roc */
96 0x20, /* 08/08 - pdfr */
97 CS4231_AUTOCALIB, /* 09/09 - ic */
98 0x00, /* 0a/10 - pc */
99 0x00, /* 0b/11 - ti */
100 CS4231_MODE2, /* 0c/12 - mi */
101 0xfc, /* 0d/13 - lbc */
102 0x00, /* 0e/14 - pbru */
103 0x00, /* 0f/15 - pbrl */
104 0x80, /* 10/16 - afei */
105 0x01, /* 11/17 - afeii */
106 0x9f, /* 12/18 - llic */
107 0x9f, /* 13/19 - rlic */
108 0x00, /* 14/20 - tlb */
109 0x00, /* 15/21 - thb */
110 0x00, /* 16/22 - la3mic/reserved */
111 0x00, /* 17/23 - ra3mic/reserved */
112 0x00, /* 18/24 - afs */
113 0x00, /* 19/25 - lamoc/version */
114 0xcf, /* 1a/26 - mioc */
115 0x00, /* 1b/27 - ramoc/reserved */
116 0x20, /* 1c/28 - cdfr */
117 0x00, /* 1d/29 - res4 */
118 0x00, /* 1e/30 - cbru */
119 0x00, /* 1f/31 - cbrl */
120};
121
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +0200122static unsigned char snd_opti93x_original_image[32] =
123{
124 0x00, /* 00/00 - l_mixout_outctrl */
125 0x00, /* 01/01 - r_mixout_outctrl */
126 0x88, /* 02/02 - l_cd_inctrl */
127 0x88, /* 03/03 - r_cd_inctrl */
128 0x88, /* 04/04 - l_a1/fm_inctrl */
129 0x88, /* 05/05 - r_a1/fm_inctrl */
130 0x80, /* 06/06 - l_dac_inctrl */
131 0x80, /* 07/07 - r_dac_inctrl */
132 0x00, /* 08/08 - ply_dataform_reg */
133 0x00, /* 09/09 - if_conf */
134 0x00, /* 0a/10 - pin_ctrl */
135 0x00, /* 0b/11 - err_init_reg */
136 0x0a, /* 0c/12 - id_reg */
137 0x00, /* 0d/13 - reserved */
138 0x00, /* 0e/14 - ply_upcount_reg */
139 0x00, /* 0f/15 - ply_lowcount_reg */
140 0x88, /* 10/16 - reserved/l_a1_inctrl */
141 0x88, /* 11/17 - reserved/r_a1_inctrl */
142 0x88, /* 12/18 - l_line_inctrl */
143 0x88, /* 13/19 - r_line_inctrl */
144 0x88, /* 14/20 - l_mic_inctrl */
145 0x88, /* 15/21 - r_mic_inctrl */
146 0x80, /* 16/22 - l_out_outctrl */
147 0x80, /* 17/23 - r_out_outctrl */
148 0x00, /* 18/24 - reserved */
149 0x00, /* 19/25 - reserved */
150 0x00, /* 1a/26 - reserved */
151 0x00, /* 1b/27 - reserved */
152 0x00, /* 1c/28 - cap_dataform_reg */
153 0x00, /* 1d/29 - reserved */
154 0x00, /* 1e/30 - cap_upcount_reg */
155 0x00 /* 1f/31 - cap_lowcount_reg */
156};
157
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158/*
159 * Basic I/O functions
160 */
161
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100162static inline void cs4231_outb(struct snd_cs4231 *chip, u8 offset, u8 val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 outb(val, chip->port + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165}
166
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100167static inline u8 cs4231_inb(struct snd_cs4231 *chip, u8 offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 return inb(chip->port + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170}
171
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200172static void snd_cs4231_wait(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173{
174 int timeout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
176 for (timeout = 250;
177 timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
178 timeout--)
179 udelay(100);
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200180}
181
182static void snd_cs4231_outm(struct snd_cs4231 *chip, unsigned char reg,
183 unsigned char mask, unsigned char value)
184{
185 unsigned char tmp = (chip->image[reg] & mask) | value;
186
187 snd_cs4231_wait(chip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188#ifdef CONFIG_SND_DEBUG
189 if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
190 snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
191#endif
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200192 chip->image[reg] = tmp;
193 if (!chip->calibrate_mute) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200195 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 cs4231_outb(chip, CS4231P(REG), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 mb();
198 }
199}
200
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100201static void snd_cs4231_dout(struct snd_cs4231 *chip, unsigned char reg, unsigned char value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
203 int timeout;
204
205 for (timeout = 250;
206 timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
207 timeout--)
208 udelay(10);
209 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
210 cs4231_outb(chip, CS4231P(REG), value);
211 mb();
212}
213
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100214void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200216 snd_cs4231_wait(chip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217#ifdef CONFIG_SND_DEBUG
218 if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
219 snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
220#endif
221 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
222 cs4231_outb(chip, CS4231P(REG), value);
223 chip->image[reg] = value;
224 mb();
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200225 snd_printdd("codec out - reg 0x%x = 0x%x\n",
226 chip->mce_bit | reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227}
228
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100229unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200231 snd_cs4231_wait(chip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232#ifdef CONFIG_SND_DEBUG
233 if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
234 snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
235#endif
236 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
237 mb();
238 return cs4231_inb(chip, CS4231P(REG));
239}
240
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100241void snd_cs4236_ext_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
244 cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01));
245 cs4231_outb(chip, CS4231P(REG), val);
246 chip->eimage[CS4236_REG(reg)] = val;
247#if 0
248 printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
249#endif
250}
251
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100252unsigned char snd_cs4236_ext_in(struct snd_cs4231 *chip, unsigned char reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
254 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
255 cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01));
256#if 1
257 return cs4231_inb(chip, CS4231P(REG));
258#else
259 {
260 unsigned char res;
261 res = cs4231_inb(chip, CS4231P(REG));
262 printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
263 return res;
264 }
265#endif
266}
267
268#if 0
269
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100270static void snd_cs4231_debug(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
272 printk("CS4231 REGS: INDEX = 0x%02x ", cs4231_inb(chip, CS4231P(REGSEL)));
273 printk(" STATUS = 0x%02x\n", cs4231_inb(chip, CS4231P(STATUS)));
274 printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00));
275 printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10));
276 printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01));
277 printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11));
278 printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02));
279 printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12));
280 printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03));
281 printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13));
282 printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04));
283 printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14));
284 printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05));
285 printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15));
286 printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06));
287 printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16));
288 printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07));
289 printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17));
290 printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08));
291 printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18));
292 printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09));
293 printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19));
294 printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a));
295 printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a));
296 printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b));
297 printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b));
298 printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c));
299 printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c));
300 printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d));
301 printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d));
302 printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e));
303 printk(" 0x1e: ply lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e));
304 printk(" 0x0f: rec upr count = 0x%02x ", snd_cs4231_in(chip, 0x0f));
305 printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f));
306}
307
308#endif
309
310/*
311 * CS4231 detection / MCE routines
312 */
313
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100314static void snd_cs4231_busy_wait(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315{
316 int timeout;
317
318 /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
319 for (timeout = 5; timeout > 0; timeout--)
320 cs4231_inb(chip, CS4231P(REGSEL));
321 /* end of cleanup sequence */
322 for (timeout = 250;
323 timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
324 timeout--)
325 udelay(10);
326}
327
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100328void snd_cs4231_mce_up(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
330 unsigned long flags;
331 int timeout;
332
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200333 snd_cs4231_wait(chip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334#ifdef CONFIG_SND_DEBUG
335 if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
336 snd_printk("mce_up - auto calibration time out (0)\n");
337#endif
338 spin_lock_irqsave(&chip->reg_lock, flags);
339 chip->mce_bit |= CS4231_MCE;
340 timeout = cs4231_inb(chip, CS4231P(REGSEL));
341 if (timeout == 0x80)
342 snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
343 if (!(timeout & CS4231_MCE))
344 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
345 spin_unlock_irqrestore(&chip->reg_lock, flags);
346}
347
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100348void snd_cs4231_mce_down(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
350 unsigned long flags;
Takashi Iwaib875d652007-09-11 10:12:14 +0200351 unsigned long end_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 int timeout;
353
354 snd_cs4231_busy_wait(chip);
Rene Hermand44df2d2007-09-10 23:22:55 +0200355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356#ifdef CONFIG_SND_DEBUG
357 if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
358 snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL));
359#endif
360 spin_lock_irqsave(&chip->reg_lock, flags);
361 chip->mce_bit &= ~CS4231_MCE;
362 timeout = cs4231_inb(chip, CS4231P(REGSEL));
363 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
364 spin_unlock_irqrestore(&chip->reg_lock, flags);
365 if (timeout == 0x80)
366 snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
367 if ((timeout & CS4231_MCE) == 0 ||
368 !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
369 return;
370 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Rene Herman90cf9b82007-09-10 23:19:55 +0200372 /*
373 * Wait for (possible -- during init auto-calibration may not be set)
374 * calibration process to start. Needs upto 5 sample periods on AD1848
375 * which at the slowest possible rate of 5.5125 kHz means 907 us.
376 */
377 msleep(1);
Rene Hermand44df2d2007-09-10 23:22:55 +0200378
379 snd_printdd("(1) jiffies = %lu\n", jiffies);
380
Krzysztof Helt23d4635ed2007-09-11 00:40:42 +0200381 /* check condition up to 250 ms */
Takashi Iwaib875d652007-09-11 10:12:14 +0200382 end_time = jiffies + msecs_to_jiffies(250);
Krzysztof Helt23d4635ed2007-09-11 00:40:42 +0200383 while (snd_cs4231_in(chip, CS4231_TEST_INIT) &
384 CS4231_CALIB_IN_PROGRESS) {
385
Takashi Iwaib875d652007-09-11 10:12:14 +0200386 if (time_after(jiffies, end_time)) {
Krzysztof Helt23d4635ed2007-09-11 00:40:42 +0200387 snd_printk(KERN_ERR "mce_down - "
388 "auto calibration time out (2)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 return;
390 }
Takashi Iwaib875d652007-09-11 10:12:14 +0200391 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 }
Rene Hermand44df2d2007-09-10 23:22:55 +0200393
394 snd_printdd("(2) jiffies = %lu\n", jiffies);
395
Krzysztof Helt23d4635ed2007-09-11 00:40:42 +0200396 /* check condition up to 100 ms */
Takashi Iwaib875d652007-09-11 10:12:14 +0200397 end_time = jiffies + msecs_to_jiffies(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
Takashi Iwaib875d652007-09-11 10:12:14 +0200399 if (time_after(jiffies, end_time)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
401 return;
402 }
Takashi Iwaib875d652007-09-11 10:12:14 +0200403 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
Rene Hermand44df2d2007-09-10 23:22:55 +0200405
406 snd_printdd("(3) jiffies = %lu\n", jiffies);
407 snd_printd("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408}
409
410static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size)
411{
412 switch (format & 0xe0) {
413 case CS4231_LINEAR_16:
414 case CS4231_LINEAR_16_BIG:
415 size >>= 1;
416 break;
417 case CS4231_ADPCM_16:
418 return size >> 2;
419 }
420 if (format & CS4231_STEREO)
421 size >>= 1;
422 return size;
423}
424
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100425static int snd_cs4231_trigger(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 int cmd)
427{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100428 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 int result = 0;
430 unsigned int what;
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100431 struct snd_pcm_substream *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 int do_start;
433
434#if 0
435 printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, cs4231_inb(chip, CS4231P(STATUS)));
436#endif
437
438 switch (cmd) {
439 case SNDRV_PCM_TRIGGER_START:
440 case SNDRV_PCM_TRIGGER_RESUME:
441 do_start = 1; break;
442 case SNDRV_PCM_TRIGGER_STOP:
443 case SNDRV_PCM_TRIGGER_SUSPEND:
444 do_start = 0; break;
445 default:
446 return -EINVAL;
447 }
448
449 what = 0;
Takashi Iwaief991b92007-02-22 12:52:53 +0100450 snd_pcm_group_for_each_entry(s, substream) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 if (s == chip->playback_substream) {
452 what |= CS4231_PLAYBACK_ENABLE;
453 snd_pcm_trigger_done(s, substream);
454 } else if (s == chip->capture_substream) {
455 what |= CS4231_RECORD_ENABLE;
456 snd_pcm_trigger_done(s, substream);
457 }
458 }
459 spin_lock(&chip->reg_lock);
460 if (do_start) {
461 chip->image[CS4231_IFACE_CTRL] |= what;
462 if (chip->trigger)
463 chip->trigger(chip, what, 1);
464 } else {
465 chip->image[CS4231_IFACE_CTRL] &= ~what;
466 if (chip->trigger)
467 chip->trigger(chip, what, 0);
468 }
469 snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
470 spin_unlock(&chip->reg_lock);
471#if 0
472 snd_cs4231_debug(chip);
473#endif
474 return result;
475}
476
477/*
478 * CODEC I/O
479 */
480
481static unsigned char snd_cs4231_get_rate(unsigned int rate)
482{
483 int i;
484
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200485 for (i = 0; i < ARRAY_SIZE(rates); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 if (rate == rates[i])
487 return freq_bits[i];
488 // snd_BUG();
Krzysztof Helt6c041b52007-09-06 15:03:59 +0200489 return freq_bits[ARRAY_SIZE(rates) - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490}
491
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100492static unsigned char snd_cs4231_get_format(struct snd_cs4231 *chip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 int format,
494 int channels)
495{
496 unsigned char rformat;
497
498 rformat = CS4231_LINEAR_8;
499 switch (format) {
500 case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break;
501 case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break;
502 case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break;
503 case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break;
504 case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break;
505 }
506 if (channels > 1)
507 rformat |= CS4231_STEREO;
508#if 0
509 snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
510#endif
511 return rformat;
512}
513
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100514static void snd_cs4231_calibrate_mute(struct snd_cs4231 *chip, int mute)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
516 unsigned long flags;
517
518 mute = mute ? 1 : 0;
519 spin_lock_irqsave(&chip->reg_lock, flags);
520 if (chip->calibrate_mute == mute) {
521 spin_unlock_irqrestore(&chip->reg_lock, flags);
522 return;
523 }
524 if (!mute) {
525 snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]);
526 snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]);
527 snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]);
528 }
529 snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]);
530 snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]);
531 snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]);
532 snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]);
533 snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]);
534 snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]);
535 snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]);
536 snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]);
537 snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
538 if (chip->hardware == CS4231_HW_INTERWAVE) {
539 snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]);
540 snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]);
541 snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]);
542 snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]);
543 }
544 chip->calibrate_mute = mute;
545 spin_unlock_irqrestore(&chip->reg_lock, flags);
546}
547
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100548static void snd_cs4231_playback_format(struct snd_cs4231 *chip,
549 struct snd_pcm_hw_params *params,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 unsigned char pdfr)
551{
552 unsigned long flags;
553 int full_calib = 1;
554
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100555 mutex_lock(&chip->mce_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 snd_cs4231_calibrate_mute(chip, 1);
557 if (chip->hardware == CS4231_HW_CS4231A ||
558 (chip->hardware & CS4231_HW_CS4232_MASK)) {
559 spin_lock_irqsave(&chip->reg_lock, flags);
560 if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */
561 snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
562 snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
563 snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
564 udelay(100); /* Fixes audible clicks at least on GUS MAX */
565 full_calib = 0;
566 }
567 spin_unlock_irqrestore(&chip->reg_lock, flags);
568 }
569 if (full_calib) {
570 snd_cs4231_mce_up(chip);
571 spin_lock_irqsave(&chip->reg_lock, flags);
572 if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) {
573 snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
574 (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ?
575 (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) :
576 pdfr);
577 } else {
578 snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
579 }
580 spin_unlock_irqrestore(&chip->reg_lock, flags);
Paul Vojtae2340462007-07-27 12:20:38 +0200581 if (chip->hardware == CS4231_HW_OPL3SA2)
582 udelay(100); /* this seems to help */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 snd_cs4231_mce_down(chip);
584 }
585 snd_cs4231_calibrate_mute(chip, 0);
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100586 mutex_unlock(&chip->mce_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587}
588
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100589static void snd_cs4231_capture_format(struct snd_cs4231 *chip,
590 struct snd_pcm_hw_params *params,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 unsigned char cdfr)
592{
593 unsigned long flags;
594 int full_calib = 1;
595
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100596 mutex_lock(&chip->mce_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 snd_cs4231_calibrate_mute(chip, 1);
598 if (chip->hardware == CS4231_HW_CS4231A ||
599 (chip->hardware & CS4231_HW_CS4232_MASK)) {
600 spin_lock_irqsave(&chip->reg_lock, flags);
601 if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */
602 (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
603 snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
604 snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr);
605 snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
606 full_calib = 0;
607 }
608 spin_unlock_irqrestore(&chip->reg_lock, flags);
609 }
610 if (full_calib) {
611 snd_cs4231_mce_up(chip);
612 spin_lock_irqsave(&chip->reg_lock, flags);
613 if (chip->hardware != CS4231_HW_INTERWAVE) {
614 if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
615 snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
616 ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) |
617 (cdfr & 0x0f));
618 spin_unlock_irqrestore(&chip->reg_lock, flags);
619 snd_cs4231_mce_down(chip);
620 snd_cs4231_mce_up(chip);
621 spin_lock_irqsave(&chip->reg_lock, flags);
622 }
623 }
624 snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr);
625 spin_unlock_irqrestore(&chip->reg_lock, flags);
626 snd_cs4231_mce_down(chip);
627 }
628 snd_cs4231_calibrate_mute(chip, 0);
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100629 mutex_unlock(&chip->mce_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630}
631
632/*
633 * Timer interface
634 */
635
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100636static unsigned long snd_cs4231_timer_resolution(struct snd_timer * timer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100638 struct snd_cs4231 *chip = snd_timer_chip(timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 if (chip->hardware & CS4231_HW_CS4236B_MASK)
640 return 14467;
641 else
642 return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
643}
644
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100645static int snd_cs4231_timer_start(struct snd_timer * timer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646{
647 unsigned long flags;
648 unsigned int ticks;
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100649 struct snd_cs4231 *chip = snd_timer_chip(timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 spin_lock_irqsave(&chip->reg_lock, flags);
651 ticks = timer->sticks;
652 if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
653 (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
654 (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
655 snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8));
656 snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks);
657 snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE);
658 }
659 spin_unlock_irqrestore(&chip->reg_lock, flags);
660 return 0;
661}
662
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100663static int snd_cs4231_timer_stop(struct snd_timer * timer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
665 unsigned long flags;
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100666 struct snd_cs4231 *chip = snd_timer_chip(timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 spin_lock_irqsave(&chip->reg_lock, flags);
668 snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE);
669 spin_unlock_irqrestore(&chip->reg_lock, flags);
670 return 0;
671}
672
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100673static void snd_cs4231_init(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674{
675 unsigned long flags;
676
677 snd_cs4231_mce_down(chip);
678
679#ifdef SNDRV_DEBUG_MCE
680 snd_printk("init: (1)\n");
681#endif
682 snd_cs4231_mce_up(chip);
683 spin_lock_irqsave(&chip->reg_lock, flags);
684 chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
685 CS4231_RECORD_ENABLE | CS4231_RECORD_PIO |
686 CS4231_CALIB_MODE);
687 chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
688 snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
689 spin_unlock_irqrestore(&chip->reg_lock, flags);
690 snd_cs4231_mce_down(chip);
691
692#ifdef SNDRV_DEBUG_MCE
693 snd_printk("init: (2)\n");
694#endif
695
696 snd_cs4231_mce_up(chip);
697 spin_lock_irqsave(&chip->reg_lock, flags);
698 snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
699 spin_unlock_irqrestore(&chip->reg_lock, flags);
700 snd_cs4231_mce_down(chip);
701
702#ifdef SNDRV_DEBUG_MCE
703 snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]);
704#endif
705
706 spin_lock_irqsave(&chip->reg_lock, flags);
707 snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]);
708 spin_unlock_irqrestore(&chip->reg_lock, flags);
709
710 snd_cs4231_mce_up(chip);
711 spin_lock_irqsave(&chip->reg_lock, flags);
712 snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]);
713 spin_unlock_irqrestore(&chip->reg_lock, flags);
714 snd_cs4231_mce_down(chip);
715
716#ifdef SNDRV_DEBUG_MCE
717 snd_printk("init: (4)\n");
718#endif
719
720 snd_cs4231_mce_up(chip);
721 spin_lock_irqsave(&chip->reg_lock, flags);
722 snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]);
723 spin_unlock_irqrestore(&chip->reg_lock, flags);
724 snd_cs4231_mce_down(chip);
725
726#ifdef SNDRV_DEBUG_MCE
727 snd_printk("init: (5)\n");
728#endif
729}
730
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100731static int snd_cs4231_open(struct snd_cs4231 *chip, unsigned int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732{
733 unsigned long flags;
734
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100735 mutex_lock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 if ((chip->mode & mode) ||
737 ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) {
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100738 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 return -EAGAIN;
740 }
741 if (chip->mode & CS4231_MODE_OPEN) {
742 chip->mode |= mode;
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100743 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 return 0;
745 }
746 /* ok. now enable and ack CODEC IRQ */
747 spin_lock_irqsave(&chip->reg_lock, flags);
748 snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
749 CS4231_RECORD_IRQ |
750 CS4231_TIMER_IRQ);
751 snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
752 cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
753 cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
754 chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
755 snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
756 snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
757 CS4231_RECORD_IRQ |
758 CS4231_TIMER_IRQ);
759 snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
760 spin_unlock_irqrestore(&chip->reg_lock, flags);
761
762 chip->mode = mode;
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100763 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 return 0;
765}
766
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100767static void snd_cs4231_close(struct snd_cs4231 *chip, unsigned int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768{
769 unsigned long flags;
770
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100771 mutex_lock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 chip->mode &= ~mode;
773 if (chip->mode & CS4231_MODE_OPEN) {
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100774 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 return;
776 }
777 snd_cs4231_calibrate_mute(chip, 1);
778
779 /* disable IRQ */
780 spin_lock_irqsave(&chip->reg_lock, flags);
781 snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
782 cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
783 cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
784 chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
785 snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
786
787 /* now disable record & playback */
788
789 if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
790 CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
791 spin_unlock_irqrestore(&chip->reg_lock, flags);
792 snd_cs4231_mce_up(chip);
793 spin_lock_irqsave(&chip->reg_lock, flags);
794 chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
795 CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
796 snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
797 spin_unlock_irqrestore(&chip->reg_lock, flags);
798 snd_cs4231_mce_down(chip);
799 spin_lock_irqsave(&chip->reg_lock, flags);
800 }
801
802 /* clear IRQ again */
803 snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
804 cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
805 cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
806 spin_unlock_irqrestore(&chip->reg_lock, flags);
807
808 snd_cs4231_calibrate_mute(chip, 0);
809
810 chip->mode = 0;
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100811 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812}
813
814/*
815 * timer open/close
816 */
817
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100818static int snd_cs4231_timer_open(struct snd_timer * timer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100820 struct snd_cs4231 *chip = snd_timer_chip(timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 snd_cs4231_open(chip, CS4231_MODE_TIMER);
822 return 0;
823}
824
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100825static int snd_cs4231_timer_close(struct snd_timer * timer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100827 struct snd_cs4231 *chip = snd_timer_chip(timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 snd_cs4231_close(chip, CS4231_MODE_TIMER);
829 return 0;
830}
831
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100832static struct snd_timer_hardware snd_cs4231_timer_table =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833{
834 .flags = SNDRV_TIMER_HW_AUTO,
835 .resolution = 9945,
836 .ticks = 65535,
837 .open = snd_cs4231_timer_open,
838 .close = snd_cs4231_timer_close,
839 .c_resolution = snd_cs4231_timer_resolution,
840 .start = snd_cs4231_timer_start,
841 .stop = snd_cs4231_timer_stop,
842};
843
844/*
845 * ok.. exported functions..
846 */
847
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100848static int snd_cs4231_playback_hw_params(struct snd_pcm_substream *substream,
849 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100851 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 unsigned char new_pdfr;
853 int err;
854
855 if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
856 return err;
857 new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
858 snd_cs4231_get_rate(params_rate(hw_params));
859 chip->set_playback_format(chip, hw_params, new_pdfr);
860 return 0;
861}
862
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100863static int snd_cs4231_playback_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
865 return snd_pcm_lib_free_pages(substream);
866}
867
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100868static int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100870 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
871 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 unsigned long flags;
873 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
874 unsigned int count = snd_pcm_lib_period_bytes(substream);
875
876 spin_lock_irqsave(&chip->reg_lock, flags);
877 chip->p_dma_size = size;
878 chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
879 snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
880 count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
881 snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
882 snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
883 spin_unlock_irqrestore(&chip->reg_lock, flags);
884#if 0
885 snd_cs4231_debug(chip);
886#endif
887 return 0;
888}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100890static int snd_cs4231_capture_hw_params(struct snd_pcm_substream *substream,
891 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100893 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 unsigned char new_cdfr;
895 int err;
896
897 if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
898 return err;
899 new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
900 snd_cs4231_get_rate(params_rate(hw_params));
901 chip->set_capture_format(chip, hw_params, new_cdfr);
902 return 0;
903}
904
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100905static int snd_cs4231_capture_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906{
907 return snd_pcm_lib_free_pages(substream);
908}
909
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100910static int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100912 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
913 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 unsigned long flags;
915 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
916 unsigned int count = snd_pcm_lib_period_bytes(substream);
917
918 spin_lock_irqsave(&chip->reg_lock, flags);
919 chip->c_dma_size = size;
920 chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
921 snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
922 count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1;
923 if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
924 snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
925 snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
926 } else {
927 snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
928 snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8));
929 }
930 spin_unlock_irqrestore(&chip->reg_lock, flags);
931 return 0;
932}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +0200934void snd_cs4231_overrange(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935{
936 unsigned long flags;
937 unsigned char res;
938
939 spin_lock_irqsave(&chip->reg_lock, flags);
940 res = snd_cs4231_in(chip, CS4231_TEST_INIT);
941 spin_unlock_irqrestore(&chip->reg_lock, flags);
942 if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */
943 chip->capture_substream->runtime->overrange++;
944}
945
David Howells7d12e782006-10-05 14:55:46 +0100946irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100948 struct snd_cs4231 *chip = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 unsigned char status;
950
951 status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
952 if (status & CS4231_TIMER_IRQ) {
953 if (chip->timer)
954 snd_timer_interrupt(chip->timer, chip->timer->sticks);
955 }
956 if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
957 if (status & CS4231_PLAYBACK_IRQ) {
958 if (chip->mode & CS4231_MODE_PLAY) {
959 if (chip->playback_substream)
960 snd_pcm_period_elapsed(chip->playback_substream);
961 }
962 if (chip->mode & CS4231_MODE_RECORD) {
963 if (chip->capture_substream) {
964 snd_cs4231_overrange(chip);
965 snd_pcm_period_elapsed(chip->capture_substream);
966 }
967 }
968 }
969 } else {
970 if (status & CS4231_PLAYBACK_IRQ) {
971 if (chip->playback_substream)
972 snd_pcm_period_elapsed(chip->playback_substream);
973 }
974 if (status & CS4231_RECORD_IRQ) {
975 if (chip->capture_substream) {
976 snd_cs4231_overrange(chip);
977 snd_pcm_period_elapsed(chip->capture_substream);
978 }
979 }
980 }
981
982 spin_lock(&chip->reg_lock);
983 snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
984 spin_unlock(&chip->reg_lock);
985 return IRQ_HANDLED;
986}
987
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100988static snd_pcm_uframes_t snd_cs4231_playback_pointer(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989{
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100990 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 size_t ptr;
992
993 if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
994 return 0;
995 ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
996 return bytes_to_frames(substream->runtime, ptr);
997}
998
Takashi Iwaiba2375a2005-11-17 14:30:42 +0100999static snd_pcm_uframes_t snd_cs4231_capture_pointer(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001001 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 size_t ptr;
1003
1004 if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
1005 return 0;
1006 ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
1007 return bytes_to_frames(substream->runtime, ptr);
1008}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009
1010/*
1011
1012 */
1013
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001014static int snd_cs4231_probe(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015{
1016 unsigned long flags;
1017 int i, id, rev;
1018 unsigned char *ptr;
1019 unsigned int hw;
1020
1021#if 0
1022 snd_cs4231_debug(chip);
1023#endif
1024 id = 0;
1025 for (i = 0; i < 50; i++) {
1026 mb();
1027 if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
1028 udelay(2000);
1029 else {
1030 spin_lock_irqsave(&chip->reg_lock, flags);
1031 snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
1032 id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f;
1033 spin_unlock_irqrestore(&chip->reg_lock, flags);
1034 if (id == 0x0a)
1035 break; /* this is valid value */
1036 }
1037 }
1038 snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id);
1039 if (id != 0x0a)
1040 return -ENODEV; /* no valid device found */
1041
1042 if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
1043 rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7;
1044 snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
1045 if (rev == 0x80) {
1046 unsigned char tmp = snd_cs4231_in(chip, 23);
1047 snd_cs4231_out(chip, 23, ~tmp);
1048 if (snd_cs4231_in(chip, 23) != tmp)
1049 chip->hardware = CS4231_HW_AD1845;
1050 else
1051 chip->hardware = CS4231_HW_CS4231;
1052 } else if (rev == 0xa0) {
1053 chip->hardware = CS4231_HW_CS4231A;
1054 } else if (rev == 0xa2) {
1055 chip->hardware = CS4231_HW_CS4232;
1056 } else if (rev == 0xb2) {
1057 chip->hardware = CS4231_HW_CS4232A;
1058 } else if (rev == 0x83) {
1059 chip->hardware = CS4231_HW_CS4236;
1060 } else if (rev == 0x03) {
1061 chip->hardware = CS4231_HW_CS4236B;
1062 } else {
1063 snd_printk("unknown CS chip with version 0x%x\n", rev);
1064 return -ENODEV; /* unknown CS4231 chip? */
1065 }
1066 }
1067 spin_lock_irqsave(&chip->reg_lock, flags);
1068 cs4231_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */
1069 cs4231_outb(chip, CS4231P(STATUS), 0);
1070 mb();
1071 spin_unlock_irqrestore(&chip->reg_lock, flags);
1072
1073 chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
1074 switch (chip->hardware) {
1075 case CS4231_HW_INTERWAVE:
1076 chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
1077 break;
1078 case CS4231_HW_CS4235:
1079 case CS4231_HW_CS4236B:
1080 case CS4231_HW_CS4237B:
1081 case CS4231_HW_CS4238B:
1082 case CS4231_HW_CS4239:
1083 if (hw == CS4231_HW_DETECT3)
1084 chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
1085 else
1086 chip->hardware = CS4231_HW_CS4236;
1087 break;
1088 }
1089
1090 chip->image[CS4231_IFACE_CTRL] =
1091 (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
1092 (chip->single_dma ? CS4231_SINGLE_DMA : 0);
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +02001093 if (chip->hardware != CS4231_HW_OPTI93X) {
1094 chip->image[CS4231_ALT_FEATURE_1] = 0x80;
1095 chip->image[CS4231_ALT_FEATURE_2] =
1096 chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
1097 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 ptr = (unsigned char *) &chip->image;
1099 snd_cs4231_mce_down(chip);
1100 spin_lock_irqsave(&chip->reg_lock, flags);
1101 for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */
1102 snd_cs4231_out(chip, i, *ptr++);
1103 spin_unlock_irqrestore(&chip->reg_lock, flags);
1104 snd_cs4231_mce_up(chip);
1105 snd_cs4231_mce_down(chip);
1106
1107 mdelay(2);
1108
1109 /* ok.. try check hardware version for CS4236+ chips */
1110 if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
1111 if (chip->hardware == CS4231_HW_CS4236B) {
1112 rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
1113 snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
1114 id = snd_cs4236_ext_in(chip, CS4236_VERSION);
1115 snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
1116 snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
1117 if ((id & 0x1f) == 0x1d) { /* CS4235 */
1118 chip->hardware = CS4231_HW_CS4235;
1119 switch (id >> 5) {
1120 case 4:
1121 case 5:
1122 case 6:
1123 break;
1124 default:
1125 snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
1126 }
1127 } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */
1128 switch (id >> 5) {
1129 case 4:
1130 case 5:
1131 case 6:
1132 case 7:
1133 chip->hardware = CS4231_HW_CS4236B;
1134 break;
1135 default:
1136 snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
1137 }
1138 } else if ((id & 0x1f) == 0x08) { /* CS4237B */
1139 chip->hardware = CS4231_HW_CS4237B;
1140 switch (id >> 5) {
1141 case 4:
1142 case 5:
1143 case 6:
1144 case 7:
1145 break;
1146 default:
1147 snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
1148 }
1149 } else if ((id & 0x1f) == 0x09) { /* CS4238B */
1150 chip->hardware = CS4231_HW_CS4238B;
1151 switch (id >> 5) {
1152 case 5:
1153 case 6:
1154 case 7:
1155 break;
1156 default:
1157 snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
1158 }
1159 } else if ((id & 0x1f) == 0x1e) { /* CS4239 */
1160 chip->hardware = CS4231_HW_CS4239;
1161 switch (id >> 5) {
1162 case 4:
1163 case 5:
1164 case 6:
1165 break;
1166 default:
1167 snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
1168 }
1169 } else {
1170 snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
1171 }
1172 }
1173 }
1174 return 0; /* all things are ok.. */
1175}
1176
1177/*
1178
1179 */
1180
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001181static struct snd_pcm_hardware snd_cs4231_playback =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182{
1183 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1184 SNDRV_PCM_INFO_MMAP_VALID |
1185 SNDRV_PCM_INFO_RESUME |
1186 SNDRV_PCM_INFO_SYNC_START),
1187 .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
1188 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
1189 .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
1190 .rate_min = 5510,
1191 .rate_max = 48000,
1192 .channels_min = 1,
1193 .channels_max = 2,
1194 .buffer_bytes_max = (128*1024),
1195 .period_bytes_min = 64,
1196 .period_bytes_max = (128*1024),
1197 .periods_min = 1,
1198 .periods_max = 1024,
1199 .fifo_size = 0,
1200};
1201
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001202static struct snd_pcm_hardware snd_cs4231_capture =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203{
1204 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1205 SNDRV_PCM_INFO_MMAP_VALID |
1206 SNDRV_PCM_INFO_RESUME |
1207 SNDRV_PCM_INFO_SYNC_START),
1208 .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
1209 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
1210 .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
1211 .rate_min = 5510,
1212 .rate_max = 48000,
1213 .channels_min = 1,
1214 .channels_max = 2,
1215 .buffer_bytes_max = (128*1024),
1216 .period_bytes_min = 64,
1217 .period_bytes_max = (128*1024),
1218 .periods_min = 1,
1219 .periods_max = 1024,
1220 .fifo_size = 0,
1221};
1222
1223/*
1224
1225 */
1226
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001227static int snd_cs4231_playback_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001229 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
1230 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 int err;
1232
1233 runtime->hw = snd_cs4231_playback;
1234
1235 /* hardware bug in InterWave chipset */
1236 if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3)
1237 runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
1238
1239 /* hardware limitation of cheap chips */
1240 if (chip->hardware == CS4231_HW_CS4235 ||
1241 chip->hardware == CS4231_HW_CS4239)
1242 runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
1243
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
1245 snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
1246
1247 if (chip->claim_dma) {
1248 if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
1249 return err;
1250 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
1252 if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 if (chip->release_dma)
1254 chip->release_dma(chip, chip->dma_private_data, chip->dma1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 snd_free_pages(runtime->dma_area, runtime->dma_bytes);
1256 return err;
1257 }
1258 chip->playback_substream = substream;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 snd_pcm_set_sync(substream);
1260 chip->rate_constraint(runtime);
1261 return 0;
1262}
1263
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001264static int snd_cs4231_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001266 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
1267 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 int err;
1269
1270 runtime->hw = snd_cs4231_capture;
1271
1272 /* hardware limitation of cheap chips */
1273 if (chip->hardware == CS4231_HW_CS4235 ||
1274 chip->hardware == CS4231_HW_CS4239)
1275 runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
1276
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
1278 snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
1279
1280 if (chip->claim_dma) {
1281 if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
1282 return err;
1283 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
1285 if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 if (chip->release_dma)
1287 chip->release_dma(chip, chip->dma_private_data, chip->dma2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 snd_free_pages(runtime->dma_area, runtime->dma_bytes);
1289 return err;
1290 }
1291 chip->capture_substream = substream;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 snd_pcm_set_sync(substream);
1293 chip->rate_constraint(runtime);
1294 return 0;
1295}
1296
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001297static int snd_cs4231_playback_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001299 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
1301 chip->playback_substream = NULL;
1302 snd_cs4231_close(chip, CS4231_MODE_PLAY);
1303 return 0;
1304}
1305
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001306static int snd_cs4231_capture_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001308 struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
1310 chip->capture_substream = NULL;
1311 snd_cs4231_close(chip, CS4231_MODE_RECORD);
1312 return 0;
1313}
1314
1315#ifdef CONFIG_PM
1316
1317/* lowlevel suspend callback for CS4231 */
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001318static void snd_cs4231_suspend(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319{
1320 int reg;
1321 unsigned long flags;
1322
Takashi Iwai7bb35e202005-11-17 17:00:17 +01001323 snd_pcm_suspend_all(chip->pcm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 spin_lock_irqsave(&chip->reg_lock, flags);
1325 for (reg = 0; reg < 32; reg++)
1326 chip->image[reg] = snd_cs4231_in(chip, reg);
1327 spin_unlock_irqrestore(&chip->reg_lock, flags);
1328}
1329
1330/* lowlevel resume callback for CS4231 */
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001331static void snd_cs4231_resume(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332{
1333 int reg;
1334 unsigned long flags;
Takashi Iwaia2c855b2005-11-18 18:52:39 +01001335 /* int timeout; */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
1337 snd_cs4231_mce_up(chip);
1338 spin_lock_irqsave(&chip->reg_lock, flags);
1339 for (reg = 0; reg < 32; reg++) {
1340 switch (reg) {
1341 case CS4231_VERSION:
1342 break;
1343 default:
1344 snd_cs4231_out(chip, reg, chip->image[reg]);
1345 break;
1346 }
1347 }
1348 spin_unlock_irqrestore(&chip->reg_lock, flags);
Takashi Iwaifa55f832005-11-17 17:48:30 +01001349#if 1
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 snd_cs4231_mce_down(chip);
1351#else
1352 /* The following is a workaround to avoid freeze after resume on TP600E.
1353 This is the first half of copy of snd_cs4231_mce_down(), but doesn't
1354 include rescheduling. -- iwai
1355 */
1356 snd_cs4231_busy_wait(chip);
1357 spin_lock_irqsave(&chip->reg_lock, flags);
1358 chip->mce_bit &= ~CS4231_MCE;
1359 timeout = cs4231_inb(chip, CS4231P(REGSEL));
1360 cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
1361 spin_unlock_irqrestore(&chip->reg_lock, flags);
1362 if (timeout == 0x80)
1363 snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
1364 if ((timeout & CS4231_MCE) == 0 ||
1365 !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
1366 return;
1367 }
1368 snd_cs4231_busy_wait(chip);
1369#endif
1370}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371#endif /* CONFIG_PM */
1372
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001373static int snd_cs4231_free(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374{
Takashi Iwaib1d57762005-10-10 11:56:31 +02001375 release_and_free_resource(chip->res_port);
1376 release_and_free_resource(chip->res_cport);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 if (chip->irq >= 0) {
1378 disable_irq(chip->irq);
1379 if (!(chip->hwshare & CS4231_HWSHARE_IRQ))
1380 free_irq(chip->irq, (void *) chip);
1381 }
1382 if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) {
1383 snd_dma_disable(chip->dma1);
1384 free_dma(chip->dma1);
1385 }
1386 if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
1387 snd_dma_disable(chip->dma2);
1388 free_dma(chip->dma2);
1389 }
1390 if (chip->timer)
1391 snd_device_free(chip->card, chip->timer);
1392 kfree(chip);
1393 return 0;
1394}
1395
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001396static int snd_cs4231_dev_free(struct snd_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001398 struct snd_cs4231 *chip = device->device_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 return snd_cs4231_free(chip);
1400}
1401
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001402const char *snd_cs4231_chip_id(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403{
1404 switch (chip->hardware) {
1405 case CS4231_HW_CS4231: return "CS4231";
1406 case CS4231_HW_CS4231A: return "CS4231A";
1407 case CS4231_HW_CS4232: return "CS4232";
1408 case CS4231_HW_CS4232A: return "CS4232A";
1409 case CS4231_HW_CS4235: return "CS4235";
1410 case CS4231_HW_CS4236: return "CS4236";
1411 case CS4231_HW_CS4236B: return "CS4236B";
1412 case CS4231_HW_CS4237B: return "CS4237B";
1413 case CS4231_HW_CS4238B: return "CS4238B";
1414 case CS4231_HW_CS4239: return "CS4239";
1415 case CS4231_HW_INTERWAVE: return "AMD InterWave";
1416 case CS4231_HW_OPL3SA2: return chip->card->shortname;
1417 case CS4231_HW_AD1845: return "AD1845";
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +02001418 case CS4231_HW_OPTI93X: return "OPTi 93x";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 default: return "???";
1420 }
1421}
1422
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001423static int snd_cs4231_new(struct snd_card *card,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 unsigned short hardware,
1425 unsigned short hwshare,
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001426 struct snd_cs4231 ** rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001428 struct snd_cs4231 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429
1430 *rchip = NULL;
Takashi Iwai9e76a762005-09-09 14:21:17 +02001431 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 if (chip == NULL)
1433 return -ENOMEM;
1434 chip->hardware = hardware;
1435 chip->hwshare = hwshare;
1436
1437 spin_lock_init(&chip->reg_lock);
Ingo Molnar8b7547f2006-01-16 16:33:08 +01001438 mutex_init(&chip->mce_mutex);
1439 mutex_init(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 chip->card = card;
1441 chip->rate_constraint = snd_cs4231_xrate;
1442 chip->set_playback_format = snd_cs4231_playback_format;
1443 chip->set_capture_format = snd_cs4231_capture_format;
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +02001444 if (chip->hardware == CS4231_HW_OPTI93X)
1445 memcpy(&chip->image, &snd_opti93x_original_image,
1446 sizeof(snd_opti93x_original_image));
1447 else
1448 memcpy(&chip->image, &snd_cs4231_original_image,
1449 sizeof(snd_cs4231_original_image));
1450
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 *rchip = chip;
1452 return 0;
1453}
1454
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001455int snd_cs4231_create(struct snd_card *card,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 unsigned long port,
1457 unsigned long cport,
1458 int irq, int dma1, int dma2,
1459 unsigned short hardware,
1460 unsigned short hwshare,
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001461 struct snd_cs4231 ** rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001463 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 .dev_free = snd_cs4231_dev_free,
1465 };
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001466 struct snd_cs4231 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 int err;
1468
1469 err = snd_cs4231_new(card, hardware, hwshare, &chip);
1470 if (err < 0)
1471 return err;
1472
1473 chip->irq = -1;
1474 chip->dma1 = -1;
1475 chip->dma2 = -1;
1476
1477 if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) {
1478 snd_printk(KERN_ERR "cs4231: can't grab port 0x%lx\n", port);
1479 snd_cs4231_free(chip);
1480 return -EBUSY;
1481 }
1482 chip->port = port;
1483 if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) {
1484 snd_printk(KERN_ERR "cs4231: can't grab control port 0x%lx\n", cport);
1485 snd_cs4231_free(chip);
1486 return -ENODEV;
1487 }
1488 chip->cport = cport;
Thomas Gleixner65ca68b2006-07-01 19:29:46 -07001489 if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, IRQF_DISABLED, "CS4231", (void *) chip)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 snd_printk(KERN_ERR "cs4231: can't grab IRQ %d\n", irq);
1491 snd_cs4231_free(chip);
1492 return -EBUSY;
1493 }
1494 chip->irq = irq;
1495 if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) {
1496 snd_printk(KERN_ERR "cs4231: can't grab DMA1 %d\n", dma1);
1497 snd_cs4231_free(chip);
1498 return -EBUSY;
1499 }
1500 chip->dma1 = dma1;
1501 if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) {
1502 snd_printk(KERN_ERR "cs4231: can't grab DMA2 %d\n", dma2);
1503 snd_cs4231_free(chip);
1504 return -EBUSY;
1505 }
1506 if (dma1 == dma2 || dma2 < 0) {
1507 chip->single_dma = 1;
1508 chip->dma2 = chip->dma1;
1509 } else
1510 chip->dma2 = dma2;
1511
1512 /* global setup */
1513 if (snd_cs4231_probe(chip) < 0) {
1514 snd_cs4231_free(chip);
1515 return -ENODEV;
1516 }
1517 snd_cs4231_init(chip);
1518
Takashi Iwaia9824c82005-11-17 17:51:00 +01001519#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 if (chip->hardware & CS4231_HW_CS4232_MASK) {
1521 if (chip->res_cport == NULL)
1522 snd_printk("CS4232 control port features are not accessible\n");
1523 }
Takashi Iwaia9824c82005-11-17 17:51:00 +01001524#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
1526 /* Register device */
1527 if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
1528 snd_cs4231_free(chip);
1529 return err;
1530 }
1531
1532#ifdef CONFIG_PM
1533 /* Power Management */
1534 chip->suspend = snd_cs4231_suspend;
1535 chip->resume = snd_cs4231_resume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536#endif
1537
1538 *rchip = chip;
1539 return 0;
1540}
1541
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001542static struct snd_pcm_ops snd_cs4231_playback_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 .open = snd_cs4231_playback_open,
1544 .close = snd_cs4231_playback_close,
1545 .ioctl = snd_pcm_lib_ioctl,
1546 .hw_params = snd_cs4231_playback_hw_params,
1547 .hw_free = snd_cs4231_playback_hw_free,
1548 .prepare = snd_cs4231_playback_prepare,
1549 .trigger = snd_cs4231_trigger,
1550 .pointer = snd_cs4231_playback_pointer,
1551};
1552
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001553static struct snd_pcm_ops snd_cs4231_capture_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 .open = snd_cs4231_capture_open,
1555 .close = snd_cs4231_capture_close,
1556 .ioctl = snd_pcm_lib_ioctl,
1557 .hw_params = snd_cs4231_capture_hw_params,
1558 .hw_free = snd_cs4231_capture_hw_free,
1559 .prepare = snd_cs4231_capture_prepare,
1560 .trigger = snd_cs4231_trigger,
1561 .pointer = snd_cs4231_capture_pointer,
1562};
1563
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001564int snd_cs4231_pcm(struct snd_cs4231 *chip, int device, struct snd_pcm **rpcm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001566 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 int err;
1568
1569 if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0)
1570 return err;
1571
1572 spin_lock_init(&chip->reg_lock);
Ingo Molnar8b7547f2006-01-16 16:33:08 +01001573 mutex_init(&chip->mce_mutex);
1574 mutex_init(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
1576 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops);
1577 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops);
1578
1579 /* global setup */
1580 pcm->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 pcm->info_flags = 0;
1582 if (chip->single_dma)
1583 pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
1584 if (chip->hardware != CS4231_HW_INTERWAVE)
1585 pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
1586 strcpy(pcm->name, snd_cs4231_chip_id(chip));
1587
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
1589 snd_dma_isa_data(),
1590 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
1592 chip->pcm = pcm;
1593 if (rpcm)
1594 *rpcm = pcm;
1595 return 0;
1596}
1597
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001598static void snd_cs4231_timer_free(struct snd_timer *timer)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001600 struct snd_cs4231 *chip = timer->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 chip->timer = NULL;
1602}
1603
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001604int snd_cs4231_timer(struct snd_cs4231 *chip, int device, struct snd_timer **rtimer)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001606 struct snd_timer *timer;
1607 struct snd_timer_id tid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 int err;
1609
1610 /* Timer initialization */
1611 tid.dev_class = SNDRV_TIMER_CLASS_CARD;
1612 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
1613 tid.card = chip->card->number;
1614 tid.device = device;
1615 tid.subdevice = 0;
1616 if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
1617 return err;
1618 strcpy(timer->name, snd_cs4231_chip_id(chip));
1619 timer->private_data = chip;
1620 timer->private_free = snd_cs4231_timer_free;
1621 timer->hw = snd_cs4231_timer_table;
1622 chip->timer = timer;
1623 if (rtimer)
1624 *rtimer = timer;
1625 return 0;
1626}
1627
1628/*
1629 * MIXER part
1630 */
1631
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001632static int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633{
1634 static char *texts[4] = {
1635 "Line", "Aux", "Mic", "Mix"
1636 };
1637 static char *opl3sa_texts[4] = {
1638 "Line", "CD", "Mic", "Mix"
1639 };
1640 static char *gusmax_texts[4] = {
1641 "Line", "Synth", "Mic", "Mix"
1642 };
1643 char **ptexts = texts;
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001644 struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
1646 snd_assert(chip->card != NULL, return -EINVAL);
1647 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1648 uinfo->count = 2;
1649 uinfo->value.enumerated.items = 4;
1650 if (uinfo->value.enumerated.item > 3)
1651 uinfo->value.enumerated.item = 3;
1652 if (!strcmp(chip->card->driver, "GUS MAX"))
1653 ptexts = gusmax_texts;
1654 switch (chip->hardware) {
1655 case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break;
1656 case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break;
1657 }
1658 strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
1659 return 0;
1660}
1661
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001662static int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001664 struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 unsigned long flags;
1666
1667 spin_lock_irqsave(&chip->reg_lock, flags);
1668 ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
1669 ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
1670 spin_unlock_irqrestore(&chip->reg_lock, flags);
1671 return 0;
1672}
1673
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001674static int snd_cs4231_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001676 struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 unsigned long flags;
1678 unsigned short left, right;
1679 int change;
1680
1681 if (ucontrol->value.enumerated.item[0] > 3 ||
1682 ucontrol->value.enumerated.item[1] > 3)
1683 return -EINVAL;
1684 left = ucontrol->value.enumerated.item[0] << 6;
1685 right = ucontrol->value.enumerated.item[1] << 6;
1686 spin_lock_irqsave(&chip->reg_lock, flags);
1687 left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
1688 right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
1689 change = left != chip->image[CS4231_LEFT_INPUT] ||
1690 right != chip->image[CS4231_RIGHT_INPUT];
1691 snd_cs4231_out(chip, CS4231_LEFT_INPUT, left);
1692 snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right);
1693 spin_unlock_irqrestore(&chip->reg_lock, flags);
1694 return change;
1695}
1696
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001697int snd_cs4231_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698{
1699 int mask = (kcontrol->private_value >> 16) & 0xff;
1700
1701 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1702 uinfo->count = 1;
1703 uinfo->value.integer.min = 0;
1704 uinfo->value.integer.max = mask;
1705 return 0;
1706}
1707
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001708int snd_cs4231_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001710 struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 unsigned long flags;
1712 int reg = kcontrol->private_value & 0xff;
1713 int shift = (kcontrol->private_value >> 8) & 0xff;
1714 int mask = (kcontrol->private_value >> 16) & 0xff;
1715 int invert = (kcontrol->private_value >> 24) & 0xff;
1716
1717 spin_lock_irqsave(&chip->reg_lock, flags);
1718 ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
1719 spin_unlock_irqrestore(&chip->reg_lock, flags);
1720 if (invert)
1721 ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
1722 return 0;
1723}
1724
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001725int snd_cs4231_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001727 struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 unsigned long flags;
1729 int reg = kcontrol->private_value & 0xff;
1730 int shift = (kcontrol->private_value >> 8) & 0xff;
1731 int mask = (kcontrol->private_value >> 16) & 0xff;
1732 int invert = (kcontrol->private_value >> 24) & 0xff;
1733 int change;
1734 unsigned short val;
1735
1736 val = (ucontrol->value.integer.value[0] & mask);
1737 if (invert)
1738 val = mask - val;
1739 val <<= shift;
1740 spin_lock_irqsave(&chip->reg_lock, flags);
1741 val = (chip->image[reg] & ~(mask << shift)) | val;
1742 change = val != chip->image[reg];
1743 snd_cs4231_out(chip, reg, val);
1744 spin_unlock_irqrestore(&chip->reg_lock, flags);
1745 return change;
1746}
1747
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001748int snd_cs4231_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749{
1750 int mask = (kcontrol->private_value >> 24) & 0xff;
1751
1752 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1753 uinfo->count = 2;
1754 uinfo->value.integer.min = 0;
1755 uinfo->value.integer.max = mask;
1756 return 0;
1757}
1758
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001759int snd_cs4231_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001761 struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 unsigned long flags;
1763 int left_reg = kcontrol->private_value & 0xff;
1764 int right_reg = (kcontrol->private_value >> 8) & 0xff;
1765 int shift_left = (kcontrol->private_value >> 16) & 0x07;
1766 int shift_right = (kcontrol->private_value >> 19) & 0x07;
1767 int mask = (kcontrol->private_value >> 24) & 0xff;
1768 int invert = (kcontrol->private_value >> 22) & 1;
1769
1770 spin_lock_irqsave(&chip->reg_lock, flags);
1771 ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
1772 ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
1773 spin_unlock_irqrestore(&chip->reg_lock, flags);
1774 if (invert) {
1775 ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
1776 ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
1777 }
1778 return 0;
1779}
1780
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001781int snd_cs4231_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001783 struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 unsigned long flags;
1785 int left_reg = kcontrol->private_value & 0xff;
1786 int right_reg = (kcontrol->private_value >> 8) & 0xff;
1787 int shift_left = (kcontrol->private_value >> 16) & 0x07;
1788 int shift_right = (kcontrol->private_value >> 19) & 0x07;
1789 int mask = (kcontrol->private_value >> 24) & 0xff;
1790 int invert = (kcontrol->private_value >> 22) & 1;
1791 int change;
1792 unsigned short val1, val2;
1793
1794 val1 = ucontrol->value.integer.value[0] & mask;
1795 val2 = ucontrol->value.integer.value[1] & mask;
1796 if (invert) {
1797 val1 = mask - val1;
1798 val2 = mask - val2;
1799 }
1800 val1 <<= shift_left;
1801 val2 <<= shift_right;
1802 spin_lock_irqsave(&chip->reg_lock, flags);
1803 val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
1804 val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
1805 change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
1806 snd_cs4231_out(chip, left_reg, val1);
1807 snd_cs4231_out(chip, right_reg, val2);
1808 spin_unlock_irqrestore(&chip->reg_lock, flags);
1809 return change;
1810}
1811
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001812static struct snd_kcontrol_new snd_cs4231_controls[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
1814CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
1815CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
1816CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
1817CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
1818CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
1819CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
1820CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
1821CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1),
1822CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
1823CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1),
1824CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
1825CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
1826{
1827 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1828 .name = "Capture Source",
1829 .info = snd_cs4231_info_mux,
1830 .get = snd_cs4231_get_mux,
1831 .put = snd_cs4231_put_mux,
1832},
1833CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
1834CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
1835CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1)
1836};
1837
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +02001838static struct snd_kcontrol_new snd_opti93x_controls[] = {
1839CS4231_DOUBLE("Master Playback Switch", 0,
1840 OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
1841CS4231_DOUBLE("Master Playback Volume", 0,
1842 OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
1843CS4231_DOUBLE("PCM Playback Switch", 0,
1844 CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
1845CS4231_DOUBLE("PCM Playback Volume", 0,
1846 CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1),
1847CS4231_DOUBLE("FM Playback Switch", 0,
1848 CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
1849CS4231_DOUBLE("FM Playback Volume", 0,
1850 CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1),
1851CS4231_DOUBLE("Line Playback Switch", 0,
1852 CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
1853CS4231_DOUBLE("Line Playback Volume", 0,
1854 CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1),
1855CS4231_DOUBLE("Mic Playback Switch", 0,
1856 OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
1857CS4231_DOUBLE("Mic Playback Volume", 0,
1858 OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
1859CS4231_DOUBLE("Mic Boost", 0,
1860 CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
1861CS4231_DOUBLE("CD Playback Switch", 0,
1862 CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
1863CS4231_DOUBLE("CD Playback Volume", 0,
1864 CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1),
1865CS4231_DOUBLE("Aux Playback Switch", 0,
1866 OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
1867CS4231_DOUBLE("Aux Playback Volume", 0,
1868 OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
1869CS4231_DOUBLE("Capture Volume", 0,
1870 CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
1871{
1872 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1873 .name = "Capture Source",
1874 .info = snd_cs4231_info_mux,
1875 .get = snd_cs4231_get_mux,
1876 .put = snd_cs4231_put_mux,
1877}
1878};
1879
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001880int snd_cs4231_mixer(struct snd_cs4231 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881{
Takashi Iwaiba2375a2005-11-17 14:30:42 +01001882 struct snd_card *card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 unsigned int idx;
1884 int err;
1885
1886 snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
1887
1888 card = chip->card;
1889
1890 strcpy(card->mixername, chip->pcm->name);
1891
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +02001892 if (chip->hardware == CS4231_HW_OPTI93X)
1893 for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
1894 err = snd_ctl_add(card,
1895 snd_ctl_new1(&snd_opti93x_controls[idx],
1896 chip));
1897 if (err < 0)
1898 return err;
1899 }
1900 else
1901 for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
1902 err = snd_ctl_add(card,
1903 snd_ctl_new1(&snd_cs4231_controls[idx],
1904 chip));
1905 if (err < 0)
1906 return err;
1907 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 return 0;
1909}
1910
1911EXPORT_SYMBOL(snd_cs4231_out);
1912EXPORT_SYMBOL(snd_cs4231_in);
1913EXPORT_SYMBOL(snd_cs4236_ext_out);
1914EXPORT_SYMBOL(snd_cs4236_ext_in);
1915EXPORT_SYMBOL(snd_cs4231_mce_up);
1916EXPORT_SYMBOL(snd_cs4231_mce_down);
Krzysztof Heltabf1f5a2008-06-09 23:07:28 +02001917EXPORT_SYMBOL(snd_cs4231_overrange);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918EXPORT_SYMBOL(snd_cs4231_interrupt);
1919EXPORT_SYMBOL(snd_cs4231_chip_id);
1920EXPORT_SYMBOL(snd_cs4231_create);
1921EXPORT_SYMBOL(snd_cs4231_pcm);
1922EXPORT_SYMBOL(snd_cs4231_mixer);
1923EXPORT_SYMBOL(snd_cs4231_timer);
1924EXPORT_SYMBOL(snd_cs4231_info_single);
1925EXPORT_SYMBOL(snd_cs4231_get_single);
1926EXPORT_SYMBOL(snd_cs4231_put_single);
1927EXPORT_SYMBOL(snd_cs4231_info_double);
1928EXPORT_SYMBOL(snd_cs4231_get_double);
1929EXPORT_SYMBOL(snd_cs4231_put_double);
1930
1931/*
1932 * INIT part
1933 */
1934
1935static int __init alsa_cs4231_init(void)
1936{
1937 return 0;
1938}
1939
1940static void __exit alsa_cs4231_exit(void)
1941{
1942}
1943
1944module_init(alsa_cs4231_init)
1945module_exit(alsa_cs4231_exit)