1193640Sariff/*-
2193640Sariff * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
3193640Sariff * All rights reserved.
4193640Sariff *
5193640Sariff * Redistribution and use in source and binary forms, with or without
6193640Sariff * modification, are permitted provided that the following conditions
7193640Sariff * are met:
8193640Sariff * 1. Redistributions of source code must retain the above copyright
9193640Sariff *    notice, this list of conditions and the following disclaimer.
10193640Sariff * 2. Redistributions in binary form must reproduce the above copyright
11193640Sariff *    notice, this list of conditions and the following disclaimer in the
12193640Sariff *    documentation and/or other materials provided with the distribution.
13193640Sariff *
14193640Sariff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15193640Sariff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16193640Sariff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17193640Sariff * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18193640Sariff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19193640Sariff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20193640Sariff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21193640Sariff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22193640Sariff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23193640Sariff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24193640Sariff * SUCH DAMAGE.
25193640Sariff */
26193640Sariff
27193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
28193640Sariff#include "opt_snd.h"
29193640Sariff#endif
30193640Sariff
31193640Sariff#include <dev/sound/pcm/sound.h>
32193640Sariff
33193640Sariff#include "feeder_if.h"
34193640Sariff
35193640SariffSND_DECLARE_FILE("$FreeBSD$");
36193640Sariff
37193640Sariff/* chain state */
38193640Sariffstruct feeder_chain_state {
39193640Sariff	uint32_t afmt;				/* audio format */
40193640Sariff	uint32_t rate;				/* sampling rate */
41193640Sariff	struct pcmchan_matrix *matrix;		/* matrix map */
42193640Sariff};
43193640Sariff
44193640Sariff/*
45193640Sariff * chain descriptor that will be passed around from the beginning until the
46193640Sariff * end of chain process.
47193640Sariff */
48193640Sariffstruct feeder_chain_desc {
49193640Sariff	struct feeder_chain_state origin;	/* original state */
50193640Sariff	struct feeder_chain_state current;	/* current state */
51193640Sariff	struct feeder_chain_state target;	/* target state */
52193640Sariff	struct pcm_feederdesc desc;		/* feeder descriptor */
53193640Sariff	uint32_t afmt_ne;			/* prefered native endian */
54193640Sariff	int mode;				/* chain mode */
55193640Sariff	int use_eq;				/* need EQ? */
56193640Sariff	int use_matrix;				/* need channel matrixing? */
57193640Sariff	int use_volume;				/* need softpcmvol? */
58193640Sariff	int dummy;				/* dummy passthrough */
59193640Sariff	int expensive;				/* possibly expensive */
60193640Sariff};
61193640Sariff
62193640Sariff#define FEEDER_CHAIN_LEAN		0
63193640Sariff#define FEEDER_CHAIN_16			1
64193640Sariff#define FEEDER_CHAIN_32			2
65193640Sariff#define FEEDER_CHAIN_MULTI		3
66193640Sariff#define FEEDER_CHAIN_FULLMULTI		4
67193640Sariff#define FEEDER_CHAIN_LAST		5
68193640Sariff
69193640Sariff#if defined(SND_FEEDER_FULL_MULTIFORMAT)
70193640Sariff#define FEEDER_CHAIN_DEFAULT		FEEDER_CHAIN_FULLMULTI
71193640Sariff#elif defined(SND_FEEDER_MULTIFORMAT)
72193640Sariff#define FEEDER_CHAIN_DEFAULT		FEEDER_CHAIN_MULTI
73193640Sariff#else
74193640Sariff#define FEEDER_CHAIN_DEFAULT		FEEDER_CHAIN_LEAN
75193640Sariff#endif
76193640Sariff
77193640Sariff/*
78193640Sariff * List of prefered formats that might be required during
79193640Sariff * processing. It will be decided through snd_fmtbest().
80193640Sariff */
81193640Sariff
82193640Sariff/* 'Lean' mode, signed 16 or 32 bit native endian. */
83193640Sariffstatic uint32_t feeder_chain_formats_lean[] = {
84193640Sariff	AFMT_S16_NE, AFMT_S32_NE,
85193640Sariff	0
86193640Sariff};
87193640Sariff
88193640Sariff/* Force everything to signed 16 bit native endian. */
89193640Sariffstatic uint32_t feeder_chain_formats_16[] = {
90193640Sariff	AFMT_S16_NE,
91193640Sariff	0
92193640Sariff};
93193640Sariff
94193640Sariff/* Force everything to signed 32 bit native endian. */
95193640Sariffstatic uint32_t feeder_chain_formats_32[] = {
96193640Sariff	AFMT_S32_NE,
97193640Sariff	0
98193640Sariff};
99193640Sariff
100193640Sariff/* Multiple choices, all except 8 bit. */
101193640Sariffstatic uint32_t feeder_chain_formats_multi[] = {
102193640Sariff	AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE,
103193640Sariff	AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE,
104193640Sariff	AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE,
105193640Sariff	0
106193640Sariff};
107193640Sariff
108193640Sariff/* Everything that is convertible. */
109193640Sariffstatic uint32_t feeder_chain_formats_fullmulti[] = {
110193640Sariff	AFMT_S8, AFMT_U8,
111193640Sariff	AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE,
112193640Sariff	AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE,
113193640Sariff	AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE,
114193640Sariff	0
115193640Sariff};
116193640Sariff
117193640Sariffstatic uint32_t *feeder_chain_formats[FEEDER_CHAIN_LAST] = {
118193640Sariff	[FEEDER_CHAIN_LEAN]      = feeder_chain_formats_lean,
119193640Sariff	[FEEDER_CHAIN_16]        = feeder_chain_formats_16,
120193640Sariff	[FEEDER_CHAIN_32]        = feeder_chain_formats_32,
121193640Sariff	[FEEDER_CHAIN_MULTI]     = feeder_chain_formats_multi,
122193640Sariff	[FEEDER_CHAIN_FULLMULTI] = feeder_chain_formats_fullmulti
123193640Sariff};
124193640Sariff
125193640Sariffstatic int feeder_chain_mode = FEEDER_CHAIN_DEFAULT;
126193640Sariff
127193640Sariff#if defined(_KERNEL) && defined(SND_DEBUG) && defined(SND_FEEDER_FULL_MULTIFORMAT)
128267992ShselaskySYSCTL_INT(_hw_snd, OID_AUTO, feeder_chain_mode, CTLFLAG_RWTUN,
129193640Sariff    &feeder_chain_mode, 0,
130193640Sariff    "feeder chain mode "
131193640Sariff    "(0=lean, 1=16bit, 2=32bit, 3=multiformat, 4=fullmultiformat)");
132193640Sariff#endif
133193640Sariff
134193640Sariff/*
135193640Sariff * feeder_build_format(): Chain any format converter.
136193640Sariff */
137193640Sariffstatic int
138193640Sarifffeeder_build_format(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
139193640Sariff{
140193640Sariff	struct feeder_class *fc;
141193640Sariff	struct pcm_feederdesc *desc;
142193640Sariff	int ret;
143193640Sariff
144193640Sariff	desc = &(cdesc->desc);
145193640Sariff	desc->type = FEEDER_FORMAT;
146193640Sariff	desc->in = 0;
147193640Sariff	desc->out = 0;
148193640Sariff	desc->flags = 0;
149193640Sariff
150193640Sariff	fc = feeder_getclass(desc);
151193640Sariff	if (fc == NULL) {
152193640Sariff		device_printf(c->dev,
153193640Sariff		    "%s(): can't find feeder_format\n", __func__);
154193640Sariff		return (ENOTSUP);
155193640Sariff	}
156193640Sariff
157193640Sariff	desc->in = cdesc->current.afmt;
158193640Sariff	desc->out = cdesc->target.afmt;
159193640Sariff
160193640Sariff	ret = chn_addfeeder(c, fc, desc);
161193640Sariff	if (ret != 0) {
162193640Sariff		device_printf(c->dev,
163193640Sariff		    "%s(): can't add feeder_format\n", __func__);
164193640Sariff		return (ret);
165193640Sariff	}
166193640Sariff
167193640Sariff	c->feederflags |= 1 << FEEDER_FORMAT;
168193640Sariff
169193640Sariff	cdesc->current.afmt = cdesc->target.afmt;
170193640Sariff
171193640Sariff	return (0);
172193640Sariff}
173193640Sariff
174193640Sariff/*
175193640Sariff * feeder_build_formatne(): Chain format converter that suite best for native
176193640Sariff *                          endian format.
177193640Sariff */
178193640Sariffstatic int
179193640Sarifffeeder_build_formatne(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
180193640Sariff{
181193640Sariff	struct feeder_chain_state otarget;
182193640Sariff	int ret;
183193640Sariff
184193640Sariff	if (cdesc->afmt_ne == 0 ||
185193640Sariff	    AFMT_ENCODING(cdesc->current.afmt) == cdesc->afmt_ne)
186193640Sariff		return (0);
187193640Sariff
188193640Sariff	otarget = cdesc->target;
189193640Sariff	cdesc->target = cdesc->current;
190193640Sariff	cdesc->target.afmt = SND_FORMAT(cdesc->afmt_ne,
191193640Sariff	    cdesc->current.matrix->channels, cdesc->current.matrix->ext);
192193640Sariff
193193640Sariff	ret = feeder_build_format(c, cdesc);
194193640Sariff	if (ret != 0)
195193640Sariff		return (ret);
196193640Sariff
197193640Sariff	cdesc->target = otarget;
198193640Sariff
199193640Sariff	return (0);
200193640Sariff}
201193640Sariff
202193640Sariff/*
203193640Sariff * feeder_build_rate(): Chain sample rate converter.
204193640Sariff */
205193640Sariffstatic int
206193640Sarifffeeder_build_rate(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
207193640Sariff{
208193640Sariff	struct feeder_class *fc;
209193640Sariff	struct pcm_feeder *f;
210193640Sariff	struct pcm_feederdesc *desc;
211193640Sariff	int ret;
212193640Sariff
213193640Sariff	ret = feeder_build_formatne(c, cdesc);
214193640Sariff	if (ret != 0)
215193640Sariff		return (ret);
216193640Sariff
217193640Sariff	desc = &(cdesc->desc);
218193640Sariff	desc->type = FEEDER_RATE;
219193640Sariff	desc->in = 0;
220193640Sariff	desc->out = 0;
221193640Sariff	desc->flags = 0;
222193640Sariff
223193640Sariff	fc = feeder_getclass(desc);
224193640Sariff	if (fc == NULL) {
225193640Sariff		device_printf(c->dev,
226193640Sariff		    "%s(): can't find feeder_rate\n", __func__);
227193640Sariff		return (ENOTSUP);
228193640Sariff	}
229193640Sariff
230193640Sariff	desc->in = cdesc->current.afmt;
231193640Sariff	desc->out = desc->in;
232193640Sariff
233193640Sariff	ret = chn_addfeeder(c, fc, desc);
234193640Sariff	if (ret != 0) {
235193640Sariff		device_printf(c->dev,
236193640Sariff		    "%s(): can't add feeder_rate\n", __func__);
237193640Sariff		return (ret);
238193640Sariff	}
239193640Sariff
240193640Sariff	f = c->feeder;
241193640Sariff
242193640Sariff	/*
243193640Sariff	 * If in 'dummy' mode (possibly due to passthrough mode), set the
244193640Sariff	 * conversion quality to the lowest possible (should be fastest) since
245193640Sariff	 * listener won't be hearing anything. Theoretically we can just
246193640Sariff	 * disable it, but that will cause weird runtime behaviour:
247193640Sariff	 * application appear to play something that is either too fast or too
248193640Sariff	 * slow.
249193640Sariff	 */
250193640Sariff	if (cdesc->dummy != 0) {
251193640Sariff		ret = FEEDER_SET(f, FEEDRATE_QUALITY, 0);
252193640Sariff		if (ret != 0) {
253193640Sariff			device_printf(c->dev,
254193640Sariff			    "%s(): can't set resampling quality\n", __func__);
255193640Sariff			return (ret);
256193640Sariff		}
257193640Sariff	}
258193640Sariff
259193640Sariff	ret = FEEDER_SET(f, FEEDRATE_SRC, cdesc->current.rate);
260193640Sariff	if (ret != 0) {
261193640Sariff		device_printf(c->dev,
262193640Sariff		    "%s(): can't set source rate\n", __func__);
263193640Sariff		return (ret);
264193640Sariff	}
265193640Sariff
266193640Sariff	ret = FEEDER_SET(f, FEEDRATE_DST, cdesc->target.rate);
267193640Sariff	if (ret != 0) {
268193640Sariff		device_printf(c->dev,
269193640Sariff		    "%s(): can't set destination rate\n", __func__);
270193640Sariff		return (ret);
271193640Sariff	}
272193640Sariff
273193640Sariff	c->feederflags |= 1 << FEEDER_RATE;
274193640Sariff
275193640Sariff	cdesc->current.rate = cdesc->target.rate;
276193640Sariff
277193640Sariff	return (0);
278193640Sariff}
279193640Sariff
280193640Sariff/*
281193640Sariff * feeder_build_matrix(): Chain channel matrixing converter.
282193640Sariff */
283193640Sariffstatic int
284193640Sarifffeeder_build_matrix(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
285193640Sariff{
286193640Sariff	struct feeder_class *fc;
287193640Sariff	struct pcm_feeder *f;
288193640Sariff	struct pcm_feederdesc *desc;
289193640Sariff	int ret;
290193640Sariff
291193640Sariff	ret = feeder_build_formatne(c, cdesc);
292193640Sariff	if (ret != 0)
293193640Sariff		return (ret);
294193640Sariff
295193640Sariff	desc = &(cdesc->desc);
296193640Sariff	desc->type = FEEDER_MATRIX;
297193640Sariff	desc->in = 0;
298193640Sariff	desc->out = 0;
299193640Sariff	desc->flags = 0;
300193640Sariff
301193640Sariff	fc = feeder_getclass(desc);
302193640Sariff	if (fc == NULL) {
303193640Sariff		device_printf(c->dev,
304193640Sariff		    "%s(): can't find feeder_matrix\n", __func__);
305193640Sariff		return (ENOTSUP);
306193640Sariff	}
307193640Sariff
308193640Sariff	desc->in = cdesc->current.afmt;
309193640Sariff	desc->out = SND_FORMAT(cdesc->current.afmt,
310193640Sariff	    cdesc->target.matrix->channels, cdesc->target.matrix->ext);
311193640Sariff
312193640Sariff	ret = chn_addfeeder(c, fc, desc);
313193640Sariff	if (ret != 0) {
314193640Sariff		device_printf(c->dev,
315193640Sariff		    "%s(): can't add feeder_matrix\n", __func__);
316193640Sariff		return (ret);
317193640Sariff	}
318193640Sariff
319193640Sariff	f = c->feeder;
320193640Sariff	ret = feeder_matrix_setup(f, cdesc->current.matrix,
321193640Sariff	    cdesc->target.matrix);
322193640Sariff	if (ret != 0) {
323193640Sariff		device_printf(c->dev,
324193640Sariff		    "%s(): feeder_matrix_setup() failed\n", __func__);
325193640Sariff		return (ret);
326193640Sariff	}
327193640Sariff
328193640Sariff	c->feederflags |= 1 << FEEDER_MATRIX;
329193640Sariff
330193640Sariff	cdesc->current.afmt = desc->out;
331193640Sariff	cdesc->current.matrix = cdesc->target.matrix;
332193640Sariff	cdesc->use_matrix = 0;
333193640Sariff
334193640Sariff	return (0);
335193640Sariff}
336193640Sariff
337193640Sariff/*
338193640Sariff * feeder_build_volume(): Chain soft volume.
339193640Sariff */
340193640Sariffstatic int
341193640Sarifffeeder_build_volume(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
342193640Sariff{
343193640Sariff	struct feeder_class *fc;
344193640Sariff	struct pcm_feeder *f;
345193640Sariff	struct pcm_feederdesc *desc;
346193640Sariff	int ret;
347193640Sariff
348193640Sariff	ret = feeder_build_formatne(c, cdesc);
349193640Sariff	if (ret != 0)
350193640Sariff		return (ret);
351193640Sariff
352193640Sariff	desc = &(cdesc->desc);
353193640Sariff	desc->type = FEEDER_VOLUME;
354193640Sariff	desc->in = 0;
355193640Sariff	desc->out = 0;
356193640Sariff	desc->flags = 0;
357193640Sariff
358193640Sariff	fc = feeder_getclass(desc);
359193640Sariff	if (fc == NULL) {
360193640Sariff		device_printf(c->dev,
361193640Sariff		    "%s(): can't find feeder_volume\n", __func__);
362193640Sariff		return (ENOTSUP);
363193640Sariff	}
364193640Sariff
365193640Sariff	desc->in = cdesc->current.afmt;
366193640Sariff	desc->out = desc->in;
367193640Sariff
368193640Sariff	ret = chn_addfeeder(c, fc, desc);
369193640Sariff	if (ret != 0) {
370193640Sariff		device_printf(c->dev,
371193640Sariff		    "%s(): can't add feeder_volume\n", __func__);
372193640Sariff		return (ret);
373193640Sariff	}
374193640Sariff
375193640Sariff	f = c->feeder;
376193640Sariff
377193640Sariff	/*
378193640Sariff	 * If in 'dummy' mode (possibly due to passthrough mode), set BYPASS
379193640Sariff	 * mode since listener won't be hearing anything. Theoretically we can
380193640Sariff	 * just disable it, but that will confuse volume per channel mixer.
381193640Sariff	 */
382193640Sariff	if (cdesc->dummy != 0) {
383193640Sariff		ret = FEEDER_SET(f, FEEDVOLUME_STATE, FEEDVOLUME_BYPASS);
384193640Sariff		if (ret != 0) {
385193640Sariff			device_printf(c->dev,
386193640Sariff			    "%s(): can't set volume bypass\n", __func__);
387193640Sariff			return (ret);
388193640Sariff		}
389193640Sariff	}
390193640Sariff
391193640Sariff	ret = feeder_volume_apply_matrix(f, cdesc->current.matrix);
392193640Sariff	if (ret != 0) {
393193640Sariff		device_printf(c->dev,
394193640Sariff		    "%s(): feeder_volume_apply_matrix() failed\n", __func__);
395193640Sariff		return (ret);
396193640Sariff	}
397193640Sariff
398193640Sariff	c->feederflags |= 1 << FEEDER_VOLUME;
399193640Sariff
400193640Sariff	cdesc->use_volume = 0;
401193640Sariff
402193640Sariff	return (0);
403193640Sariff}
404193640Sariff
405193640Sariff/*
406193640Sariff * feeder_build_eq(): Chain parametric software equalizer.
407193640Sariff */
408193640Sariffstatic int
409193640Sarifffeeder_build_eq(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
410193640Sariff{
411193640Sariff	struct feeder_class *fc;
412193640Sariff	struct pcm_feeder *f;
413193640Sariff	struct pcm_feederdesc *desc;
414193640Sariff	int ret;
415193640Sariff
416193640Sariff	ret = feeder_build_formatne(c, cdesc);
417193640Sariff	if (ret != 0)
418193640Sariff		return (ret);
419193640Sariff
420193640Sariff	desc = &(cdesc->desc);
421193640Sariff	desc->type = FEEDER_EQ;
422193640Sariff	desc->in = 0;
423193640Sariff	desc->out = 0;
424193640Sariff	desc->flags = 0;
425193640Sariff
426193640Sariff	fc = feeder_getclass(desc);
427193640Sariff	if (fc == NULL) {
428193640Sariff		device_printf(c->dev,
429193640Sariff		    "%s(): can't find feeder_eq\n", __func__);
430193640Sariff		return (ENOTSUP);
431193640Sariff	}
432193640Sariff
433193640Sariff	desc->in = cdesc->current.afmt;
434193640Sariff	desc->out = desc->in;
435193640Sariff
436193640Sariff	ret = chn_addfeeder(c, fc, desc);
437193640Sariff	if (ret != 0) {
438193640Sariff		device_printf(c->dev,
439193640Sariff		    "%s(): can't add feeder_eq\n", __func__);
440193640Sariff		return (ret);
441193640Sariff	}
442193640Sariff
443193640Sariff	f = c->feeder;
444193640Sariff
445193640Sariff	ret = FEEDER_SET(f, FEEDEQ_RATE, cdesc->current.rate);
446193640Sariff	if (ret != 0) {
447193640Sariff		device_printf(c->dev,
448193640Sariff		    "%s(): can't set rate on feeder_eq\n", __func__);
449193640Sariff		return (ret);
450193640Sariff	}
451193640Sariff
452193640Sariff	c->feederflags |= 1 << FEEDER_EQ;
453193640Sariff
454193640Sariff	cdesc->use_eq = 0;
455193640Sariff
456193640Sariff	return (0);
457193640Sariff}
458193640Sariff
459193640Sariff/*
460193640Sariff * feeder_build_root(): Chain root feeder, the top, father of all.
461193640Sariff */
462193640Sariffstatic int
463193640Sarifffeeder_build_root(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
464193640Sariff{
465193640Sariff	struct feeder_class *fc;
466193640Sariff	int ret;
467193640Sariff
468193640Sariff	fc = feeder_getclass(NULL);
469193640Sariff	if (fc == NULL) {
470193640Sariff		device_printf(c->dev,
471193640Sariff		    "%s(): can't find feeder_root\n", __func__);
472193640Sariff		return (ENOTSUP);
473193640Sariff	}
474193640Sariff
475193640Sariff	ret = chn_addfeeder(c, fc, NULL);
476193640Sariff	if (ret != 0) {
477193640Sariff		device_printf(c->dev,
478193640Sariff		    "%s(): can't add feeder_root\n", __func__);
479193640Sariff		return (ret);
480193640Sariff	}
481193640Sariff
482193640Sariff	c->feederflags |= 1 << FEEDER_ROOT;
483193640Sariff
484193640Sariff	c->feeder->desc->in = cdesc->current.afmt;
485193640Sariff	c->feeder->desc->out = cdesc->current.afmt;
486193640Sariff
487193640Sariff	return (0);
488193640Sariff}
489193640Sariff
490193640Sariff/*
491193640Sariff * feeder_build_mixer(): Chain software mixer for virtual channels.
492193640Sariff */
493193640Sariffstatic int
494193640Sarifffeeder_build_mixer(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
495193640Sariff{
496193640Sariff	struct feeder_class *fc;
497193640Sariff	struct pcm_feederdesc *desc;
498193640Sariff	int ret;
499193640Sariff
500193640Sariff	desc = &(cdesc->desc);
501193640Sariff	desc->type = FEEDER_MIXER;
502193640Sariff	desc->in = 0;
503193640Sariff	desc->out = 0;
504193640Sariff	desc->flags = 0;
505193640Sariff
506193640Sariff	fc = feeder_getclass(desc);
507193640Sariff	if (fc == NULL) {
508193640Sariff		device_printf(c->dev,
509193640Sariff		    "%s(): can't find feeder_mixer\n", __func__);
510193640Sariff		return (ENOTSUP);
511193640Sariff	}
512193640Sariff
513193640Sariff	desc->in = cdesc->current.afmt;
514193640Sariff	desc->out = desc->in;
515193640Sariff
516193640Sariff	ret = chn_addfeeder(c, fc, desc);
517193640Sariff	if (ret != 0) {
518193640Sariff		device_printf(c->dev,
519193640Sariff		    "%s(): can't add feeder_mixer\n", __func__);
520193640Sariff		return (ret);
521193640Sariff	}
522193640Sariff
523193640Sariff	c->feederflags |= 1 << FEEDER_MIXER;
524193640Sariff
525193640Sariff	return (0);
526193640Sariff}
527193640Sariff
528193640Sariff/* Macrosses to ease our job doing stuffs later. */
529193640Sariff#define FEEDER_BW(c, t)		((c)->t.matrix->channels * (c)->t.rate)
530193640Sariff
531193640Sariff#define FEEDRATE_UP(c)		((c)->target.rate > (c)->current.rate)
532193640Sariff#define FEEDRATE_DOWN(c)	((c)->target.rate < (c)->current.rate)
533193640Sariff#define FEEDRATE_REQUIRED(c)	(FEEDRATE_UP(c) || FEEDRATE_DOWN(c))
534193640Sariff
535193640Sariff#define FEEDMATRIX_UP(c)	((c)->target.matrix->channels >		\
536193640Sariff				 (c)->current.matrix->channels)
537193640Sariff#define FEEDMATRIX_DOWN(c)	((c)->target.matrix->channels <		\
538193640Sariff				 (c)->current.matrix->channels)
539193640Sariff#define FEEDMATRIX_REQUIRED(c)	(FEEDMATRIX_UP(c) ||			\
540193640Sariff				 FEEDMATRIX_DOWN(c) || (c)->use_matrix != 0)
541193640Sariff
542193640Sariff#define FEEDFORMAT_REQUIRED(c)	(AFMT_ENCODING((c)->current.afmt) !=	\
543193640Sariff				 AFMT_ENCODING((c)->target.afmt))
544193640Sariff
545193640Sariff#define FEEDVOLUME_REQUIRED(c)	((c)->use_volume != 0)
546193640Sariff
547193640Sariff#define FEEDEQ_VALIDRATE(c, t)	(feeder_eq_validrate((c)->t.rate) != 0)
548193640Sariff#define FEEDEQ_ECONOMY(c)	(FEEDER_BW(c, current) < FEEDER_BW(c, target))
549193640Sariff#define FEEDEQ_REQUIRED(c)	((c)->use_eq != 0 &&			\
550193640Sariff				 FEEDEQ_VALIDRATE(c, current))
551193640Sariff
552193640Sariff#define FEEDFORMAT_NE_REQUIRED(c)					\
553193640Sariff	((c)->afmt_ne != AFMT_S32_NE &&					\
554193640Sariff	(((c)->mode == FEEDER_CHAIN_16 &&				\
555193640Sariff	AFMT_ENCODING((c)->current.afmt) != AFMT_S16_NE) ||		\
556193640Sariff	((c)->mode == FEEDER_CHAIN_32 &&				\
557193640Sariff	AFMT_ENCODING((c)->current.afmt) != AFMT_S32_NE) ||		\
558193640Sariff	(c)->mode == FEEDER_CHAIN_FULLMULTI ||				\
559193640Sariff	((c)->mode == FEEDER_CHAIN_MULTI &&				\
560193640Sariff	((c)->current.afmt & AFMT_8BIT)) ||				\
561193640Sariff	((c)->mode == FEEDER_CHAIN_LEAN &&				\
562193640Sariff	!((c)->current.afmt & (AFMT_S16_NE | AFMT_S32_NE)))))
563193640Sariff
564282650Shselaskystatic void
565282650Shselaskyfeeder_default_matrix(struct pcmchan_matrix *m, uint32_t fmt, int id)
566282650Shselasky{
567282650Shselasky	int x;
568282650Shselasky
569282650Shselasky	memset(m, 0, sizeof(*m));
570282650Shselasky
571282650Shselasky	m->id = id;
572282650Shselasky	m->channels = AFMT_CHANNEL(fmt);
573282650Shselasky	m->ext = AFMT_EXTCHANNEL(fmt);
574282650Shselasky	for (x = 0; x != SND_CHN_T_MAX; x++)
575282650Shselasky		m->offset[x] = -1;
576282650Shselasky}
577282650Shselasky
578193640Sariffint
579193640Sarifffeeder_chain(struct pcm_channel *c)
580193640Sariff{
581193640Sariff	struct snddev_info *d;
582193640Sariff	struct pcmchan_caps *caps;
583193640Sariff	struct feeder_chain_desc cdesc;
584193640Sariff	struct pcmchan_matrix *hwmatrix, *softmatrix;
585193640Sariff	uint32_t hwfmt, softfmt;
586193640Sariff	int ret;
587193640Sariff
588193640Sariff	CHN_LOCKASSERT(c);
589193640Sariff
590193640Sariff	/* Remove everything first. */
591193640Sariff	while (chn_removefeeder(c) == 0)
592193640Sariff		;
593193640Sariff
594193640Sariff	KASSERT(c->feeder == NULL, ("feeder chain not empty"));
595193640Sariff
596193640Sariff	/* clear and populate chain descriptor. */
597193640Sariff	bzero(&cdesc, sizeof(cdesc));
598193640Sariff
599193640Sariff	switch (feeder_chain_mode) {
600193640Sariff	case FEEDER_CHAIN_LEAN:
601193640Sariff	case FEEDER_CHAIN_16:
602193640Sariff	case FEEDER_CHAIN_32:
603193640Sariff#if defined(SND_FEEDER_MULTIFORMAT) || defined(SND_FEEDER_FULL_MULTIFORMAT)
604193640Sariff	case FEEDER_CHAIN_MULTI:
605193640Sariff#endif
606193640Sariff#if defined(SND_FEEDER_FULL_MULTIFORMAT)
607193640Sariff	case FEEDER_CHAIN_FULLMULTI:
608193640Sariff#endif
609193640Sariff		break;
610193640Sariff	default:
611193640Sariff		feeder_chain_mode = FEEDER_CHAIN_DEFAULT;
612193640Sariff		break;
613193640Sariff	}
614193640Sariff
615193640Sariff	cdesc.mode = feeder_chain_mode;
616193640Sariff	cdesc.expensive = 1;	/* XXX faster.. */
617193640Sariff
618193640Sariff#define VCHAN_PASSTHROUGH(c)	(((c)->flags & (CHN_F_VIRTUAL |		\
619193640Sariff				 CHN_F_PASSTHROUGH)) ==			\
620193640Sariff				 (CHN_F_VIRTUAL | CHN_F_PASSTHROUGH))
621193640Sariff
622193640Sariff	/* Get the best possible hardware format. */
623193640Sariff	if (VCHAN_PASSTHROUGH(c))
624193640Sariff		hwfmt = c->parentchannel->format;
625193640Sariff	else {
626193640Sariff		caps = chn_getcaps(c);
627193640Sariff		if (caps == NULL || caps->fmtlist == NULL) {
628193640Sariff			device_printf(c->dev,
629193640Sariff			    "%s(): failed to get channel caps\n", __func__);
630193640Sariff			return (ENODEV);
631193640Sariff		}
632193640Sariff
633193640Sariff		if ((c->format & AFMT_PASSTHROUGH) &&
634193640Sariff		    !snd_fmtvalid(c->format, caps->fmtlist))
635193640Sariff			return (ENODEV);
636193640Sariff
637193640Sariff		hwfmt = snd_fmtbest(c->format, caps->fmtlist);
638193640Sariff		if (hwfmt == 0 || !snd_fmtvalid(hwfmt, caps->fmtlist)) {
639193640Sariff			device_printf(c->dev,
640193640Sariff			    "%s(): invalid hardware format 0x%08x\n",
641193640Sariff			    __func__, hwfmt);
642193640Sariff			{
643193640Sariff				int i;
644193640Sariff				for (i = 0; caps->fmtlist[i] != 0; i++)
645193640Sariff					printf("0x%08x\n", caps->fmtlist[i]);
646193640Sariff				printf("Req: 0x%08x\n", c->format);
647193640Sariff			}
648193640Sariff			return (ENODEV);
649193640Sariff		}
650193640Sariff	}
651193640Sariff
652193640Sariff	/*
653193640Sariff	 * The 'hardware' possibly have different intepretation of channel
654193640Sariff	 * matrixing, so get it first .....
655193640Sariff	 */
656193640Sariff	hwmatrix = CHANNEL_GETMATRIX(c->methods, c->devinfo, hwfmt);
657193640Sariff	if (hwmatrix == NULL) {
658282650Shselasky		/* setup a default matrix */
659282650Shselasky		hwmatrix = &c->matrix_scratch;
660282650Shselasky		feeder_default_matrix(hwmatrix, hwfmt,
661282650Shselasky		    SND_CHN_MATRIX_UNKNOWN);
662193640Sariff	}
663193640Sariff	/* ..... and rebuild hwfmt. */
664193640Sariff	hwfmt = SND_FORMAT(hwfmt, hwmatrix->channels, hwmatrix->ext);
665193640Sariff
666193640Sariff	/* Reset and rebuild default channel format/matrix map. */
667193640Sariff	softfmt = c->format;
668193640Sariff	softmatrix = &c->matrix;
669193640Sariff	if (softmatrix->channels != AFMT_CHANNEL(softfmt) ||
670193640Sariff	    softmatrix->ext != AFMT_EXTCHANNEL(softfmt)) {
671193640Sariff		softmatrix = feeder_matrix_format_map(softfmt);
672193640Sariff		if (softmatrix == NULL) {
673282650Shselasky			/* setup a default matrix */
674282650Shselasky		  	softmatrix = &c->matrix;
675282650Shselasky			feeder_default_matrix(softmatrix, softfmt,
676282650Shselasky			    SND_CHN_MATRIX_PCMCHANNEL);
677282650Shselasky		} else {
678282650Shselasky			c->matrix = *softmatrix;
679282650Shselasky			c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
680193640Sariff		}
681193640Sariff	}
682193640Sariff	softfmt = SND_FORMAT(softfmt, softmatrix->channels, softmatrix->ext);
683193640Sariff	if (softfmt != c->format)
684193640Sariff		device_printf(c->dev,
685193640Sariff		    "%s(): WARNING: %s Soft format 0x%08x -> 0x%08x\n",
686193640Sariff		    __func__, CHN_DIRSTR(c), c->format, softfmt);
687193640Sariff
688193640Sariff	/*
689193640Sariff	 * PLAY and REC are opposite.
690193640Sariff	 */
691193640Sariff	if (c->direction == PCMDIR_PLAY) {
692193640Sariff		cdesc.origin.afmt    = softfmt;
693193640Sariff		cdesc.origin.matrix  = softmatrix;
694193640Sariff		cdesc.origin.rate    = c->speed;
695193640Sariff		cdesc.target.afmt    = hwfmt;
696193640Sariff		cdesc.target.matrix  = hwmatrix;
697193640Sariff		cdesc.target.rate    = sndbuf_getspd(c->bufhard);
698193640Sariff	} else {
699193640Sariff		cdesc.origin.afmt    = hwfmt;
700193640Sariff		cdesc.origin.matrix  = hwmatrix;
701193640Sariff		cdesc.origin.rate    = sndbuf_getspd(c->bufhard);
702193640Sariff		cdesc.target.afmt    = softfmt;
703193640Sariff		cdesc.target.matrix  = softmatrix;
704193640Sariff		cdesc.target.rate    = c->speed;
705193640Sariff	}
706193640Sariff
707193640Sariff	d = c->parentsnddev;
708193640Sariff
709193640Sariff	/*
710193640Sariff	 * If channel is in bitperfect or passthrough mode, make it appear
711193640Sariff	 * that 'origin' and 'target' identical, skipping mostly chain
712193640Sariff	 * procedures.
713193640Sariff	 */
714193640Sariff	if (CHN_BITPERFECT(c) || (c->format & AFMT_PASSTHROUGH)) {
715193640Sariff		if (c->direction == PCMDIR_PLAY)
716193640Sariff			cdesc.origin = cdesc.target;
717193640Sariff		else
718193640Sariff			cdesc.target = cdesc.origin;
719193640Sariff		c->format = cdesc.target.afmt;
720193640Sariff		c->speed  = cdesc.target.rate;
721193640Sariff	} else {
722193640Sariff		/* hwfmt is not convertible, so 'dummy' it. */
723193640Sariff		if (hwfmt & AFMT_PASSTHROUGH)
724193640Sariff			cdesc.dummy = 1;
725193640Sariff
726193640Sariff		if ((softfmt & AFMT_CONVERTIBLE) &&
727193640Sariff		    (((d->flags & SD_F_VPC) && !(c->flags & CHN_F_HAS_VCHAN)) ||
728193640Sariff		    (!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) &&
729193640Sariff		    !(c->flags & CHN_F_VIRTUAL))))
730193640Sariff			cdesc.use_volume = 1;
731193640Sariff
732193640Sariff		if (feeder_matrix_compare(cdesc.origin.matrix,
733193640Sariff		    cdesc.target.matrix) != 0)
734193640Sariff			cdesc.use_matrix = 1;
735193640Sariff
736193640Sariff		/* Soft EQ only applicable for PLAY. */
737193640Sariff		if (cdesc.dummy == 0 &&
738193640Sariff		    c->direction == PCMDIR_PLAY && (d->flags & SD_F_EQ) &&
739193640Sariff		    (((d->flags & SD_F_EQ_PC) &&
740193640Sariff		    !(c->flags & CHN_F_HAS_VCHAN)) ||
741193640Sariff		    (!(d->flags & SD_F_EQ_PC) && !(c->flags & CHN_F_VIRTUAL))))
742193640Sariff			cdesc.use_eq = 1;
743193640Sariff
744193640Sariff		if (FEEDFORMAT_NE_REQUIRED(&cdesc)) {
745193640Sariff			cdesc.afmt_ne =
746193640Sariff			    (cdesc.dummy != 0) ?
747193640Sariff			    snd_fmtbest(AFMT_ENCODING(softfmt),
748193640Sariff			    feeder_chain_formats[cdesc.mode]) :
749193640Sariff			    snd_fmtbest(AFMT_ENCODING(cdesc.target.afmt),
750193640Sariff			    feeder_chain_formats[cdesc.mode]);
751193640Sariff			if (cdesc.afmt_ne == 0) {
752193640Sariff				device_printf(c->dev,
753193640Sariff				    "%s(): snd_fmtbest failed!\n", __func__);
754193640Sariff				cdesc.afmt_ne =
755193640Sariff				    (((cdesc.dummy != 0) ? softfmt :
756193640Sariff				    cdesc.target.afmt) &
757193640Sariff				    (AFMT_24BIT | AFMT_32BIT)) ?
758193640Sariff				    AFMT_S32_NE : AFMT_S16_NE;
759193640Sariff			}
760193640Sariff		}
761193640Sariff	}
762193640Sariff
763193640Sariff	cdesc.current = cdesc.origin;
764193640Sariff
765193640Sariff	/* Build everything. */
766193640Sariff
767193640Sariff	c->feederflags = 0;
768193640Sariff
769193640Sariff#define FEEDER_BUILD(t)	do {						\
770193640Sariff	ret = feeder_build_##t(c, &cdesc);				\
771193640Sariff	if (ret != 0)							\
772193640Sariff		return (ret);						\
773193640Sariff	} while (0)
774193640Sariff
775193640Sariff	if (!(c->flags & CHN_F_HAS_VCHAN) || c->direction == PCMDIR_REC)
776193640Sariff		FEEDER_BUILD(root);
777193640Sariff	else if (c->direction == PCMDIR_PLAY && (c->flags & CHN_F_HAS_VCHAN))
778193640Sariff		FEEDER_BUILD(mixer);
779193640Sariff	else
780193640Sariff		return (ENOTSUP);
781193640Sariff
782193640Sariff	/*
783193640Sariff	 * The basic idea is: The smaller the bandwidth, the cheaper the
784193640Sariff	 * conversion process, with following constraints:-
785193640Sariff	 *
786193640Sariff	 * 1) Almost all feeders work best in 16/32 native endian.
787193640Sariff	 * 2) Try to avoid 8bit feeders due to poor dynamic range.
788193640Sariff	 * 3) Avoid volume, format, matrix and rate in BITPERFECT or
789193640Sariff	 *    PASSTHROUGH mode.
790193640Sariff	 * 4) Try putting volume before EQ or rate. Should help to
791193640Sariff	 *    avoid/reduce possible clipping.
792193640Sariff	 * 5) EQ require specific, valid rate, unless it allow sloppy
793193640Sariff	 *    conversion.
794193640Sariff	 */
795193640Sariff	if (FEEDMATRIX_UP(&cdesc)) {
796193640Sariff		if (FEEDEQ_REQUIRED(&cdesc) &&
797193640Sariff		    (!FEEDEQ_VALIDRATE(&cdesc, target) ||
798193640Sariff		    (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc))))
799193640Sariff			FEEDER_BUILD(eq);
800193640Sariff		if (FEEDRATE_REQUIRED(&cdesc))
801193640Sariff			FEEDER_BUILD(rate);
802193640Sariff		FEEDER_BUILD(matrix);
803193640Sariff		if (FEEDVOLUME_REQUIRED(&cdesc))
804193640Sariff			FEEDER_BUILD(volume);
805193640Sariff		if (FEEDEQ_REQUIRED(&cdesc))
806193640Sariff			FEEDER_BUILD(eq);
807193640Sariff	} else if (FEEDMATRIX_DOWN(&cdesc)) {
808193640Sariff		FEEDER_BUILD(matrix);
809193640Sariff		if (FEEDVOLUME_REQUIRED(&cdesc))
810193640Sariff			FEEDER_BUILD(volume);
811193640Sariff		if (FEEDEQ_REQUIRED(&cdesc) &&
812193640Sariff		    (!FEEDEQ_VALIDRATE(&cdesc, target) ||
813193640Sariff		    FEEDEQ_ECONOMY(&cdesc)))
814193640Sariff			FEEDER_BUILD(eq);
815193640Sariff		if (FEEDRATE_REQUIRED(&cdesc))
816193640Sariff			FEEDER_BUILD(rate);
817193640Sariff		if (FEEDEQ_REQUIRED(&cdesc))
818193640Sariff			FEEDER_BUILD(eq);
819193640Sariff	} else {
820193640Sariff		if (FEEDRATE_DOWN(&cdesc)) {
821193640Sariff			if (FEEDEQ_REQUIRED(&cdesc) &&
822193640Sariff			    !FEEDEQ_VALIDRATE(&cdesc, target)) {
823193640Sariff				if (FEEDVOLUME_REQUIRED(&cdesc))
824193640Sariff					FEEDER_BUILD(volume);
825193640Sariff				FEEDER_BUILD(eq);
826193640Sariff			}
827193640Sariff			FEEDER_BUILD(rate);
828193640Sariff		}
829193640Sariff		if (FEEDMATRIX_REQUIRED(&cdesc))
830193640Sariff			FEEDER_BUILD(matrix);
831193640Sariff		if (FEEDVOLUME_REQUIRED(&cdesc))
832193640Sariff			FEEDER_BUILD(volume);
833193640Sariff		if (FEEDRATE_UP(&cdesc)) {
834193640Sariff			if (FEEDEQ_REQUIRED(&cdesc) &&
835193640Sariff			    !FEEDEQ_VALIDRATE(&cdesc, target))
836193640Sariff				FEEDER_BUILD(eq);
837193640Sariff			FEEDER_BUILD(rate);
838193640Sariff		}
839193640Sariff		if (FEEDEQ_REQUIRED(&cdesc))
840193640Sariff			FEEDER_BUILD(eq);
841193640Sariff	}
842193640Sariff
843193640Sariff	if (FEEDFORMAT_REQUIRED(&cdesc))
844193640Sariff		FEEDER_BUILD(format);
845193640Sariff
846193640Sariff	if (c->direction == PCMDIR_REC && (c->flags & CHN_F_HAS_VCHAN))
847193640Sariff		FEEDER_BUILD(mixer);
848193640Sariff
849193640Sariff	sndbuf_setfmt(c->bufsoft, c->format);
850193640Sariff	sndbuf_setspd(c->bufsoft, c->speed);
851193640Sariff
852193640Sariff	sndbuf_setfmt(c->bufhard, hwfmt);
853193640Sariff
854193640Sariff	chn_syncstate(c);
855193640Sariff
856193640Sariff	return (0);
857193640Sariff}
858