• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/sound/pci/au88x0/
1/***************************************************************************
2 *            au88x0_a3d.c
3 *
4 *  Fri Jul 18 14:16:22 2003
5 *  Copyright  2003  mjander
6 *  mjander@users.sourceforge.net
7 *
8 * A3D. You may think i'm crazy, but this may work someday. Who knows...
9 ****************************************************************************/
10
11/*
12 *  This program is free software; you can redistribute it and/or modify
13 *  it under the terms of the GNU General Public License as published by
14 *  the Free Software Foundation; either version 2 of the License, or
15 *  (at your option) any later version.
16 *
17 *  This program is distributed in the hope that it will be useful,
18 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 *  GNU Library General Public License for more details.
21 *
22 *  You should have received a copy of the GNU General Public License
23 *  along with this program; if not, write to the Free Software
24 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27#include "au88x0_a3d.h"
28#include "au88x0_a3ddata.c"
29#include "au88x0_xtalk.h"
30#include "au88x0.h"
31
32static void
33a3dsrc_SetTimeConsts(a3dsrc_t * a, short HrtfTrack, short ItdTrack,
34		     short GTrack, short CTrack)
35{
36	vortex_t *vortex = (vortex_t *) (a->vortex);
37	hwwrite(vortex->mmio,
38		a3d_addrA(a->slice, a->source, A3D_A_HrtfTrackTC), HrtfTrack);
39	hwwrite(vortex->mmio,
40		a3d_addrA(a->slice, a->source, A3D_A_ITDTrackTC), ItdTrack);
41	hwwrite(vortex->mmio,
42		a3d_addrA(a->slice, a->source, A3D_A_GainTrackTC), GTrack);
43	hwwrite(vortex->mmio,
44		a3d_addrA(a->slice, a->source, A3D_A_CoeffTrackTC), CTrack);
45}
46
47/* Atmospheric absorbtion. */
48
49static void
50a3dsrc_SetAtmosTarget(a3dsrc_t * a, short aa, short b, short c, short d,
51		      short e)
52{
53	vortex_t *vortex = (vortex_t *) (a->vortex);
54	hwwrite(vortex->mmio,
55		a3d_addrB(a->slice, a->source, A3D_B_A21Target),
56		(e << 0x10) | d);
57	hwwrite(vortex->mmio,
58		a3d_addrB(a->slice, a->source, A3D_B_B10Target),
59		(b << 0x10) | aa);
60	hwwrite(vortex->mmio,
61		a3d_addrB(a->slice, a->source, A3D_B_B2Target), c);
62}
63
64static void
65a3dsrc_SetAtmosCurrent(a3dsrc_t * a, short aa, short b, short c, short d,
66		       short e)
67{
68	vortex_t *vortex = (vortex_t *) (a->vortex);
69	hwwrite(vortex->mmio,
70		a3d_addrB(a->slice, a->source, A3D_B_A12Current),
71		(e << 0x10) | d);
72	hwwrite(vortex->mmio,
73		a3d_addrB(a->slice, a->source, A3D_B_B01Current),
74		(b << 0x10) | aa);
75	hwwrite(vortex->mmio,
76		a3d_addrB(a->slice, a->source, A3D_B_B2Current), c);
77}
78
79static void
80a3dsrc_SetAtmosState(a3dsrc_t * a, short x1, short x2, short y1, short y2)
81{
82	vortex_t *vortex = (vortex_t *) (a->vortex);
83	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_x1), x1);
84	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_x2), x2);
85	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_y1), y1);
86	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_y2), y2);
87}
88
89/* HRTF */
90
91static void
92a3dsrc_SetHrtfTarget(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
93{
94	vortex_t *vortex = (vortex_t *) (a->vortex);
95	int i;
96
97	for (i = 0; i < HRTF_SZ; i++)
98		hwwrite(vortex->mmio,
99			a3d_addrB(a->slice, a->source,
100				  A3D_B_HrtfTarget) + (i << 2),
101			(b[i] << 0x10) | aa[i]);
102}
103
104static void
105a3dsrc_SetHrtfCurrent(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
106{
107	vortex_t *vortex = (vortex_t *) (a->vortex);
108	int i;
109
110	for (i = 0; i < HRTF_SZ; i++)
111		hwwrite(vortex->mmio,
112			a3d_addrB(a->slice, a->source,
113				  A3D_B_HrtfCurrent) + (i << 2),
114			(b[i] << 0x10) | aa[i]);
115}
116
117static void
118a3dsrc_SetHrtfState(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
119{
120	vortex_t *vortex = (vortex_t *) (a->vortex);
121	int i;
122
123	for (i = 0; i < HRTF_SZ; i++)
124		hwwrite(vortex->mmio,
125			a3d_addrB(a->slice, a->source,
126				  A3D_B_HrtfDelayLine) + (i << 2),
127			(b[i] << 0x10) | aa[i]);
128}
129
130static void a3dsrc_SetHrtfOutput(a3dsrc_t * a, short left, short right)
131{
132	vortex_t *vortex = (vortex_t *) (a->vortex);
133	hwwrite(vortex->mmio,
134		a3d_addrA(a->slice, a->source, A3D_A_HrtfOutL), left);
135	hwwrite(vortex->mmio,
136		a3d_addrA(a->slice, a->source, A3D_A_HrtfOutR), right);
137}
138
139
140/* Interaural Time Difference.
141 * "The other main clue that humans use to locate sounds, is called
142 * Interaural Time Difference (ITD). The differences in distance from
143 * the sound source to a listeners ears means  that the sound will
144 * reach one ear slightly before the other....", found somewhere with google.*/
145static void a3dsrc_SetItdTarget(a3dsrc_t * a, short litd, short ritd)
146{
147	vortex_t *vortex = (vortex_t *) (a->vortex);
148
149	if (litd < 0)
150		litd = 0;
151	if (litd > 0x57FF)
152		litd = 0x57FF;
153	if (ritd < 0)
154		ritd = 0;
155	if (ritd > 0x57FF)
156		ritd = 0x57FF;
157	hwwrite(vortex->mmio,
158		a3d_addrB(a->slice, a->source, A3D_B_ITDTarget),
159		(ritd << 0x10) | litd);
160	//hwwrite(vortex->mmio, addr(0x191DF+5, this04, this08), (ritd<<0x10)|litd);
161}
162
163static void a3dsrc_SetItdCurrent(a3dsrc_t * a, short litd, short ritd)
164{
165	vortex_t *vortex = (vortex_t *) (a->vortex);
166
167	if (litd < 0)
168		litd = 0;
169	if (litd > 0x57FF)
170		litd = 0x57FF;
171	if (ritd < 0)
172		ritd = 0;
173	if (ritd > 0x57FF)
174		ritd = 0x57FF;
175	hwwrite(vortex->mmio,
176		a3d_addrB(a->slice, a->source, A3D_B_ITDCurrent),
177		(ritd << 0x10) | litd);
178	//hwwrite(vortex->mmio, addr(0x191DF+1, this04, this08), (ritd<<0x10)|litd);
179}
180
181static void a3dsrc_SetItdDline(a3dsrc_t * a, a3d_ItdDline_t const dline)
182{
183	vortex_t *vortex = (vortex_t *) (a->vortex);
184	int i;
185	/* 45 != 40 -> Check this ! */
186	for (i = 0; i < DLINE_SZ; i++)
187		hwwrite(vortex->mmio,
188			a3d_addrA(a->slice, a->source,
189				  A3D_A_ITDDelayLine) + (i << 2), dline[i]);
190}
191
192/* This is may be used for ILD Interaural Level Difference. */
193
194static void a3dsrc_SetGainTarget(a3dsrc_t * a, short left, short right)
195{
196	vortex_t *vortex = (vortex_t *) (a->vortex);
197	hwwrite(vortex->mmio,
198		a3d_addrB(a->slice, a->source, A3D_B_GainTarget),
199		(right << 0x10) | left);
200}
201
202static void a3dsrc_SetGainCurrent(a3dsrc_t * a, short left, short right)
203{
204	vortex_t *vortex = (vortex_t *) (a->vortex);
205	hwwrite(vortex->mmio,
206		a3d_addrB(a->slice, a->source, A3D_B_GainCurrent),
207		(right << 0x10) | left);
208}
209
210/* Generic A3D stuff */
211
212static void a3dsrc_SetA3DSampleRate(a3dsrc_t * a, int sr)
213{
214	vortex_t *vortex = (vortex_t *) (a->vortex);
215	int esp0 = 0;
216
217	esp0 = (((esp0 & 0x7fffffff) | 0xB8000000) & 0x7) | ((sr & 0x1f) << 3);
218	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd), esp0);
219	//hwwrite(vortex->mmio, 0x19C38 + (this08<<0xd), esp0);
220}
221
222static void a3dsrc_EnableA3D(a3dsrc_t * a)
223{
224	vortex_t *vortex = (vortex_t *) (a->vortex);
225	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd),
226		0xF0000001);
227	//hwwrite(vortex->mmio, 0x19C38 + (this08<<0xd), 0xF0000001);
228}
229
230static void a3dsrc_DisableA3D(a3dsrc_t * a)
231{
232	vortex_t *vortex = (vortex_t *) (a->vortex);
233	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd),
234		0xF0000000);
235}
236
237static void a3dsrc_SetA3DControlReg(a3dsrc_t * a, unsigned long ctrl)
238{
239	vortex_t *vortex = (vortex_t *) (a->vortex);
240	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd), ctrl);
241}
242
243static void a3dsrc_SetA3DPointerReg(a3dsrc_t * a, unsigned long ptr)
244{
245	vortex_t *vortex = (vortex_t *) (a->vortex);
246	hwwrite(vortex->mmio, A3D_SLICE_Pointers + ((a->slice) << 0xd), ptr);
247}
248
249static void a3dsrc_ZeroSliceIO(a3dsrc_t * a)
250{
251	vortex_t *vortex = (vortex_t *) (a->vortex);
252	int i;
253
254	for (i = 0; i < 8; i++)
255		hwwrite(vortex->mmio,
256			A3D_SLICE_VDBDest +
257			((((a->slice) << 0xb) + i) << 2), 0);
258	for (i = 0; i < 4; i++)
259		hwwrite(vortex->mmio,
260			A3D_SLICE_VDBSource +
261			((((a->slice) << 0xb) + i) << 2), 0);
262}
263
264/* Reset Single A3D source. */
265static void a3dsrc_ZeroState(a3dsrc_t * a)
266{
267	/*
268	printk(KERN_DEBUG "vortex: ZeroState slice: %d, source %d\n",
269	       a->slice, a->source);
270	*/
271	a3dsrc_SetAtmosState(a, 0, 0, 0, 0);
272	a3dsrc_SetHrtfState(a, A3dHrirZeros, A3dHrirZeros);
273	a3dsrc_SetItdDline(a, A3dItdDlineZeros);
274	a3dsrc_SetHrtfOutput(a, 0, 0);
275	a3dsrc_SetTimeConsts(a, 0, 0, 0, 0);
276
277	a3dsrc_SetAtmosCurrent(a, 0, 0, 0, 0, 0);
278	a3dsrc_SetAtmosTarget(a, 0, 0, 0, 0, 0);
279	a3dsrc_SetItdCurrent(a, 0, 0);
280	a3dsrc_SetItdTarget(a, 0, 0);
281	a3dsrc_SetGainCurrent(a, 0, 0);
282	a3dsrc_SetGainTarget(a, 0, 0);
283
284	a3dsrc_SetHrtfCurrent(a, A3dHrirZeros, A3dHrirZeros);
285	a3dsrc_SetHrtfTarget(a, A3dHrirZeros, A3dHrirZeros);
286}
287
288/* Reset entire A3D engine */
289static void a3dsrc_ZeroStateA3D(a3dsrc_t * a)
290{
291	int i, var, var2;
292
293	if ((a->vortex) == NULL) {
294		printk(KERN_ERR "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
295		return;
296	}
297
298	a3dsrc_SetA3DControlReg(a, 0);
299	a3dsrc_SetA3DPointerReg(a, 0);
300
301	var = a->slice;
302	var2 = a->source;
303	for (i = 0; i < 4; i++) {
304		a->slice = i;
305		a3dsrc_ZeroSliceIO(a);
306		//a3dsrc_ZeroState(a);
307	}
308	a->source = var2;
309	a->slice = var;
310}
311
312/* Program A3D block as pass through */
313static void a3dsrc_ProgramPipe(a3dsrc_t * a)
314{
315	a3dsrc_SetTimeConsts(a, 0, 0, 0, 0);
316	a3dsrc_SetAtmosCurrent(a, 0, 0x4000, 0, 0, 0);
317	a3dsrc_SetAtmosTarget(a, 0x4000, 0, 0, 0, 0);
318	a3dsrc_SetItdCurrent(a, 0, 0);
319	a3dsrc_SetItdTarget(a, 0, 0);
320	a3dsrc_SetGainCurrent(a, 0x7fff, 0x7fff);
321	a3dsrc_SetGainTarget(a, 0x7fff, 0x7fff);
322
323	/* SET HRTF HERE */
324
325	/* Single spike leads to identity transfer function. */
326	a3dsrc_SetHrtfCurrent(a, A3dHrirImpulse, A3dHrirImpulse);
327	a3dsrc_SetHrtfTarget(a, A3dHrirImpulse, A3dHrirImpulse);
328
329	/* Test: Sounds saturated. */
330	//a3dsrc_SetHrtfCurrent(a, A3dHrirSatTest, A3dHrirSatTest);
331	//a3dsrc_SetHrtfTarget(a, A3dHrirSatTest, A3dHrirSatTest);
332}
333
334/* VDB = Vortex audio Dataflow Bus */
335
336/* A3D HwSource stuff. */
337
338static void vortex_A3dSourceHw_Initialize(vortex_t * v, int source, int slice)
339{
340	a3dsrc_t *a3dsrc = &(v->a3d[source + (slice * 4)]);
341	//a3dsrc_t *a3dsrc = &(v->a3d[source + (slice*4)]);
342
343	a3dsrc->vortex = (void *)v;
344	a3dsrc->source = source;	/* source */
345	a3dsrc->slice = slice;	/* slice */
346	a3dsrc_ZeroState(a3dsrc);
347	/* Added by me. */
348	a3dsrc_SetA3DSampleRate(a3dsrc, 0x11);
349}
350
351static int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
352{
353	v->xt_mode = mode;	/* this_14 */
354
355	vortex_XtalkHw_init(v);
356	vortex_XtalkHw_SetGainsAllChan(v);
357	switch (v->xt_mode) {
358	case XT_SPEAKER0:
359		vortex_XtalkHw_ProgramXtalkNarrow(v);
360		break;
361	case XT_SPEAKER1:
362		vortex_XtalkHw_ProgramXtalkWide(v);
363		break;
364	default:
365	case XT_HEADPHONE:
366		vortex_XtalkHw_ProgramPipe(v);
367		break;
368	case XT_DIAMOND:
369		vortex_XtalkHw_ProgramDiamondXtalk(v);
370		break;
371	}
372	vortex_XtalkHw_SetSampleRate(v, 0x11);
373	vortex_XtalkHw_Enable(v);
374	return 0;
375}
376
377/* 3D Sound entry points. */
378
379static int vortex_a3d_register_controls(vortex_t * vortex);
380static void vortex_a3d_unregister_controls(vortex_t * vortex);
381/* A3D base support init/shudown */
382static void __devinit vortex_Vort3D_enable(vortex_t * v)
383{
384	int i;
385
386	Vort3DRend_Initialize(v, XT_HEADPHONE);
387	for (i = 0; i < NR_A3D; i++) {
388		vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2);
389		a3dsrc_ZeroStateA3D(&(v->a3d[0]));
390	}
391	/* Register ALSA controls */
392	vortex_a3d_register_controls(v);
393}
394
395static void vortex_Vort3D_disable(vortex_t * v)
396{
397	vortex_XtalkHw_Disable(v);
398	vortex_a3d_unregister_controls(v);
399}
400
401/* Make A3D subsystem connections. */
402static void vortex_Vort3D_connect(vortex_t * v, int en)
403{
404	int i;
405
406// Disable AU8810 routes, since they seem to be wrong (in au8810.h).
407#ifdef CHIP_AU8810
408	return;
409#endif
410
411	/* Alloc Xtalk mixin resources */
412	v->mixxtlk[0] =
413	    vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
414	if (v->mixxtlk[0] < 0) {
415		printk
416		    ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
417		return;
418	}
419	v->mixxtlk[1] =
420	    vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
421	if (v->mixxtlk[1] < 0) {
422		printk
423		    ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
424		return;
425	}
426
427	/* Connect A3D -> XTALK */
428	for (i = 0; i < 4; i++) {
429		// 2 outputs per each A3D slice.
430		vortex_route(v, en, 0x11, ADB_A3DOUT(i * 2), ADB_XTALKIN(i));
431		vortex_route(v, en, 0x11, ADB_A3DOUT(i * 2) + 1, ADB_XTALKIN(5 + i));
432	}
433	/* Connect XTalk -> mixer */
434	vortex_route(v, en, 0x11, ADB_XTALKOUT(0), ADB_MIXIN(v->mixxtlk[0]));
435	vortex_route(v, en, 0x11, ADB_XTALKOUT(1), ADB_MIXIN(v->mixxtlk[1]));
436	vortex_connection_mixin_mix(v, en, v->mixxtlk[0], v->mixplayb[0], 0);
437	vortex_connection_mixin_mix(v, en, v->mixxtlk[1], v->mixplayb[1], 0);
438	vortex_mix_setinputvolumebyte(v, v->mixplayb[0], v->mixxtlk[0],
439				      en ? MIX_DEFIGAIN : VOL_MIN);
440	vortex_mix_setinputvolumebyte(v, v->mixplayb[1], v->mixxtlk[1],
441				      en ? MIX_DEFIGAIN : VOL_MIN);
442	if (VORTEX_IS_QUAD(v)) {
443		vortex_connection_mixin_mix(v, en, v->mixxtlk[0],
444					    v->mixplayb[2], 0);
445		vortex_connection_mixin_mix(v, en, v->mixxtlk[1],
446					    v->mixplayb[3], 0);
447		vortex_mix_setinputvolumebyte(v, v->mixplayb[2],
448					      v->mixxtlk[0],
449					      en ? MIX_DEFIGAIN : VOL_MIN);
450		vortex_mix_setinputvolumebyte(v, v->mixplayb[3],
451					      v->mixxtlk[1],
452					      en ? MIX_DEFIGAIN : VOL_MIN);
453	}
454}
455
456/* Initialize one single A3D source. */
457static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en)
458{
459	if (a->vortex == NULL) {
460		printk
461		    ("vortex: Vort3D_InitializeSource: A3D source not initialized\n");
462		return;
463	}
464	if (en) {
465		a3dsrc_ProgramPipe(a);
466		a3dsrc_SetA3DSampleRate(a, 0x11);
467		a3dsrc_SetTimeConsts(a, HrtfTCDefault,
468				     ItdTCDefault, GainTCDefault,
469				     CoefTCDefault);
470		/* Remark: zero gain is muted. */
471		//a3dsrc_SetGainTarget(a,0,0);
472		//a3dsrc_SetGainCurrent(a,0,0);
473		a3dsrc_EnableA3D(a);
474	} else {
475		a3dsrc_DisableA3D(a);
476		a3dsrc_ZeroState(a);
477	}
478}
479
480/* Conversion of coordinates into 3D parameters. */
481
482static void vortex_a3d_coord2hrtf(a3d_Hrtf_t hrtf, int *coord)
483{
484
485}
486static void vortex_a3d_coord2itd(a3d_Itd_t itd, int *coord)
487{
488
489}
490static void vortex_a3d_coord2ild(a3d_LRGains_t ild, int left, int right)
491{
492
493}
494static void vortex_a3d_translate_filter(a3d_atmos_t filter, int *params)
495{
496
497}
498
499/* ALSA control interface.  */
500
501static int
502snd_vortex_a3d_hrtf_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
503{
504	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
505	uinfo->count = 6;
506	uinfo->value.integer.min = 0x00000000;
507	uinfo->value.integer.max = 0xffffffff;
508	return 0;
509}
510static int
511snd_vortex_a3d_itd_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
512{
513	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
514	uinfo->count = 2;
515	uinfo->value.integer.min = 0x00000000;
516	uinfo->value.integer.max = 0xffffffff;
517	return 0;
518}
519static int
520snd_vortex_a3d_ild_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
521{
522	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
523	uinfo->count = 2;
524	uinfo->value.integer.min = 0x00000000;
525	uinfo->value.integer.max = 0xffffffff;
526	return 0;
527}
528static int
529snd_vortex_a3d_filter_info(struct snd_kcontrol *kcontrol,
530			   struct snd_ctl_elem_info *uinfo)
531{
532	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
533	uinfo->count = 4;
534	uinfo->value.integer.min = 0x00000000;
535	uinfo->value.integer.max = 0xffffffff;
536	return 0;
537}
538
539static int
540snd_vortex_a3d_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
541{
542	//a3dsrc_t *a = kcontrol->private_data;
543	/* No read yet. Would this be really useable/needed ? */
544
545	return 0;
546}
547
548static int
549snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol,
550			struct snd_ctl_elem_value *ucontrol)
551{
552	a3dsrc_t *a = kcontrol->private_data;
553	int changed = 1, i;
554	int coord[6];
555	for (i = 0; i < 6; i++)
556		coord[i] = ucontrol->value.integer.value[i];
557	/* Translate orientation coordinates to a3d params. */
558	vortex_a3d_coord2hrtf(a->hrtf[0], coord);
559	vortex_a3d_coord2hrtf(a->hrtf[1], coord);
560	a3dsrc_SetHrtfTarget(a, a->hrtf[0], a->hrtf[1]);
561	a3dsrc_SetHrtfCurrent(a, a->hrtf[0], a->hrtf[1]);
562	return changed;
563}
564
565static int
566snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol,
567		       struct snd_ctl_elem_value *ucontrol)
568{
569	a3dsrc_t *a = kcontrol->private_data;
570	int coord[6];
571	int i, changed = 1;
572	for (i = 0; i < 6; i++)
573		coord[i] = ucontrol->value.integer.value[i];
574	/* Translate orientation coordinates to a3d params. */
575	vortex_a3d_coord2itd(a->hrtf[0], coord);
576	vortex_a3d_coord2itd(a->hrtf[1], coord);
577	/* Inter aural time difference. */
578	a3dsrc_SetItdTarget(a, a->itd[0], a->itd[1]);
579	a3dsrc_SetItdCurrent(a, a->itd[0], a->itd[1]);
580	a3dsrc_SetItdDline(a, a->dline);
581	return changed;
582}
583
584static int
585snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol,
586		       struct snd_ctl_elem_value *ucontrol)
587{
588	a3dsrc_t *a = kcontrol->private_data;
589	int changed = 1;
590	int l, r;
591	/* There may be some scale tranlation needed here. */
592	l = ucontrol->value.integer.value[0];
593	r = ucontrol->value.integer.value[1];
594	vortex_a3d_coord2ild(a->ild, l, r);
595	/* Left Right panning. */
596	a3dsrc_SetGainTarget(a, l, r);
597	a3dsrc_SetGainCurrent(a, l, r);
598	return changed;
599}
600
601static int
602snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol,
603			  struct snd_ctl_elem_value *ucontrol)
604{
605	a3dsrc_t *a = kcontrol->private_data;
606	int i, changed = 1;
607	int params[6];
608	for (i = 0; i < 6; i++)
609		params[i] = ucontrol->value.integer.value[i];
610	/* Translate generic filter params to a3d filter params. */
611	vortex_a3d_translate_filter(a->filter, params);
612	/* Atmospheric absorbtion and filtering. */
613	a3dsrc_SetAtmosTarget(a, a->filter[0],
614			      a->filter[1], a->filter[2],
615			      a->filter[3], a->filter[4]);
616	a3dsrc_SetAtmosCurrent(a, a->filter[0],
617			       a->filter[1], a->filter[2],
618			       a->filter[3], a->filter[4]);
619	return changed;
620}
621
622static struct snd_kcontrol_new vortex_a3d_kcontrol __devinitdata = {
623	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
624	.name = "Playback PCM advanced processing",
625	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
626	.info = snd_vortex_a3d_hrtf_info,
627	.get = snd_vortex_a3d_get,
628	.put = snd_vortex_a3d_hrtf_put,
629};
630
631/* Control (un)registration. */
632static int __devinit vortex_a3d_register_controls(vortex_t * vortex)
633{
634	struct snd_kcontrol *kcontrol;
635	int err, i;
636	/* HRTF controls. */
637	for (i = 0; i < NR_A3D; i++) {
638		if ((kcontrol =
639		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
640			return -ENOMEM;
641		kcontrol->id.numid = CTRLID_HRTF;
642		kcontrol->info = snd_vortex_a3d_hrtf_info;
643		kcontrol->put = snd_vortex_a3d_hrtf_put;
644		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
645			return err;
646	}
647	/* ITD controls. */
648	for (i = 0; i < NR_A3D; i++) {
649		if ((kcontrol =
650		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
651			return -ENOMEM;
652		kcontrol->id.numid = CTRLID_ITD;
653		kcontrol->info = snd_vortex_a3d_itd_info;
654		kcontrol->put = snd_vortex_a3d_itd_put;
655		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
656			return err;
657	}
658	/* ILD (gains) controls. */
659	for (i = 0; i < NR_A3D; i++) {
660		if ((kcontrol =
661		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
662			return -ENOMEM;
663		kcontrol->id.numid = CTRLID_GAINS;
664		kcontrol->info = snd_vortex_a3d_ild_info;
665		kcontrol->put = snd_vortex_a3d_ild_put;
666		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
667			return err;
668	}
669	/* Filter controls. */
670	for (i = 0; i < NR_A3D; i++) {
671		if ((kcontrol =
672		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
673			return -ENOMEM;
674		kcontrol->id.numid = CTRLID_FILTER;
675		kcontrol->info = snd_vortex_a3d_filter_info;
676		kcontrol->put = snd_vortex_a3d_filter_put;
677		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
678			return err;
679	}
680	return 0;
681}
682
683static void vortex_a3d_unregister_controls(vortex_t * vortex)
684{
685
686}
687
688/* End of File*/
689