diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/isa/es1688 |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/isa/es1688')
-rw-r--r-- | sound/isa/es1688/Makefile | 11 | ||||
-rw-r--r-- | sound/isa/es1688/es1688.c | 204 | ||||
-rw-r--r-- | sound/isa/es1688/es1688_lib.c | 1062 |
3 files changed, 1277 insertions, 0 deletions
diff --git a/sound/isa/es1688/Makefile b/sound/isa/es1688/Makefile new file mode 100644 index 000000000000..501c8bf903af --- /dev/null +++ b/sound/isa/es1688/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-es1688-lib-objs := es1688_lib.o | ||
7 | snd-es1688-objs := es1688.o | ||
8 | |||
9 | # Toplevel Module Dependency | ||
10 | obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o | ||
11 | obj-$(CONFIG_SND_GUSEXTREME) += snd-es1688-lib.o | ||
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c new file mode 100644 index 000000000000..c5eaec087b46 --- /dev/null +++ b/sound/isa/es1688/es1688.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * Driver for generic ESS AudioDrive ESx688 soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
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 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/es1688.h> | ||
30 | #include <sound/mpu401.h> | ||
31 | #include <sound/opl3.h> | ||
32 | #define SNDRV_LEGACY_AUTO_PROBE | ||
33 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
34 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
35 | #include <sound/initval.h> | ||
36 | |||
37 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
38 | MODULE_DESCRIPTION("ESS ESx688 AudioDrive"); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," | ||
41 | "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102}," | ||
42 | "{ESS,ES688 AudioDrive,pnp:ESS6881}," | ||
43 | "{ESS,ES1688 AudioDrive,pnp:ESS1681}}"); | ||
44 | |||
45 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
46 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
47 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
48 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ | ||
49 | static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; | ||
50 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
51 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
52 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
53 | |||
54 | module_param_array(index, int, NULL, 0444); | ||
55 | MODULE_PARM_DESC(index, "Index value for ESx688 soundcard."); | ||
56 | module_param_array(id, charp, NULL, 0444); | ||
57 | MODULE_PARM_DESC(id, "ID string for ESx688 soundcard."); | ||
58 | module_param_array(enable, bool, NULL, 0444); | ||
59 | MODULE_PARM_DESC(enable, "Enable ESx688 soundcard."); | ||
60 | module_param_array(port, long, NULL, 0444); | ||
61 | MODULE_PARM_DESC(port, "Port # for ESx688 driver."); | ||
62 | module_param_array(mpu_port, long, NULL, 0444); | ||
63 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ESx688 driver."); | ||
64 | module_param_array(irq, int, NULL, 0444); | ||
65 | MODULE_PARM_DESC(irq, "IRQ # for ESx688 driver."); | ||
66 | module_param_array(mpu_irq, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ESx688 driver."); | ||
68 | module_param_array(dma8, int, NULL, 0444); | ||
69 | MODULE_PARM_DESC(dma8, "8-bit DMA # for ESx688 driver."); | ||
70 | |||
71 | static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
72 | |||
73 | |||
74 | static int __init snd_audiodrive_probe(int dev) | ||
75 | { | ||
76 | static int possible_irqs[] = {5, 9, 10, 7, -1}; | ||
77 | static int possible_dmas[] = {1, 3, 0, -1}; | ||
78 | int xirq, xdma, xmpu_irq; | ||
79 | snd_card_t *card; | ||
80 | es1688_t *chip; | ||
81 | opl3_t *opl3; | ||
82 | snd_pcm_t *pcm; | ||
83 | int err; | ||
84 | |||
85 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
86 | if (card == NULL) | ||
87 | return -ENOMEM; | ||
88 | |||
89 | xirq = irq[dev]; | ||
90 | if (xirq == SNDRV_AUTO_IRQ) { | ||
91 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
92 | snd_card_free(card); | ||
93 | snd_printk("unable to find a free IRQ\n"); | ||
94 | return -EBUSY; | ||
95 | } | ||
96 | } | ||
97 | xmpu_irq = mpu_irq[dev]; | ||
98 | xdma = dma8[dev]; | ||
99 | if (xdma == SNDRV_AUTO_DMA) { | ||
100 | if ((xdma = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
101 | snd_card_free(card); | ||
102 | snd_printk("unable to find a free DMA\n"); | ||
103 | return -EBUSY; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | if ((err = snd_es1688_create(card, port[dev], mpu_port[dev], | ||
108 | xirq, xmpu_irq, xdma, | ||
109 | ES1688_HW_AUTO, &chip)) < 0) { | ||
110 | snd_card_free(card); | ||
111 | return err; | ||
112 | } | ||
113 | if ((err = snd_es1688_pcm(chip, 0, &pcm)) < 0) { | ||
114 | snd_card_free(card); | ||
115 | return err; | ||
116 | } | ||
117 | if ((err = snd_es1688_mixer(chip)) < 0) { | ||
118 | snd_card_free(card); | ||
119 | return err; | ||
120 | } | ||
121 | |||
122 | strcpy(card->driver, "ES1688"); | ||
123 | strcpy(card->shortname, pcm->name); | ||
124 | sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, xirq, xdma); | ||
125 | |||
126 | if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) { | ||
127 | printk(KERN_ERR "es1688: opl3 not detected at 0x%lx\n", chip->port); | ||
128 | } else { | ||
129 | if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
130 | snd_card_free(card); | ||
131 | return err; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | if (xmpu_irq >= 0 && xmpu_irq != SNDRV_AUTO_IRQ && chip->mpu_port > 0) { | ||
136 | if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, | ||
137 | chip->mpu_port, 0, | ||
138 | xmpu_irq, | ||
139 | SA_INTERRUPT, | ||
140 | NULL)) < 0) { | ||
141 | snd_card_free(card); | ||
142 | return err; | ||
143 | } | ||
144 | } | ||
145 | if ((err = snd_card_register(card)) < 0) { | ||
146 | snd_card_free(card); | ||
147 | return err; | ||
148 | } | ||
149 | snd_audiodrive_cards[dev] = card; | ||
150 | return 0; | ||
151 | |||
152 | } | ||
153 | |||
154 | static int __init snd_audiodrive_legacy_auto_probe(unsigned long xport) | ||
155 | { | ||
156 | static int dev; | ||
157 | int res; | ||
158 | |||
159 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
160 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
161 | continue; | ||
162 | port[dev] = xport; | ||
163 | res = snd_audiodrive_probe(dev); | ||
164 | if (res < 0) | ||
165 | port[dev] = SNDRV_AUTO_PORT; | ||
166 | return res; | ||
167 | } | ||
168 | return -ENODEV; | ||
169 | } | ||
170 | |||
171 | static int __init alsa_card_es1688_init(void) | ||
172 | { | ||
173 | static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; | ||
174 | int dev, cards = 0, i; | ||
175 | |||
176 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
177 | if (port[dev] == SNDRV_AUTO_PORT) | ||
178 | continue; | ||
179 | if (snd_audiodrive_probe(dev) >= 0) | ||
180 | cards++; | ||
181 | } | ||
182 | i = snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe); | ||
183 | if (i > 0) | ||
184 | cards += i; | ||
185 | |||
186 | if (!cards) { | ||
187 | #ifdef MODULE | ||
188 | printk(KERN_ERR "ESS AudioDrive ES1688 soundcard not found or device busy\n"); | ||
189 | #endif | ||
190 | return -ENODEV; | ||
191 | } | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static void __exit alsa_card_es1688_exit(void) | ||
196 | { | ||
197 | int idx; | ||
198 | |||
199 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
200 | snd_card_free(snd_audiodrive_cards[idx]); | ||
201 | } | ||
202 | |||
203 | module_init(alsa_card_es1688_init) | ||
204 | module_exit(alsa_card_es1688_exit) | ||
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c new file mode 100644 index 000000000000..17f68d07d9b2 --- /dev/null +++ b/sound/isa/es1688/es1688_lib.c | |||
@@ -0,0 +1,1062 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of ESS ES1688/688/488 chip | ||
4 | * | ||
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 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/es1688.h> | ||
30 | #include <sound/initval.h> | ||
31 | |||
32 | #include <asm/io.h> | ||
33 | #include <asm/dma.h> | ||
34 | |||
35 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
36 | MODULE_DESCRIPTION("ESS ESx688 lowlevel module"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | |||
39 | static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val) | ||
40 | { | ||
41 | int i; | ||
42 | |||
43 | for (i = 10000; i; i--) | ||
44 | if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) { | ||
45 | outb(val, ES1688P(chip, COMMAND)); | ||
46 | return 1; | ||
47 | } | ||
48 | #ifdef CONFIG_SND_DEBUG | ||
49 | printk("snd_es1688_dsp_command: timeout (0x%x)\n", val); | ||
50 | #endif | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static int snd_es1688_dsp_get_byte(es1688_t *chip) | ||
55 | { | ||
56 | int i; | ||
57 | |||
58 | for (i = 1000; i; i--) | ||
59 | if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) | ||
60 | return inb(ES1688P(chip, READ)); | ||
61 | snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL))); | ||
62 | return -ENODEV; | ||
63 | } | ||
64 | |||
65 | static int snd_es1688_write(es1688_t *chip, | ||
66 | unsigned char reg, unsigned char data) | ||
67 | { | ||
68 | if (!snd_es1688_dsp_command(chip, reg)) | ||
69 | return 0; | ||
70 | return snd_es1688_dsp_command(chip, data); | ||
71 | } | ||
72 | |||
73 | static int snd_es1688_read(es1688_t *chip, unsigned char reg) | ||
74 | { | ||
75 | /* Read a byte from an extended mode register of ES1688 */ | ||
76 | if (!snd_es1688_dsp_command(chip, 0xc0)) | ||
77 | return -1; | ||
78 | if (!snd_es1688_dsp_command(chip, reg)) | ||
79 | return -1; | ||
80 | return snd_es1688_dsp_get_byte(chip); | ||
81 | } | ||
82 | |||
83 | void snd_es1688_mixer_write(es1688_t *chip, | ||
84 | unsigned char reg, unsigned char data) | ||
85 | { | ||
86 | outb(reg, ES1688P(chip, MIXER_ADDR)); | ||
87 | udelay(10); | ||
88 | outb(data, ES1688P(chip, MIXER_DATA)); | ||
89 | udelay(10); | ||
90 | } | ||
91 | |||
92 | static unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg) | ||
93 | { | ||
94 | unsigned char result; | ||
95 | |||
96 | outb(reg, ES1688P(chip, MIXER_ADDR)); | ||
97 | udelay(10); | ||
98 | result = inb(ES1688P(chip, MIXER_DATA)); | ||
99 | udelay(10); | ||
100 | return result; | ||
101 | } | ||
102 | |||
103 | static int snd_es1688_reset(es1688_t *chip) | ||
104 | { | ||
105 | int i; | ||
106 | |||
107 | outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */ | ||
108 | udelay(10); | ||
109 | outb(0, ES1688P(chip, RESET)); | ||
110 | udelay(30); | ||
111 | for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++); | ||
112 | if (inb(ES1688P(chip, READ)) != 0xaa) { | ||
113 | snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port); | ||
114 | return -ENODEV; | ||
115 | } | ||
116 | snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */ | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static int snd_es1688_probe(es1688_t *chip) | ||
121 | { | ||
122 | unsigned long flags; | ||
123 | unsigned short major, minor, hw; | ||
124 | int i; | ||
125 | |||
126 | /* | ||
127 | * initialization sequence | ||
128 | */ | ||
129 | |||
130 | spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */ | ||
131 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
132 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
133 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
134 | inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ | ||
135 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
136 | inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ | ||
137 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
138 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
139 | inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ | ||
140 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
141 | inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */ | ||
142 | |||
143 | if (snd_es1688_reset(chip) < 0) { | ||
144 | snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ))); | ||
145 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
146 | return -ENODEV; | ||
147 | } | ||
148 | snd_es1688_dsp_command(chip, 0xe7); /* return identification */ | ||
149 | |||
150 | for (i = 1000, major = minor = 0; i; i--) { | ||
151 | if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) { | ||
152 | if (major == 0) { | ||
153 | major = inb(ES1688P(chip, READ)); | ||
154 | } else { | ||
155 | minor = inb(ES1688P(chip, READ)); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
161 | |||
162 | snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor); | ||
163 | |||
164 | chip->version = (major << 8) | minor; | ||
165 | if (!chip->version) | ||
166 | return -ENODEV; /* probably SB */ | ||
167 | |||
168 | hw = ES1688_HW_AUTO; | ||
169 | switch (chip->version & 0xfff0) { | ||
170 | case 0x4880: | ||
171 | snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port); | ||
172 | return -ENODEV; | ||
173 | case 0x6880: | ||
174 | hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688; | ||
175 | break; | ||
176 | default: | ||
177 | snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version); | ||
178 | return -ENODEV; | ||
179 | } | ||
180 | |||
181 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
182 | snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ | ||
183 | snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ | ||
184 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
185 | |||
186 | /* enable joystick, but disable OPL3 */ | ||
187 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
188 | snd_es1688_mixer_write(chip, 0x40, 0x01); | ||
189 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int snd_es1688_init(es1688_t * chip, int enable) | ||
195 | { | ||
196 | static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1}; | ||
197 | unsigned long flags; | ||
198 | int cfg, irq_bits, dma, dma_bits, tmp, tmp1; | ||
199 | |||
200 | /* ok.. setup MPU-401 port and joystick and OPL3 */ | ||
201 | cfg = 0x01; /* enable joystick, but disable OPL3 */ | ||
202 | if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) { | ||
203 | tmp = (chip->mpu_port & 0x0f0) >> 4; | ||
204 | if (tmp <= 3) { | ||
205 | switch (chip->mpu_irq) { | ||
206 | case 9: | ||
207 | tmp1 = 4; | ||
208 | break; | ||
209 | case 5: | ||
210 | tmp1 = 5; | ||
211 | break; | ||
212 | case 7: | ||
213 | tmp1 = 6; | ||
214 | break; | ||
215 | case 10: | ||
216 | tmp1 = 7; | ||
217 | break; | ||
218 | default: | ||
219 | tmp1 = 0; | ||
220 | } | ||
221 | if (tmp1) { | ||
222 | cfg |= (tmp << 3) | (tmp1 << 5); | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | #if 0 | ||
227 | snd_printk("mpu cfg = 0x%x\n", cfg); | ||
228 | #endif | ||
229 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
230 | snd_es1688_mixer_write(chip, 0x40, cfg); | ||
231 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
232 | /* --- */ | ||
233 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
234 | snd_es1688_read(chip, 0xb1); | ||
235 | snd_es1688_read(chip, 0xb2); | ||
236 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
237 | if (enable) { | ||
238 | cfg = 0xf0; /* enable only DMA counter interrupt */ | ||
239 | irq_bits = irqs[chip->irq & 0x0f]; | ||
240 | if (irq_bits < 0) { | ||
241 | snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq); | ||
242 | #if 0 | ||
243 | irq_bits = 0; | ||
244 | cfg = 0x10; | ||
245 | #endif | ||
246 | return -EINVAL; | ||
247 | } | ||
248 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
249 | snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2)); | ||
250 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
251 | cfg = 0xf0; /* extended mode DMA enable */ | ||
252 | dma = chip->dma8; | ||
253 | if (dma > 3 || dma == 2) { | ||
254 | snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma); | ||
255 | #if 0 | ||
256 | dma_bits = 0; | ||
257 | cfg = 0x00; /* disable all DMA */ | ||
258 | #endif | ||
259 | return -EINVAL; | ||
260 | } else { | ||
261 | dma_bits = dma; | ||
262 | if (dma != 3) | ||
263 | dma_bits++; | ||
264 | } | ||
265 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
266 | snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2)); | ||
267 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
268 | } else { | ||
269 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
270 | snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ | ||
271 | snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ | ||
272 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
273 | } | ||
274 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
275 | snd_es1688_read(chip, 0xb1); | ||
276 | snd_es1688_read(chip, 0xb2); | ||
277 | snd_es1688_reset(chip); | ||
278 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | |||
284 | */ | ||
285 | |||
286 | static ratnum_t clocks[2] = { | ||
287 | { | ||
288 | .num = 795444, | ||
289 | .den_min = 1, | ||
290 | .den_max = 128, | ||
291 | .den_step = 1, | ||
292 | }, | ||
293 | { | ||
294 | .num = 397722, | ||
295 | .den_min = 1, | ||
296 | .den_max = 128, | ||
297 | .den_step = 1, | ||
298 | } | ||
299 | }; | ||
300 | |||
301 | static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { | ||
302 | .nrats = 2, | ||
303 | .rats = clocks, | ||
304 | }; | ||
305 | |||
306 | static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream) | ||
307 | { | ||
308 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
309 | unsigned int bits, divider; | ||
310 | |||
311 | if (runtime->rate_num == clocks[0].num) | ||
312 | bits = 256 - runtime->rate_den; | ||
313 | else | ||
314 | bits = 128 - runtime->rate_den; | ||
315 | /* set filter register */ | ||
316 | divider = 256 - 7160000*20/(8*82*runtime->rate); | ||
317 | /* write result to hardware */ | ||
318 | snd_es1688_write(chip, 0xa1, bits); | ||
319 | snd_es1688_write(chip, 0xa2, divider); | ||
320 | } | ||
321 | |||
322 | static int snd_es1688_ioctl(snd_pcm_substream_t * substream, | ||
323 | unsigned int cmd, void *arg) | ||
324 | { | ||
325 | return snd_pcm_lib_ioctl(substream, cmd, arg); | ||
326 | } | ||
327 | |||
328 | static int snd_es1688_trigger(es1688_t *chip, int cmd, unsigned char value) | ||
329 | { | ||
330 | int val; | ||
331 | |||
332 | if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
333 | value = 0x00; | ||
334 | } else if (cmd != SNDRV_PCM_TRIGGER_START) { | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | spin_lock(&chip->reg_lock); | ||
338 | chip->trigger_value = value; | ||
339 | val = snd_es1688_read(chip, 0xb8); | ||
340 | if ((val < 0) || (val & 0x0f) == value) { | ||
341 | spin_unlock(&chip->reg_lock); | ||
342 | return -EINVAL; /* something is wrong */ | ||
343 | } | ||
344 | #if 0 | ||
345 | printk("trigger: val = 0x%x, value = 0x%x\n", val, value); | ||
346 | printk("trigger: pointer = 0x%x\n", snd_dma_pointer(chip->dma8, chip->dma_size)); | ||
347 | #endif | ||
348 | snd_es1688_write(chip, 0xb8, (val & 0xf0) | value); | ||
349 | spin_unlock(&chip->reg_lock); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int snd_es1688_hw_params(snd_pcm_substream_t * substream, | ||
354 | snd_pcm_hw_params_t * hw_params) | ||
355 | { | ||
356 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
357 | } | ||
358 | |||
359 | static int snd_es1688_hw_free(snd_pcm_substream_t * substream) | ||
360 | { | ||
361 | return snd_pcm_lib_free_pages(substream); | ||
362 | } | ||
363 | |||
364 | static int snd_es1688_playback_prepare(snd_pcm_substream_t * substream) | ||
365 | { | ||
366 | unsigned long flags; | ||
367 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
368 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
369 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
370 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
371 | |||
372 | chip->dma_size = size; | ||
373 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
374 | snd_es1688_reset(chip); | ||
375 | snd_es1688_set_rate(chip, substream); | ||
376 | snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */ | ||
377 | snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); | ||
378 | snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ | ||
379 | if (runtime->channels == 1) { | ||
380 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
381 | /* 8. bit mono */ | ||
382 | snd_es1688_write(chip, 0xb6, 0x80); | ||
383 | snd_es1688_write(chip, 0xb7, 0x51); | ||
384 | snd_es1688_write(chip, 0xb7, 0xd0); | ||
385 | } else { | ||
386 | /* 16. bit mono */ | ||
387 | snd_es1688_write(chip, 0xb6, 0x00); | ||
388 | snd_es1688_write(chip, 0xb7, 0x71); | ||
389 | snd_es1688_write(chip, 0xb7, 0xf4); | ||
390 | } | ||
391 | } else { | ||
392 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
393 | /* 8. bit stereo */ | ||
394 | snd_es1688_write(chip, 0xb6, 0x80); | ||
395 | snd_es1688_write(chip, 0xb7, 0x51); | ||
396 | snd_es1688_write(chip, 0xb7, 0x98); | ||
397 | } else { | ||
398 | /* 16. bit stereo */ | ||
399 | snd_es1688_write(chip, 0xb6, 0x00); | ||
400 | snd_es1688_write(chip, 0xb7, 0x71); | ||
401 | snd_es1688_write(chip, 0xb7, 0xbc); | ||
402 | } | ||
403 | } | ||
404 | snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); | ||
405 | snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); | ||
406 | snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON); | ||
407 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
408 | /* --- */ | ||
409 | count = -count; | ||
410 | snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
411 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
412 | snd_es1688_write(chip, 0xa4, (unsigned char) count); | ||
413 | snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); | ||
414 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static int snd_es1688_playback_trigger(snd_pcm_substream_t * substream, | ||
419 | int cmd) | ||
420 | { | ||
421 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
422 | return snd_es1688_trigger(chip, cmd, 0x05); | ||
423 | } | ||
424 | |||
425 | static int snd_es1688_capture_prepare(snd_pcm_substream_t * substream) | ||
426 | { | ||
427 | unsigned long flags; | ||
428 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
429 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
430 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
431 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
432 | |||
433 | chip->dma_size = size; | ||
434 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
435 | snd_es1688_reset(chip); | ||
436 | snd_es1688_set_rate(chip, substream); | ||
437 | snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF); | ||
438 | snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */ | ||
439 | snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); | ||
440 | snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ | ||
441 | if (runtime->channels == 1) { | ||
442 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
443 | /* 8. bit mono */ | ||
444 | snd_es1688_write(chip, 0xb7, 0x51); | ||
445 | snd_es1688_write(chip, 0xb7, 0xd0); | ||
446 | } else { | ||
447 | /* 16. bit mono */ | ||
448 | snd_es1688_write(chip, 0xb7, 0x71); | ||
449 | snd_es1688_write(chip, 0xb7, 0xf4); | ||
450 | } | ||
451 | } else { | ||
452 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
453 | /* 8. bit stereo */ | ||
454 | snd_es1688_write(chip, 0xb7, 0x51); | ||
455 | snd_es1688_write(chip, 0xb7, 0x98); | ||
456 | } else { | ||
457 | /* 16. bit stereo */ | ||
458 | snd_es1688_write(chip, 0xb7, 0x71); | ||
459 | snd_es1688_write(chip, 0xb7, 0xbc); | ||
460 | } | ||
461 | } | ||
462 | snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); | ||
463 | snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); | ||
464 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
465 | /* --- */ | ||
466 | count = -count; | ||
467 | snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); | ||
468 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
469 | snd_es1688_write(chip, 0xa4, (unsigned char) count); | ||
470 | snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); | ||
471 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static int snd_es1688_capture_trigger(snd_pcm_substream_t * substream, | ||
476 | int cmd) | ||
477 | { | ||
478 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
479 | return snd_es1688_trigger(chip, cmd, 0x0f); | ||
480 | } | ||
481 | |||
482 | static irqreturn_t snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
483 | { | ||
484 | es1688_t *chip = dev_id; | ||
485 | |||
486 | if (chip->trigger_value == 0x05) /* ok.. playback is active */ | ||
487 | snd_pcm_period_elapsed(chip->playback_substream); | ||
488 | if (chip->trigger_value == 0x0f) /* ok.. capture is active */ | ||
489 | snd_pcm_period_elapsed(chip->capture_substream); | ||
490 | |||
491 | inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */ | ||
492 | return IRQ_HANDLED; | ||
493 | } | ||
494 | |||
495 | static snd_pcm_uframes_t snd_es1688_playback_pointer(snd_pcm_substream_t * substream) | ||
496 | { | ||
497 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
498 | size_t ptr; | ||
499 | |||
500 | if (chip->trigger_value != 0x05) | ||
501 | return 0; | ||
502 | ptr = snd_dma_pointer(chip->dma8, chip->dma_size); | ||
503 | return bytes_to_frames(substream->runtime, ptr); | ||
504 | } | ||
505 | |||
506 | static snd_pcm_uframes_t snd_es1688_capture_pointer(snd_pcm_substream_t * substream) | ||
507 | { | ||
508 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
509 | size_t ptr; | ||
510 | |||
511 | if (chip->trigger_value != 0x0f) | ||
512 | return 0; | ||
513 | ptr = snd_dma_pointer(chip->dma8, chip->dma_size); | ||
514 | return bytes_to_frames(substream->runtime, ptr); | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | |||
519 | */ | ||
520 | |||
521 | static snd_pcm_hardware_t snd_es1688_playback = | ||
522 | { | ||
523 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
524 | SNDRV_PCM_INFO_MMAP_VALID), | ||
525 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
526 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
527 | .rate_min = 4000, | ||
528 | .rate_max = 48000, | ||
529 | .channels_min = 1, | ||
530 | .channels_max = 2, | ||
531 | .buffer_bytes_max = 65536, | ||
532 | .period_bytes_min = 64, | ||
533 | .period_bytes_max = 65536, | ||
534 | .periods_min = 1, | ||
535 | .periods_max = 1024, | ||
536 | .fifo_size = 0, | ||
537 | }; | ||
538 | |||
539 | static snd_pcm_hardware_t snd_es1688_capture = | ||
540 | { | ||
541 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
542 | SNDRV_PCM_INFO_MMAP_VALID), | ||
543 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
544 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
545 | .rate_min = 4000, | ||
546 | .rate_max = 48000, | ||
547 | .channels_min = 1, | ||
548 | .channels_max = 2, | ||
549 | .buffer_bytes_max = 65536, | ||
550 | .period_bytes_min = 64, | ||
551 | .period_bytes_max = 65536, | ||
552 | .periods_min = 1, | ||
553 | .periods_max = 1024, | ||
554 | .fifo_size = 0, | ||
555 | }; | ||
556 | |||
557 | /* | ||
558 | |||
559 | */ | ||
560 | |||
561 | static int snd_es1688_playback_open(snd_pcm_substream_t * substream) | ||
562 | { | ||
563 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
564 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
565 | |||
566 | if (chip->capture_substream != NULL) | ||
567 | return -EAGAIN; | ||
568 | chip->playback_substream = substream; | ||
569 | runtime->hw = snd_es1688_playback; | ||
570 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
571 | &hw_constraints_clocks); | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static int snd_es1688_capture_open(snd_pcm_substream_t * substream) | ||
576 | { | ||
577 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
578 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
579 | |||
580 | if (chip->playback_substream != NULL) | ||
581 | return -EAGAIN; | ||
582 | chip->capture_substream = substream; | ||
583 | runtime->hw = snd_es1688_capture; | ||
584 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
585 | &hw_constraints_clocks); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static int snd_es1688_playback_close(snd_pcm_substream_t * substream) | ||
590 | { | ||
591 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
592 | |||
593 | chip->playback_substream = NULL; | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static int snd_es1688_capture_close(snd_pcm_substream_t * substream) | ||
598 | { | ||
599 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
600 | |||
601 | chip->capture_substream = NULL; | ||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int snd_es1688_free(es1688_t *chip) | ||
606 | { | ||
607 | if (chip->res_port) { | ||
608 | snd_es1688_init(chip, 0); | ||
609 | release_resource(chip->res_port); | ||
610 | kfree_nocheck(chip->res_port); | ||
611 | } | ||
612 | if (chip->irq >= 0) | ||
613 | free_irq(chip->irq, (void *) chip); | ||
614 | if (chip->dma8 >= 0) { | ||
615 | disable_dma(chip->dma8); | ||
616 | free_dma(chip->dma8); | ||
617 | } | ||
618 | kfree(chip); | ||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int snd_es1688_dev_free(snd_device_t *device) | ||
623 | { | ||
624 | es1688_t *chip = device->device_data; | ||
625 | return snd_es1688_free(chip); | ||
626 | } | ||
627 | |||
628 | static const char *snd_es1688_chip_id(es1688_t *chip) | ||
629 | { | ||
630 | static char tmp[16]; | ||
631 | sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f); | ||
632 | return tmp; | ||
633 | } | ||
634 | |||
635 | int snd_es1688_create(snd_card_t * card, | ||
636 | unsigned long port, | ||
637 | unsigned long mpu_port, | ||
638 | int irq, | ||
639 | int mpu_irq, | ||
640 | int dma8, | ||
641 | unsigned short hardware, | ||
642 | es1688_t **rchip) | ||
643 | { | ||
644 | static snd_device_ops_t ops = { | ||
645 | .dev_free = snd_es1688_dev_free, | ||
646 | }; | ||
647 | |||
648 | es1688_t *chip; | ||
649 | int err; | ||
650 | |||
651 | *rchip = NULL; | ||
652 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
653 | if (chip == NULL) | ||
654 | return -ENOMEM; | ||
655 | chip->irq = -1; | ||
656 | chip->dma8 = -1; | ||
657 | |||
658 | if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) { | ||
659 | snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4); | ||
660 | snd_es1688_free(chip); | ||
661 | return -EBUSY; | ||
662 | } | ||
663 | if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) { | ||
664 | snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq); | ||
665 | snd_es1688_free(chip); | ||
666 | return -EBUSY; | ||
667 | } | ||
668 | chip->irq = irq; | ||
669 | if (request_dma(dma8, "ES1688")) { | ||
670 | snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8); | ||
671 | snd_es1688_free(chip); | ||
672 | return -EBUSY; | ||
673 | } | ||
674 | chip->dma8 = dma8; | ||
675 | |||
676 | spin_lock_init(&chip->reg_lock); | ||
677 | spin_lock_init(&chip->mixer_lock); | ||
678 | chip->card = card; | ||
679 | chip->port = port; | ||
680 | mpu_port &= ~0x000f; | ||
681 | if (mpu_port < 0x300 || mpu_port > 0x330) | ||
682 | mpu_port = 0; | ||
683 | chip->mpu_port = mpu_port; | ||
684 | chip->mpu_irq = mpu_irq; | ||
685 | chip->hardware = hardware; | ||
686 | |||
687 | if ((err = snd_es1688_probe(chip)) < 0) { | ||
688 | snd_es1688_free(chip); | ||
689 | return err; | ||
690 | } | ||
691 | if ((err = snd_es1688_init(chip, 1)) < 0) { | ||
692 | snd_es1688_free(chip); | ||
693 | return err; | ||
694 | } | ||
695 | |||
696 | /* Register device */ | ||
697 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
698 | snd_es1688_free(chip); | ||
699 | return err; | ||
700 | } | ||
701 | |||
702 | *rchip = chip; | ||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | static snd_pcm_ops_t snd_es1688_playback_ops = { | ||
707 | .open = snd_es1688_playback_open, | ||
708 | .close = snd_es1688_playback_close, | ||
709 | .ioctl = snd_es1688_ioctl, | ||
710 | .hw_params = snd_es1688_hw_params, | ||
711 | .hw_free = snd_es1688_hw_free, | ||
712 | .prepare = snd_es1688_playback_prepare, | ||
713 | .trigger = snd_es1688_playback_trigger, | ||
714 | .pointer = snd_es1688_playback_pointer, | ||
715 | }; | ||
716 | |||
717 | static snd_pcm_ops_t snd_es1688_capture_ops = { | ||
718 | .open = snd_es1688_capture_open, | ||
719 | .close = snd_es1688_capture_close, | ||
720 | .ioctl = snd_es1688_ioctl, | ||
721 | .hw_params = snd_es1688_hw_params, | ||
722 | .hw_free = snd_es1688_hw_free, | ||
723 | .prepare = snd_es1688_capture_prepare, | ||
724 | .trigger = snd_es1688_capture_trigger, | ||
725 | .pointer = snd_es1688_capture_pointer, | ||
726 | }; | ||
727 | |||
728 | static void snd_es1688_pcm_free(snd_pcm_t *pcm) | ||
729 | { | ||
730 | es1688_t *chip = pcm->private_data; | ||
731 | chip->pcm = NULL; | ||
732 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
733 | } | ||
734 | |||
735 | int snd_es1688_pcm(es1688_t * chip, int device, snd_pcm_t ** rpcm) | ||
736 | { | ||
737 | snd_pcm_t *pcm; | ||
738 | int err; | ||
739 | |||
740 | if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0) | ||
741 | return err; | ||
742 | |||
743 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops); | ||
744 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops); | ||
745 | |||
746 | pcm->private_data = chip; | ||
747 | pcm->private_free = snd_es1688_pcm_free; | ||
748 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | ||
749 | sprintf(pcm->name, snd_es1688_chip_id(chip)); | ||
750 | chip->pcm = pcm; | ||
751 | |||
752 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
753 | snd_dma_isa_data(), | ||
754 | 64*1024, 64*1024); | ||
755 | |||
756 | if (rpcm) | ||
757 | *rpcm = pcm; | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | /* | ||
762 | * MIXER part | ||
763 | */ | ||
764 | |||
765 | static int snd_es1688_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
766 | { | ||
767 | static char *texts[9] = { | ||
768 | "Mic", "Mic Master", "CD", "AOUT", | ||
769 | "Mic1", "Mix", "Line", "Master" | ||
770 | }; | ||
771 | |||
772 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
773 | uinfo->count = 1; | ||
774 | uinfo->value.enumerated.items = 8; | ||
775 | if (uinfo->value.enumerated.item > 7) | ||
776 | uinfo->value.enumerated.item = 7; | ||
777 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | static int snd_es1688_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
782 | { | ||
783 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
784 | ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7; | ||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | static int snd_es1688_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
789 | { | ||
790 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
791 | unsigned long flags; | ||
792 | unsigned char oval, nval; | ||
793 | int change; | ||
794 | |||
795 | if (ucontrol->value.enumerated.item[0] > 8) | ||
796 | return -EINVAL; | ||
797 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
798 | oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV); | ||
799 | nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15); | ||
800 | change = nval != oval; | ||
801 | if (change) | ||
802 | snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval); | ||
803 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
804 | return change; | ||
805 | } | ||
806 | |||
807 | #define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \ | ||
808 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
809 | .info = snd_es1688_info_single, \ | ||
810 | .get = snd_es1688_get_single, .put = snd_es1688_put_single, \ | ||
811 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
812 | |||
813 | static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
814 | { | ||
815 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
816 | |||
817 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
818 | uinfo->count = 1; | ||
819 | uinfo->value.integer.min = 0; | ||
820 | uinfo->value.integer.max = mask; | ||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | static int snd_es1688_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
825 | { | ||
826 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
827 | unsigned long flags; | ||
828 | int reg = kcontrol->private_value & 0xff; | ||
829 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
830 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
831 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
832 | |||
833 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
834 | ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask; | ||
835 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
836 | if (invert) | ||
837 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
838 | return 0; | ||
839 | } | ||
840 | |||
841 | static int snd_es1688_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
842 | { | ||
843 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
844 | unsigned long flags; | ||
845 | int reg = kcontrol->private_value & 0xff; | ||
846 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
847 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
848 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
849 | int change; | ||
850 | unsigned char oval, nval; | ||
851 | |||
852 | nval = (ucontrol->value.integer.value[0] & mask); | ||
853 | if (invert) | ||
854 | nval = mask - nval; | ||
855 | nval <<= shift; | ||
856 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
857 | oval = snd_es1688_mixer_read(chip, reg); | ||
858 | nval = (oval & ~(mask << shift)) | nval; | ||
859 | change = nval != oval; | ||
860 | if (change) | ||
861 | snd_es1688_mixer_write(chip, reg, nval); | ||
862 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
863 | return change; | ||
864 | } | ||
865 | |||
866 | #define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ | ||
867 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
868 | .info = snd_es1688_info_double, \ | ||
869 | .get = snd_es1688_get_double, .put = snd_es1688_put_double, \ | ||
870 | .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } | ||
871 | |||
872 | static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
873 | { | ||
874 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
875 | |||
876 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
877 | uinfo->count = 2; | ||
878 | uinfo->value.integer.min = 0; | ||
879 | uinfo->value.integer.max = mask; | ||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | static int snd_es1688_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
884 | { | ||
885 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
886 | unsigned long flags; | ||
887 | int left_reg = kcontrol->private_value & 0xff; | ||
888 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
889 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
890 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
891 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
892 | int invert = (kcontrol->private_value >> 22) & 1; | ||
893 | unsigned char left, right; | ||
894 | |||
895 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
896 | if (left_reg < 0xa0) | ||
897 | left = snd_es1688_mixer_read(chip, left_reg); | ||
898 | else | ||
899 | left = snd_es1688_read(chip, left_reg); | ||
900 | if (left_reg != right_reg) { | ||
901 | if (right_reg < 0xa0) | ||
902 | right = snd_es1688_mixer_read(chip, right_reg); | ||
903 | else | ||
904 | right = snd_es1688_read(chip, right_reg); | ||
905 | } else | ||
906 | right = left; | ||
907 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
908 | ucontrol->value.integer.value[0] = (left >> shift_left) & mask; | ||
909 | ucontrol->value.integer.value[1] = (right >> shift_right) & mask; | ||
910 | if (invert) { | ||
911 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
912 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
913 | } | ||
914 | return 0; | ||
915 | } | ||
916 | |||
917 | static int snd_es1688_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
918 | { | ||
919 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
920 | unsigned long flags; | ||
921 | int left_reg = kcontrol->private_value & 0xff; | ||
922 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
923 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
924 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
925 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
926 | int invert = (kcontrol->private_value >> 22) & 1; | ||
927 | int change; | ||
928 | unsigned char val1, val2, oval1, oval2; | ||
929 | |||
930 | val1 = ucontrol->value.integer.value[0] & mask; | ||
931 | val2 = ucontrol->value.integer.value[1] & mask; | ||
932 | if (invert) { | ||
933 | val1 = mask - val1; | ||
934 | val2 = mask - val2; | ||
935 | } | ||
936 | val1 <<= shift_left; | ||
937 | val2 <<= shift_right; | ||
938 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
939 | if (left_reg != right_reg) { | ||
940 | if (left_reg < 0xa0) | ||
941 | oval1 = snd_es1688_mixer_read(chip, left_reg); | ||
942 | else | ||
943 | oval1 = snd_es1688_read(chip, left_reg); | ||
944 | if (right_reg < 0xa0) | ||
945 | oval2 = snd_es1688_mixer_read(chip, right_reg); | ||
946 | else | ||
947 | oval2 = snd_es1688_read(chip, right_reg); | ||
948 | val1 = (oval1 & ~(mask << shift_left)) | val1; | ||
949 | val2 = (oval2 & ~(mask << shift_right)) | val2; | ||
950 | change = val1 != oval1 || val2 != oval2; | ||
951 | if (change) { | ||
952 | if (left_reg < 0xa0) | ||
953 | snd_es1688_mixer_write(chip, left_reg, val1); | ||
954 | else | ||
955 | snd_es1688_write(chip, left_reg, val1); | ||
956 | if (right_reg < 0xa0) | ||
957 | snd_es1688_mixer_write(chip, right_reg, val1); | ||
958 | else | ||
959 | snd_es1688_write(chip, right_reg, val1); | ||
960 | } | ||
961 | } else { | ||
962 | if (left_reg < 0xa0) | ||
963 | oval1 = snd_es1688_mixer_read(chip, left_reg); | ||
964 | else | ||
965 | oval1 = snd_es1688_read(chip, left_reg); | ||
966 | val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
967 | change = val1 != oval1; | ||
968 | if (change) { | ||
969 | if (left_reg < 0xa0) | ||
970 | snd_es1688_mixer_write(chip, left_reg, val1); | ||
971 | else | ||
972 | snd_es1688_write(chip, left_reg, val1); | ||
973 | } | ||
974 | |||
975 | } | ||
976 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
977 | return change; | ||
978 | } | ||
979 | |||
980 | static snd_kcontrol_new_t snd_es1688_controls[] = { | ||
981 | ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), | ||
982 | ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), | ||
983 | ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0), | ||
984 | ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0), | ||
985 | ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0), | ||
986 | ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0), | ||
987 | ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0), | ||
988 | ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0), | ||
989 | ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0), | ||
990 | ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1), | ||
991 | { | ||
992 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
993 | .name = "Capture Source", | ||
994 | .info = snd_es1688_info_mux, | ||
995 | .get = snd_es1688_get_mux, | ||
996 | .put = snd_es1688_put_mux, | ||
997 | }, | ||
998 | }; | ||
999 | |||
1000 | #define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2) | ||
1001 | |||
1002 | static unsigned char snd_es1688_init_table[][2] = { | ||
1003 | { ES1688_MASTER_DEV, 0 }, | ||
1004 | { ES1688_PCM_DEV, 0 }, | ||
1005 | { ES1688_LINE_DEV, 0 }, | ||
1006 | { ES1688_CD_DEV, 0 }, | ||
1007 | { ES1688_FM_DEV, 0 }, | ||
1008 | { ES1688_MIC_DEV, 0 }, | ||
1009 | { ES1688_AUX_DEV, 0 }, | ||
1010 | { ES1688_SPEAKER_DEV, 0 }, | ||
1011 | { ES1688_RECLEV_DEV, 0 }, | ||
1012 | { ES1688_REC_DEV, 0x17 } | ||
1013 | }; | ||
1014 | |||
1015 | int snd_es1688_mixer(es1688_t *chip) | ||
1016 | { | ||
1017 | snd_card_t *card; | ||
1018 | unsigned int idx; | ||
1019 | int err; | ||
1020 | unsigned char reg, val; | ||
1021 | |||
1022 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
1023 | |||
1024 | card = chip->card; | ||
1025 | |||
1026 | strcpy(card->mixername, snd_es1688_chip_id(chip)); | ||
1027 | |||
1028 | for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) { | ||
1029 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0) | ||
1030 | return err; | ||
1031 | } | ||
1032 | for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) { | ||
1033 | reg = snd_es1688_init_table[idx][0]; | ||
1034 | val = snd_es1688_init_table[idx][1]; | ||
1035 | if (reg < 0xa0) | ||
1036 | snd_es1688_mixer_write(chip, reg, val); | ||
1037 | else | ||
1038 | snd_es1688_write(chip, reg, val); | ||
1039 | } | ||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | EXPORT_SYMBOL(snd_es1688_mixer_write); | ||
1044 | EXPORT_SYMBOL(snd_es1688_create); | ||
1045 | EXPORT_SYMBOL(snd_es1688_pcm); | ||
1046 | EXPORT_SYMBOL(snd_es1688_mixer); | ||
1047 | |||
1048 | /* | ||
1049 | * INIT part | ||
1050 | */ | ||
1051 | |||
1052 | static int __init alsa_es1688_init(void) | ||
1053 | { | ||
1054 | return 0; | ||
1055 | } | ||
1056 | |||
1057 | static void __exit alsa_es1688_exit(void) | ||
1058 | { | ||
1059 | } | ||
1060 | |||
1061 | module_init(alsa_es1688_init) | ||
1062 | module_exit(alsa_es1688_exit) | ||