1/*
2 *   ALSA driver for ICEnsemble ICE1712 (Envy24)
3 *
4 *   Lowlevel functions for Hoontech STDSP24
5 *
6 *	Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
7 *
8 *   This program is free software; you can redistribute it and/or modify
9 *   it under the terms of the GNU General Public License as published by
10 *   the Free Software Foundation; either version 2 of the License, or
11 *   (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 *
22 */
23
24#include <linux/delay.h>
25#include <linux/interrupt.h>
26#include <linux/init.h>
27#include <linux/slab.h>
28#include <linux/mutex.h>
29
30#include <sound/core.h>
31
32#include "ice1712.h"
33#include "hoontech.h"
34
35/* Hoontech-specific setting */
36struct hoontech_spec {
37	unsigned char boxbits[4];
38	unsigned int config;
39	unsigned short boxconfig[4];
40};
41
42static void snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
43{
44	byte |= ICE1712_STDSP24_CLOCK_BIT;
45	udelay(100);
46	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
47	byte &= ~ICE1712_STDSP24_CLOCK_BIT;
48	udelay(100);
49	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
50	byte |= ICE1712_STDSP24_CLOCK_BIT;
51	udelay(100);
52	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
53}
54
55static void snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
56{
57	struct hoontech_spec *spec = ice->spec;
58	mutex_lock(&ice->gpio_mutex);
59	ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate);
60	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
61	mutex_unlock(&ice->gpio_mutex);
62}
63
64static void snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
65{
66	struct hoontech_spec *spec = ice->spec;
67	mutex_lock(&ice->gpio_mutex);
68	ICE1712_STDSP24_3_MUTE(spec->boxbits, activate);
69	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
70	mutex_unlock(&ice->gpio_mutex);
71}
72
73static void snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
74{
75	struct hoontech_spec *spec = ice->spec;
76	mutex_lock(&ice->gpio_mutex);
77	ICE1712_STDSP24_3_INSEL(spec->boxbits, activate);
78	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
79	mutex_unlock(&ice->gpio_mutex);
80}
81
82static void snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
83{
84	struct hoontech_spec *spec = ice->spec;
85
86	mutex_lock(&ice->gpio_mutex);
87
88	/* select box */
89	ICE1712_STDSP24_0_BOX(spec->boxbits, box);
90	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
91
92	/* prepare for write */
93	if (chn == 3)
94		ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
95	ICE1712_STDSP24_2_MIDI1(spec->boxbits, activate);
96	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
97	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
98
99	ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
100	ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
101	ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
102	ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
103	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
104	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
105	udelay(100);
106	if (chn == 3) {
107		ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
108		snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
109	} else {
110		switch (chn) {
111		case 0:	ICE1712_STDSP24_1_CHN1(spec->boxbits, 0); break;
112		case 1:	ICE1712_STDSP24_1_CHN2(spec->boxbits, 0); break;
113		case 2:	ICE1712_STDSP24_1_CHN3(spec->boxbits, 0); break;
114		}
115		snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
116	}
117	udelay(100);
118	ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
119	ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
120	ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
121	ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
122	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
123	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
124	udelay(100);
125
126	ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
127	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
128
129	mutex_unlock(&ice->gpio_mutex);
130}
131
132static void snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
133{
134	struct hoontech_spec *spec = ice->spec;
135
136	mutex_lock(&ice->gpio_mutex);
137
138	/* select box */
139	ICE1712_STDSP24_0_BOX(spec->boxbits, box);
140	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
141
142	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
143	ICE1712_STDSP24_2_MIDI1(spec->boxbits, master);
144	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
145	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
146
147	udelay(100);
148
149	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 0);
150	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
151
152	mdelay(10);
153
154	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
155	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
156
157	mutex_unlock(&ice->gpio_mutex);
158}
159
160static void snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
161{
162	struct hoontech_spec *spec = ice->spec;
163	mutex_lock(&ice->gpio_mutex);
164	ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate);
165	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
166	mutex_unlock(&ice->gpio_mutex);
167}
168
169static int snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
170{
171	struct hoontech_spec *spec;
172	int box, chn;
173
174	ice->num_total_dacs = 8;
175	ice->num_total_adcs = 8;
176
177	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
178	if (!spec)
179		return -ENOMEM;
180	ice->spec = spec;
181
182	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 0);
183	ICE1712_STDSP24_CLOCK(spec->boxbits, 0, 1);
184	ICE1712_STDSP24_0_BOX(spec->boxbits, 0);
185	ICE1712_STDSP24_0_DAREAR(spec->boxbits, 0);
186
187	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 1);
188	ICE1712_STDSP24_CLOCK(spec->boxbits, 1, 1);
189	ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
190	ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
191	ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
192
193	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 2);
194	ICE1712_STDSP24_CLOCK(spec->boxbits, 2, 1);
195	ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
196	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
197	ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
198
199	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 3);
200	ICE1712_STDSP24_CLOCK(spec->boxbits, 3, 1);
201	ICE1712_STDSP24_3_MIDI2(spec->boxbits, 0);
202	ICE1712_STDSP24_3_MUTE(spec->boxbits, 1);
203	ICE1712_STDSP24_3_INSEL(spec->boxbits, 0);
204
205	/* let's go - activate only functions in first box */
206	spec->config = 0;
207			    /* ICE1712_STDSP24_MUTE |
208			       ICE1712_STDSP24_INSEL |
209			       ICE1712_STDSP24_DAREAR; */
210	/*  These boxconfigs have caused problems in the past.
211	 *  The code is not optimal, but should now enable a working config to
212	 *  be achieved.
213	 *  ** MIDI IN can only be configured on one box **
214	 *  ICE1712_STDSP24_BOX_MIDI1 needs to be set for that box.
215	 *  Tests on a ADAC2000 box suggest the box config flags do not
216	 *  work as would be expected, and the inputs are crossed.
217	 *  Setting ICE1712_STDSP24_BOX_MIDI1 and ICE1712_STDSP24_BOX_MIDI2
218	 *  on the same box connects MIDI-In to both 401 uarts; both outputs
219	 *  are then active on all boxes.
220	 *  The default config here sets up everything on the first box.
221	 *  Alan Horstmann  5.2.2008
222	 */
223	spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
224				     ICE1712_STDSP24_BOX_CHN2 |
225				     ICE1712_STDSP24_BOX_CHN3 |
226				     ICE1712_STDSP24_BOX_CHN4 |
227				     ICE1712_STDSP24_BOX_MIDI1 |
228				     ICE1712_STDSP24_BOX_MIDI2;
229	spec->boxconfig[1] =
230	spec->boxconfig[2] =
231	spec->boxconfig[3] = 0;
232	snd_ice1712_stdsp24_darear(ice,
233		(spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
234	snd_ice1712_stdsp24_mute(ice,
235		(spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0);
236	snd_ice1712_stdsp24_insel(ice,
237		(spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0);
238	for (box = 0; box < 4; box++) {
239		if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
240                        snd_ice1712_stdsp24_midi2(ice, 1);
241		for (chn = 0; chn < 4; chn++)
242			snd_ice1712_stdsp24_box_channel(ice, box, chn,
243				(spec->boxconfig[box] & (1 << chn)) ? 1 : 0);
244		if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1)
245			snd_ice1712_stdsp24_box_midi(ice, box, 1);
246	}
247
248	return 0;
249}
250
251/*
252 * AK4524 access
253 */
254
255/* start callback for STDSP24 with modified hardware */
256static void stdsp24_ak4524_lock(struct snd_akm4xxx *ak, int chip)
257{
258	struct snd_ice1712 *ice = ak->private_data[0];
259	unsigned char tmp;
260	snd_ice1712_save_gpio_status(ice);
261	tmp =	ICE1712_STDSP24_SERIAL_DATA |
262		ICE1712_STDSP24_SERIAL_CLOCK |
263		ICE1712_STDSP24_AK4524_CS;
264	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
265			  ice->gpio.direction | tmp);
266	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
267}
268
269static int snd_ice1712_value_init(struct snd_ice1712 *ice)
270{
271	/* Hoontech STDSP24 with modified hardware */
272	static struct snd_akm4xxx akm_stdsp24_mv = {
273		.num_adcs = 2,
274		.num_dacs = 2,
275		.type = SND_AK4524,
276		.ops = {
277			.lock = stdsp24_ak4524_lock
278		}
279	};
280
281	static struct snd_ak4xxx_private akm_stdsp24_mv_priv = {
282		.caddr = 2,
283		.cif = 1, /* CIF high */
284		.data_mask = ICE1712_STDSP24_SERIAL_DATA,
285		.clk_mask = ICE1712_STDSP24_SERIAL_CLOCK,
286		.cs_mask = ICE1712_STDSP24_AK4524_CS,
287		.cs_addr = ICE1712_STDSP24_AK4524_CS,
288		.cs_none = 0,
289		.add_flags = 0,
290	};
291
292	int err;
293	struct snd_akm4xxx *ak;
294
295	/* set the analog DACs */
296	ice->num_total_dacs = 2;
297
298	/* set the analog ADCs */
299	ice->num_total_adcs = 2;
300
301	/* analog section */
302	ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
303	if (! ak)
304		return -ENOMEM;
305	ice->akm_codecs = 1;
306
307	err = snd_ice1712_akm4xxx_init(ak, &akm_stdsp24_mv, &akm_stdsp24_mv_priv, ice);
308	if (err < 0)
309		return err;
310
311	/* ak4524 controls */
312	return snd_ice1712_akm4xxx_build_controls(ice);
313}
314
315static int snd_ice1712_ez8_init(struct snd_ice1712 *ice)
316{
317	ice->gpio.write_mask = ice->eeprom.gpiomask;
318	ice->gpio.direction = ice->eeprom.gpiodir;
319	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
320	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
321	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
322	return 0;
323}
324
325
326/* entry point */
327struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] = {
328	{
329		.subvendor = ICE1712_SUBDEVICE_STDSP24,
330		.name = "Hoontech SoundTrack Audio DSP24",
331		.model = "dsp24",
332		.chip_init = snd_ice1712_hoontech_init,
333		.mpu401_1_name = "MIDI-1 Hoontech/STA DSP24",
334		.mpu401_2_name = "MIDI-2 Hoontech/STA DSP24",
335	},
336	{
337		.subvendor = ICE1712_SUBDEVICE_STDSP24_VALUE,	/* a dummy id */
338		.name = "Hoontech SoundTrack Audio DSP24 Value",
339		.model = "dsp24_value",
340		.chip_init = snd_ice1712_value_init,
341	},
342	{
343		.subvendor = ICE1712_SUBDEVICE_STDSP24_MEDIA7_1,
344		.name = "Hoontech STA DSP24 Media 7.1",
345		.model = "dsp24_71",
346		.chip_init = snd_ice1712_hoontech_init,
347	},
348	{
349		.subvendor = ICE1712_SUBDEVICE_EVENT_EZ8,	/* a dummy id */
350		.name = "Event Electronics EZ8",
351		.model = "ez8",
352		.chip_init = snd_ice1712_ez8_init,
353	},
354	{ } /* terminator */
355};
356