feeder_chain.c revision 193640
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: head/sys/dev/sound/pcm/feeder_chain.c 193640 2009-06-07 19:12:08Z ariff $");
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)
128193640SariffTUNABLE_INT("hw.snd.feeder_chain_mode", &feeder_chain_mode);
129193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, feeder_chain_mode, CTLFLAG_RW,
130193640Sariff    &feeder_chain_mode, 0,
131193640Sariff    "feeder chain mode "
132193640Sariff    "(0=lean, 1=16bit, 2=32bit, 3=multiformat, 4=fullmultiformat)");
133193640Sariff#endif
134193640Sariff
135193640Sariff/*
136193640Sariff * feeder_build_format(): Chain any format converter.
137193640Sariff */
138193640Sariffstatic int
139193640Sarifffeeder_build_format(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
140193640Sariff{
141193640Sariff	struct feeder_class *fc;
142193640Sariff	struct pcm_feederdesc *desc;
143193640Sariff	int ret;
144193640Sariff
145193640Sariff	desc = &(cdesc->desc);
146193640Sariff	desc->type = FEEDER_FORMAT;
147193640Sariff	desc->in = 0;
148193640Sariff	desc->out = 0;
149193640Sariff	desc->flags = 0;
150193640Sariff
151193640Sariff	fc = feeder_getclass(desc);
152193640Sariff	if (fc == NULL) {
153193640Sariff		device_printf(c->dev,
154193640Sariff		    "%s(): can't find feeder_format\n", __func__);
155193640Sariff		return (ENOTSUP);
156193640Sariff	}
157193640Sariff
158193640Sariff	desc->in = cdesc->current.afmt;
159193640Sariff	desc->out = cdesc->target.afmt;
160193640Sariff
161193640Sariff	ret = chn_addfeeder(c, fc, desc);
162193640Sariff	if (ret != 0) {
163193640Sariff		device_printf(c->dev,
164193640Sariff		    "%s(): can't add feeder_format\n", __func__);
165193640Sariff		return (ret);
166193640Sariff	}
167193640Sariff
168193640Sariff	c->feederflags |= 1 << FEEDER_FORMAT;
169193640Sariff
170193640Sariff	cdesc->current.afmt = cdesc->target.afmt;
171193640Sariff
172193640Sariff	return (0);
173193640Sariff}
174193640Sariff
175193640Sariff/*
176193640Sariff * feeder_build_formatne(): Chain format converter that suite best for native
177193640Sariff *                          endian format.
178193640Sariff */
179193640Sariffstatic int
180193640Sarifffeeder_build_formatne(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
181193640Sariff{
182193640Sariff	struct feeder_chain_state otarget;
183193640Sariff	int ret;
184193640Sariff
185193640Sariff	if (cdesc->afmt_ne == 0 ||
186193640Sariff	    AFMT_ENCODING(cdesc->current.afmt) == cdesc->afmt_ne)
187193640Sariff		return (0);
188193640Sariff
189193640Sariff	otarget = cdesc->target;
190193640Sariff	cdesc->target = cdesc->current;
191193640Sariff	cdesc->target.afmt = SND_FORMAT(cdesc->afmt_ne,
192193640Sariff	    cdesc->current.matrix->channels, cdesc->current.matrix->ext);
193193640Sariff
194193640Sariff	ret = feeder_build_format(c, cdesc);
195193640Sariff	if (ret != 0)
196193640Sariff		return (ret);
197193640Sariff
198193640Sariff	cdesc->target = otarget;
199193640Sariff
200193640Sariff	return (0);
201193640Sariff}
202193640Sariff
203193640Sariff/*
204193640Sariff * feeder_build_rate(): Chain sample rate converter.
205193640Sariff */
206193640Sariffstatic int
207193640Sarifffeeder_build_rate(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
208193640Sariff{
209193640Sariff	struct feeder_class *fc;
210193640Sariff	struct pcm_feeder *f;
211193640Sariff	struct pcm_feederdesc *desc;
212193640Sariff	int ret;
213193640Sariff
214193640Sariff	ret = feeder_build_formatne(c, cdesc);
215193640Sariff	if (ret != 0)
216193640Sariff		return (ret);
217193640Sariff
218193640Sariff	desc = &(cdesc->desc);
219193640Sariff	desc->type = FEEDER_RATE;
220193640Sariff	desc->in = 0;
221193640Sariff	desc->out = 0;
222193640Sariff	desc->flags = 0;
223193640Sariff
224193640Sariff	fc = feeder_getclass(desc);
225193640Sariff	if (fc == NULL) {
226193640Sariff		device_printf(c->dev,
227193640Sariff		    "%s(): can't find feeder_rate\n", __func__);
228193640Sariff		return (ENOTSUP);
229193640Sariff	}
230193640Sariff
231193640Sariff	desc->in = cdesc->current.afmt;
232193640Sariff	desc->out = desc->in;
233193640Sariff
234193640Sariff	ret = chn_addfeeder(c, fc, desc);
235193640Sariff	if (ret != 0) {
236193640Sariff		device_printf(c->dev,
237193640Sariff		    "%s(): can't add feeder_rate\n", __func__);
238193640Sariff		return (ret);
239193640Sariff	}
240193640Sariff
241193640Sariff	f = c->feeder;
242193640Sariff
243193640Sariff	/*
244193640Sariff	 * If in 'dummy' mode (possibly due to passthrough mode), set the
245193640Sariff	 * conversion quality to the lowest possible (should be fastest) since
246193640Sariff	 * listener won't be hearing anything. Theoretically we can just
247193640Sariff	 * disable it, but that will cause weird runtime behaviour:
248193640Sariff	 * application appear to play something that is either too fast or too
249193640Sariff	 * slow.
250193640Sariff	 */
251193640Sariff	if (cdesc->dummy != 0) {
252193640Sariff		ret = FEEDER_SET(f, FEEDRATE_QUALITY, 0);
253193640Sariff		if (ret != 0) {
254193640Sariff			device_printf(c->dev,
255193640Sariff			    "%s(): can't set resampling quality\n", __func__);
256193640Sariff			return (ret);
257193640Sariff		}
258193640Sariff	}
259193640Sariff
260193640Sariff	ret = FEEDER_SET(f, FEEDRATE_SRC, cdesc->current.rate);
261193640Sariff	if (ret != 0) {
262193640Sariff		device_printf(c->dev,
263193640Sariff		    "%s(): can't set source rate\n", __func__);
264193640Sariff		return (ret);
265193640Sariff	}
266193640Sariff
267193640Sariff	ret = FEEDER_SET(f, FEEDRATE_DST, cdesc->target.rate);
268193640Sariff	if (ret != 0) {
269193640Sariff		device_printf(c->dev,
270193640Sariff		    "%s(): can't set destination rate\n", __func__);
271193640Sariff		return (ret);
272193640Sariff	}
273193640Sariff
274193640Sariff	c->feederflags |= 1 << FEEDER_RATE;
275193640Sariff
276193640Sariff	cdesc->current.rate = cdesc->target.rate;
277193640Sariff
278193640Sariff	return (0);
279193640Sariff}
280193640Sariff
281193640Sariff/*
282193640Sariff * feeder_build_matrix(): Chain channel matrixing converter.
283193640Sariff */
284193640Sariffstatic int
285193640Sarifffeeder_build_matrix(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
286193640Sariff{
287193640Sariff	struct feeder_class *fc;
288193640Sariff	struct pcm_feeder *f;
289193640Sariff	struct pcm_feederdesc *desc;
290193640Sariff	int ret;
291193640Sariff
292193640Sariff	ret = feeder_build_formatne(c, cdesc);
293193640Sariff	if (ret != 0)
294193640Sariff		return (ret);
295193640Sariff
296193640Sariff	desc = &(cdesc->desc);
297193640Sariff	desc->type = FEEDER_MATRIX;
298193640Sariff	desc->in = 0;
299193640Sariff	desc->out = 0;
300193640Sariff	desc->flags = 0;
301193640Sariff
302193640Sariff	fc = feeder_getclass(desc);
303193640Sariff	if (fc == NULL) {
304193640Sariff		device_printf(c->dev,
305193640Sariff		    "%s(): can't find feeder_matrix\n", __func__);
306193640Sariff		return (ENOTSUP);
307193640Sariff	}
308193640Sariff
309193640Sariff	desc->in = cdesc->current.afmt;
310193640Sariff	desc->out = SND_FORMAT(cdesc->current.afmt,
311193640Sariff	    cdesc->target.matrix->channels, cdesc->target.matrix->ext);
312193640Sariff
313193640Sariff	ret = chn_addfeeder(c, fc, desc);
314193640Sariff	if (ret != 0) {
315193640Sariff		device_printf(c->dev,
316193640Sariff		    "%s(): can't add feeder_matrix\n", __func__);
317193640Sariff		return (ret);
318193640Sariff	}
319193640Sariff
320193640Sariff	f = c->feeder;
321193640Sariff	ret = feeder_matrix_setup(f, cdesc->current.matrix,
322193640Sariff	    cdesc->target.matrix);
323193640Sariff	if (ret != 0) {
324193640Sariff		device_printf(c->dev,
325193640Sariff		    "%s(): feeder_matrix_setup() failed\n", __func__);
326193640Sariff		return (ret);
327193640Sariff	}
328193640Sariff
329193640Sariff	c->feederflags |= 1 << FEEDER_MATRIX;
330193640Sariff
331193640Sariff	cdesc->current.afmt = desc->out;
332193640Sariff	cdesc->current.matrix = cdesc->target.matrix;
333193640Sariff	cdesc->use_matrix = 0;
334193640Sariff
335193640Sariff	return (0);
336193640Sariff}
337193640Sariff
338193640Sariff/*
339193640Sariff * feeder_build_volume(): Chain soft volume.
340193640Sariff */
341193640Sariffstatic int
342193640Sarifffeeder_build_volume(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
343193640Sariff{
344193640Sariff	struct feeder_class *fc;
345193640Sariff	struct pcm_feeder *f;
346193640Sariff	struct pcm_feederdesc *desc;
347193640Sariff	int ret;
348193640Sariff
349193640Sariff	ret = feeder_build_formatne(c, cdesc);
350193640Sariff	if (ret != 0)
351193640Sariff		return (ret);
352193640Sariff
353193640Sariff	desc = &(cdesc->desc);
354193640Sariff	desc->type = FEEDER_VOLUME;
355193640Sariff	desc->in = 0;
356193640Sariff	desc->out = 0;
357193640Sariff	desc->flags = 0;
358193640Sariff
359193640Sariff	fc = feeder_getclass(desc);
360193640Sariff	if (fc == NULL) {
361193640Sariff		device_printf(c->dev,
362193640Sariff		    "%s(): can't find feeder_volume\n", __func__);
363193640Sariff		return (ENOTSUP);
364193640Sariff	}
365193640Sariff
366193640Sariff	desc->in = cdesc->current.afmt;
367193640Sariff	desc->out = desc->in;
368193640Sariff
369193640Sariff	ret = chn_addfeeder(c, fc, desc);
370193640Sariff	if (ret != 0) {
371193640Sariff		device_printf(c->dev,
372193640Sariff		    "%s(): can't add feeder_volume\n", __func__);
373193640Sariff		return (ret);
374193640Sariff	}
375193640Sariff
376193640Sariff	f = c->feeder;
377193640Sariff
378193640Sariff	/*
379193640Sariff	 * If in 'dummy' mode (possibly due to passthrough mode), set BYPASS
380193640Sariff	 * mode since listener won't be hearing anything. Theoretically we can
381193640Sariff	 * just disable it, but that will confuse volume per channel mixer.
382193640Sariff	 */
383193640Sariff	if (cdesc->dummy != 0) {
384193640Sariff		ret = FEEDER_SET(f, FEEDVOLUME_STATE, FEEDVOLUME_BYPASS);
385193640Sariff		if (ret != 0) {
386193640Sariff			device_printf(c->dev,
387193640Sariff			    "%s(): can't set volume bypass\n", __func__);
388193640Sariff			return (ret);
389193640Sariff		}
390193640Sariff	}
391193640Sariff
392193640Sariff	ret = feeder_volume_apply_matrix(f, cdesc->current.matrix);
393193640Sariff	if (ret != 0) {
394193640Sariff		device_printf(c->dev,
395193640Sariff		    "%s(): feeder_volume_apply_matrix() failed\n", __func__);
396193640Sariff		return (ret);
397193640Sariff	}
398193640Sariff
399193640Sariff	c->feederflags |= 1 << FEEDER_VOLUME;
400193640Sariff
401193640Sariff	cdesc->use_volume = 0;
402193640Sariff
403193640Sariff	return (0);
404193640Sariff}
405193640Sariff
406193640Sariff/*
407193640Sariff * feeder_build_eq(): Chain parametric software equalizer.
408193640Sariff */
409193640Sariffstatic int
410193640Sarifffeeder_build_eq(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
411193640Sariff{
412193640Sariff	struct feeder_class *fc;
413193640Sariff	struct pcm_feeder *f;
414193640Sariff	struct pcm_feederdesc *desc;
415193640Sariff	int ret;
416193640Sariff
417193640Sariff	ret = feeder_build_formatne(c, cdesc);
418193640Sariff	if (ret != 0)
419193640Sariff		return (ret);
420193640Sariff
421193640Sariff	desc = &(cdesc->desc);
422193640Sariff	desc->type = FEEDER_EQ;
423193640Sariff	desc->in = 0;
424193640Sariff	desc->out = 0;
425193640Sariff	desc->flags = 0;
426193640Sariff
427193640Sariff	fc = feeder_getclass(desc);
428193640Sariff	if (fc == NULL) {
429193640Sariff		device_printf(c->dev,
430193640Sariff		    "%s(): can't find feeder_eq\n", __func__);
431193640Sariff		return (ENOTSUP);
432193640Sariff	}
433193640Sariff
434193640Sariff	desc->in = cdesc->current.afmt;
435193640Sariff	desc->out = desc->in;
436193640Sariff
437193640Sariff	ret = chn_addfeeder(c, fc, desc);
438193640Sariff	if (ret != 0) {
439193640Sariff		device_printf(c->dev,
440193640Sariff		    "%s(): can't add feeder_eq\n", __func__);
441193640Sariff		return (ret);
442193640Sariff	}
443193640Sariff
444193640Sariff	f = c->feeder;
445193640Sariff
446193640Sariff	ret = FEEDER_SET(f, FEEDEQ_RATE, cdesc->current.rate);
447193640Sariff	if (ret != 0) {
448193640Sariff		device_printf(c->dev,
449193640Sariff		    "%s(): can't set rate on feeder_eq\n", __func__);
450193640Sariff		return (ret);
451193640Sariff	}
452193640Sariff
453193640Sariff	c->feederflags |= 1 << FEEDER_EQ;
454193640Sariff
455193640Sariff	cdesc->use_eq = 0;
456193640Sariff
457193640Sariff	return (0);
458193640Sariff}
459193640Sariff
460193640Sariff/*
461193640Sariff * feeder_build_root(): Chain root feeder, the top, father of all.
462193640Sariff */
463193640Sariffstatic int
464193640Sarifffeeder_build_root(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
465193640Sariff{
466193640Sariff	struct feeder_class *fc;
467193640Sariff	int ret;
468193640Sariff
469193640Sariff	fc = feeder_getclass(NULL);
470193640Sariff	if (fc == NULL) {
471193640Sariff		device_printf(c->dev,
472193640Sariff		    "%s(): can't find feeder_root\n", __func__);
473193640Sariff		return (ENOTSUP);
474193640Sariff	}
475193640Sariff
476193640Sariff	ret = chn_addfeeder(c, fc, NULL);
477193640Sariff	if (ret != 0) {
478193640Sariff		device_printf(c->dev,
479193640Sariff		    "%s(): can't add feeder_root\n", __func__);
480193640Sariff		return (ret);
481193640Sariff	}
482193640Sariff
483193640Sariff	c->feederflags |= 1 << FEEDER_ROOT;
484193640Sariff
485193640Sariff	c->feeder->desc->in = cdesc->current.afmt;
486193640Sariff	c->feeder->desc->out = cdesc->current.afmt;
487193640Sariff
488193640Sariff	return (0);
489193640Sariff}
490193640Sariff
491193640Sariff/*
492193640Sariff * feeder_build_mixer(): Chain software mixer for virtual channels.
493193640Sariff */
494193640Sariffstatic int
495193640Sarifffeeder_build_mixer(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
496193640Sariff{
497193640Sariff	struct feeder_class *fc;
498193640Sariff	struct pcm_feederdesc *desc;
499193640Sariff	int ret;
500193640Sariff
501193640Sariff	desc = &(cdesc->desc);
502193640Sariff	desc->type = FEEDER_MIXER;
503193640Sariff	desc->in = 0;
504193640Sariff	desc->out = 0;
505193640Sariff	desc->flags = 0;
506193640Sariff
507193640Sariff	fc = feeder_getclass(desc);
508193640Sariff	if (fc == NULL) {
509193640Sariff		device_printf(c->dev,
510193640Sariff		    "%s(): can't find feeder_mixer\n", __func__);
511193640Sariff		return (ENOTSUP);
512193640Sariff	}
513193640Sariff
514193640Sariff	desc->in = cdesc->current.afmt;
515193640Sariff	desc->out = desc->in;
516193640Sariff
517193640Sariff	ret = chn_addfeeder(c, fc, desc);
518193640Sariff	if (ret != 0) {
519193640Sariff		device_printf(c->dev,
520193640Sariff		    "%s(): can't add feeder_mixer\n", __func__);
521193640Sariff		return (ret);
522193640Sariff	}
523193640Sariff
524193640Sariff	c->feederflags |= 1 << FEEDER_MIXER;
525193640Sariff
526193640Sariff	return (0);
527193640Sariff}
528193640Sariff
529193640Sariff/* Macrosses to ease our job doing stuffs later. */
530193640Sariff#define FEEDER_BW(c, t)		((c)->t.matrix->channels * (c)->t.rate)
531193640Sariff
532193640Sariff#define FEEDRATE_UP(c)		((c)->target.rate > (c)->current.rate)
533193640Sariff#define FEEDRATE_DOWN(c)	((c)->target.rate < (c)->current.rate)
534193640Sariff#define FEEDRATE_REQUIRED(c)	(FEEDRATE_UP(c) || FEEDRATE_DOWN(c))
535193640Sariff
536193640Sariff#define FEEDMATRIX_UP(c)	((c)->target.matrix->channels >		\
537193640Sariff				 (c)->current.matrix->channels)
538193640Sariff#define FEEDMATRIX_DOWN(c)	((c)->target.matrix->channels <		\
539193640Sariff				 (c)->current.matrix->channels)
540193640Sariff#define FEEDMATRIX_REQUIRED(c)	(FEEDMATRIX_UP(c) ||			\
541193640Sariff				 FEEDMATRIX_DOWN(c) || (c)->use_matrix != 0)
542193640Sariff
543193640Sariff#define FEEDFORMAT_REQUIRED(c)	(AFMT_ENCODING((c)->current.afmt) !=	\
544193640Sariff				 AFMT_ENCODING((c)->target.afmt))
545193640Sariff
546193640Sariff#define FEEDVOLUME_REQUIRED(c)	((c)->use_volume != 0)
547193640Sariff
548193640Sariff#define FEEDEQ_VALIDRATE(c, t)	(feeder_eq_validrate((c)->t.rate) != 0)
549193640Sariff#define FEEDEQ_ECONOMY(c)	(FEEDER_BW(c, current) < FEEDER_BW(c, target))
550193640Sariff#define FEEDEQ_REQUIRED(c)	((c)->use_eq != 0 &&			\
551193640Sariff				 FEEDEQ_VALIDRATE(c, current))
552193640Sariff
553193640Sariff#define FEEDFORMAT_NE_REQUIRED(c)					\
554193640Sariff	((c)->afmt_ne != AFMT_S32_NE &&					\
555193640Sariff	(((c)->mode == FEEDER_CHAIN_16 &&				\
556193640Sariff	AFMT_ENCODING((c)->current.afmt) != AFMT_S16_NE) ||		\
557193640Sariff	((c)->mode == FEEDER_CHAIN_32 &&				\
558193640Sariff	AFMT_ENCODING((c)->current.afmt) != AFMT_S32_NE) ||		\
559193640Sariff	(c)->mode == FEEDER_CHAIN_FULLMULTI ||				\
560193640Sariff	((c)->mode == FEEDER_CHAIN_MULTI &&				\
561193640Sariff	((c)->current.afmt & AFMT_8BIT)) ||				\
562193640Sariff	((c)->mode == FEEDER_CHAIN_LEAN &&				\
563193640Sariff	!((c)->current.afmt & (AFMT_S16_NE | AFMT_S32_NE)))))
564193640Sariff
565193640Sariffint
566193640Sarifffeeder_chain(struct pcm_channel *c)
567193640Sariff{
568193640Sariff	struct snddev_info *d;
569193640Sariff	struct pcmchan_caps *caps;
570193640Sariff	struct feeder_chain_desc cdesc;
571193640Sariff	struct pcmchan_matrix *hwmatrix, *softmatrix;
572193640Sariff	uint32_t hwfmt, softfmt;
573193640Sariff	int ret;
574193640Sariff
575193640Sariff	CHN_LOCKASSERT(c);
576193640Sariff
577193640Sariff	/* Remove everything first. */
578193640Sariff	while (chn_removefeeder(c) == 0)
579193640Sariff		;
580193640Sariff
581193640Sariff	KASSERT(c->feeder == NULL, ("feeder chain not empty"));
582193640Sariff
583193640Sariff	/* clear and populate chain descriptor. */
584193640Sariff	bzero(&cdesc, sizeof(cdesc));
585193640Sariff
586193640Sariff	switch (feeder_chain_mode) {
587193640Sariff	case FEEDER_CHAIN_LEAN:
588193640Sariff	case FEEDER_CHAIN_16:
589193640Sariff	case FEEDER_CHAIN_32:
590193640Sariff#if defined(SND_FEEDER_MULTIFORMAT) || defined(SND_FEEDER_FULL_MULTIFORMAT)
591193640Sariff	case FEEDER_CHAIN_MULTI:
592193640Sariff#endif
593193640Sariff#if defined(SND_FEEDER_FULL_MULTIFORMAT)
594193640Sariff	case FEEDER_CHAIN_FULLMULTI:
595193640Sariff#endif
596193640Sariff		break;
597193640Sariff	default:
598193640Sariff		feeder_chain_mode = FEEDER_CHAIN_DEFAULT;
599193640Sariff		break;
600193640Sariff	}
601193640Sariff
602193640Sariff	cdesc.mode = feeder_chain_mode;
603193640Sariff	cdesc.expensive = 1;	/* XXX faster.. */
604193640Sariff
605193640Sariff#define VCHAN_PASSTHROUGH(c)	(((c)->flags & (CHN_F_VIRTUAL |		\
606193640Sariff				 CHN_F_PASSTHROUGH)) ==			\
607193640Sariff				 (CHN_F_VIRTUAL | CHN_F_PASSTHROUGH))
608193640Sariff
609193640Sariff	/* Get the best possible hardware format. */
610193640Sariff	if (VCHAN_PASSTHROUGH(c))
611193640Sariff		hwfmt = c->parentchannel->format;
612193640Sariff	else {
613193640Sariff		caps = chn_getcaps(c);
614193640Sariff		if (caps == NULL || caps->fmtlist == NULL) {
615193640Sariff			device_printf(c->dev,
616193640Sariff			    "%s(): failed to get channel caps\n", __func__);
617193640Sariff			return (ENODEV);
618193640Sariff		}
619193640Sariff
620193640Sariff		if ((c->format & AFMT_PASSTHROUGH) &&
621193640Sariff		    !snd_fmtvalid(c->format, caps->fmtlist))
622193640Sariff			return (ENODEV);
623193640Sariff
624193640Sariff		hwfmt = snd_fmtbest(c->format, caps->fmtlist);
625193640Sariff		if (hwfmt == 0 || !snd_fmtvalid(hwfmt, caps->fmtlist)) {
626193640Sariff			device_printf(c->dev,
627193640Sariff			    "%s(): invalid hardware format 0x%08x\n",
628193640Sariff			    __func__, hwfmt);
629193640Sariff			{
630193640Sariff				int i;
631193640Sariff				for (i = 0; caps->fmtlist[i] != 0; i++)
632193640Sariff					printf("0x%08x\n", caps->fmtlist[i]);
633193640Sariff				printf("Req: 0x%08x\n", c->format);
634193640Sariff			}
635193640Sariff			return (ENODEV);
636193640Sariff		}
637193640Sariff	}
638193640Sariff
639193640Sariff	/*
640193640Sariff	 * The 'hardware' possibly have different intepretation of channel
641193640Sariff	 * matrixing, so get it first .....
642193640Sariff	 */
643193640Sariff	hwmatrix = CHANNEL_GETMATRIX(c->methods, c->devinfo, hwfmt);
644193640Sariff	if (hwmatrix == NULL) {
645193640Sariff		device_printf(c->dev,
646193640Sariff		    "%s(): failed to acquire hw matrix [0x%08x]\n",
647193640Sariff		    __func__, hwfmt);
648193640Sariff		return (ENODEV);
649193640Sariff	}
650193640Sariff	/* ..... and rebuild hwfmt. */
651193640Sariff	hwfmt = SND_FORMAT(hwfmt, hwmatrix->channels, hwmatrix->ext);
652193640Sariff
653193640Sariff	/* Reset and rebuild default channel format/matrix map. */
654193640Sariff	softfmt = c->format;
655193640Sariff	softmatrix = &c->matrix;
656193640Sariff	if (softmatrix->channels != AFMT_CHANNEL(softfmt) ||
657193640Sariff	    softmatrix->ext != AFMT_EXTCHANNEL(softfmt)) {
658193640Sariff		softmatrix = feeder_matrix_format_map(softfmt);
659193640Sariff		if (softmatrix == NULL) {
660193640Sariff			device_printf(c->dev,
661193640Sariff			    "%s(): failed to acquire soft matrix [0x%08x]\n",
662193640Sariff			    __func__, softfmt);
663193640Sariff			return (ENODEV);
664193640Sariff		}
665193640Sariff		c->matrix = *softmatrix;
666193640Sariff		c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
667193640Sariff	}
668193640Sariff	softfmt = SND_FORMAT(softfmt, softmatrix->channels, softmatrix->ext);
669193640Sariff	if (softfmt != c->format)
670193640Sariff		device_printf(c->dev,
671193640Sariff		    "%s(): WARNING: %s Soft format 0x%08x -> 0x%08x\n",
672193640Sariff		    __func__, CHN_DIRSTR(c), c->format, softfmt);
673193640Sariff
674193640Sariff	/*
675193640Sariff	 * PLAY and REC are opposite.
676193640Sariff	 */
677193640Sariff	if (c->direction == PCMDIR_PLAY) {
678193640Sariff		cdesc.origin.afmt    = softfmt;
679193640Sariff		cdesc.origin.matrix  = softmatrix;
680193640Sariff		cdesc.origin.rate    = c->speed;
681193640Sariff		cdesc.target.afmt    = hwfmt;
682193640Sariff		cdesc.target.matrix  = hwmatrix;
683193640Sariff		cdesc.target.rate    = sndbuf_getspd(c->bufhard);
684193640Sariff	} else {
685193640Sariff		cdesc.origin.afmt    = hwfmt;
686193640Sariff		cdesc.origin.matrix  = hwmatrix;
687193640Sariff		cdesc.origin.rate    = sndbuf_getspd(c->bufhard);
688193640Sariff		cdesc.target.afmt    = softfmt;
689193640Sariff		cdesc.target.matrix  = softmatrix;
690193640Sariff		cdesc.target.rate    = c->speed;
691193640Sariff	}
692193640Sariff
693193640Sariff	d = c->parentsnddev;
694193640Sariff
695193640Sariff	/*
696193640Sariff	 * If channel is in bitperfect or passthrough mode, make it appear
697193640Sariff	 * that 'origin' and 'target' identical, skipping mostly chain
698193640Sariff	 * procedures.
699193640Sariff	 */
700193640Sariff	if (CHN_BITPERFECT(c) || (c->format & AFMT_PASSTHROUGH)) {
701193640Sariff		if (c->direction == PCMDIR_PLAY)
702193640Sariff			cdesc.origin = cdesc.target;
703193640Sariff		else
704193640Sariff			cdesc.target = cdesc.origin;
705193640Sariff		c->format = cdesc.target.afmt;
706193640Sariff		c->speed  = cdesc.target.rate;
707193640Sariff	} else {
708193640Sariff		/* hwfmt is not convertible, so 'dummy' it. */
709193640Sariff		if (hwfmt & AFMT_PASSTHROUGH)
710193640Sariff			cdesc.dummy = 1;
711193640Sariff
712193640Sariff		if ((softfmt & AFMT_CONVERTIBLE) &&
713193640Sariff		    (((d->flags & SD_F_VPC) && !(c->flags & CHN_F_HAS_VCHAN)) ||
714193640Sariff		    (!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) &&
715193640Sariff		    !(c->flags & CHN_F_VIRTUAL))))
716193640Sariff			cdesc.use_volume = 1;
717193640Sariff
718193640Sariff		if (feeder_matrix_compare(cdesc.origin.matrix,
719193640Sariff		    cdesc.target.matrix) != 0)
720193640Sariff			cdesc.use_matrix = 1;
721193640Sariff
722193640Sariff		/* Soft EQ only applicable for PLAY. */
723193640Sariff		if (cdesc.dummy == 0 &&
724193640Sariff		    c->direction == PCMDIR_PLAY && (d->flags & SD_F_EQ) &&
725193640Sariff		    (((d->flags & SD_F_EQ_PC) &&
726193640Sariff		    !(c->flags & CHN_F_HAS_VCHAN)) ||
727193640Sariff		    (!(d->flags & SD_F_EQ_PC) && !(c->flags & CHN_F_VIRTUAL))))
728193640Sariff			cdesc.use_eq = 1;
729193640Sariff
730193640Sariff		if (FEEDFORMAT_NE_REQUIRED(&cdesc)) {
731193640Sariff			cdesc.afmt_ne =
732193640Sariff			    (cdesc.dummy != 0) ?
733193640Sariff			    snd_fmtbest(AFMT_ENCODING(softfmt),
734193640Sariff			    feeder_chain_formats[cdesc.mode]) :
735193640Sariff			    snd_fmtbest(AFMT_ENCODING(cdesc.target.afmt),
736193640Sariff			    feeder_chain_formats[cdesc.mode]);
737193640Sariff			if (cdesc.afmt_ne == 0) {
738193640Sariff				device_printf(c->dev,
739193640Sariff				    "%s(): snd_fmtbest failed!\n", __func__);
740193640Sariff				cdesc.afmt_ne =
741193640Sariff				    (((cdesc.dummy != 0) ? softfmt :
742193640Sariff				    cdesc.target.afmt) &
743193640Sariff				    (AFMT_24BIT | AFMT_32BIT)) ?
744193640Sariff				    AFMT_S32_NE : AFMT_S16_NE;
745193640Sariff			}
746193640Sariff		}
747193640Sariff	}
748193640Sariff
749193640Sariff	cdesc.current = cdesc.origin;
750193640Sariff
751193640Sariff	/* Build everything. */
752193640Sariff
753193640Sariff	c->feederflags = 0;
754193640Sariff
755193640Sariff#define FEEDER_BUILD(t)	do {						\
756193640Sariff	ret = feeder_build_##t(c, &cdesc);				\
757193640Sariff	if (ret != 0)							\
758193640Sariff		return (ret);						\
759193640Sariff	} while (0)
760193640Sariff
761193640Sariff	if (!(c->flags & CHN_F_HAS_VCHAN) || c->direction == PCMDIR_REC)
762193640Sariff		FEEDER_BUILD(root);
763193640Sariff	else if (c->direction == PCMDIR_PLAY && (c->flags & CHN_F_HAS_VCHAN))
764193640Sariff		FEEDER_BUILD(mixer);
765193640Sariff	else
766193640Sariff		return (ENOTSUP);
767193640Sariff
768193640Sariff	/*
769193640Sariff	 * The basic idea is: The smaller the bandwidth, the cheaper the
770193640Sariff	 * conversion process, with following constraints:-
771193640Sariff	 *
772193640Sariff	 * 1) Almost all feeders work best in 16/32 native endian.
773193640Sariff	 * 2) Try to avoid 8bit feeders due to poor dynamic range.
774193640Sariff	 * 3) Avoid volume, format, matrix and rate in BITPERFECT or
775193640Sariff	 *    PASSTHROUGH mode.
776193640Sariff	 * 4) Try putting volume before EQ or rate. Should help to
777193640Sariff	 *    avoid/reduce possible clipping.
778193640Sariff	 * 5) EQ require specific, valid rate, unless it allow sloppy
779193640Sariff	 *    conversion.
780193640Sariff	 */
781193640Sariff	if (FEEDMATRIX_UP(&cdesc)) {
782193640Sariff		if (FEEDEQ_REQUIRED(&cdesc) &&
783193640Sariff		    (!FEEDEQ_VALIDRATE(&cdesc, target) ||
784193640Sariff		    (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc))))
785193640Sariff			FEEDER_BUILD(eq);
786193640Sariff		if (FEEDRATE_REQUIRED(&cdesc))
787193640Sariff			FEEDER_BUILD(rate);
788193640Sariff		FEEDER_BUILD(matrix);
789193640Sariff		if (FEEDVOLUME_REQUIRED(&cdesc))
790193640Sariff			FEEDER_BUILD(volume);
791193640Sariff		if (FEEDEQ_REQUIRED(&cdesc))
792193640Sariff			FEEDER_BUILD(eq);
793193640Sariff	} else if (FEEDMATRIX_DOWN(&cdesc)) {
794193640Sariff		FEEDER_BUILD(matrix);
795193640Sariff		if (FEEDVOLUME_REQUIRED(&cdesc))
796193640Sariff			FEEDER_BUILD(volume);
797193640Sariff		if (FEEDEQ_REQUIRED(&cdesc) &&
798193640Sariff		    (!FEEDEQ_VALIDRATE(&cdesc, target) ||
799193640Sariff		    FEEDEQ_ECONOMY(&cdesc)))
800193640Sariff			FEEDER_BUILD(eq);
801193640Sariff		if (FEEDRATE_REQUIRED(&cdesc))
802193640Sariff			FEEDER_BUILD(rate);
803193640Sariff		if (FEEDEQ_REQUIRED(&cdesc))
804193640Sariff			FEEDER_BUILD(eq);
805193640Sariff	} else {
806193640Sariff		if (FEEDRATE_DOWN(&cdesc)) {
807193640Sariff			if (FEEDEQ_REQUIRED(&cdesc) &&
808193640Sariff			    !FEEDEQ_VALIDRATE(&cdesc, target)) {
809193640Sariff				if (FEEDVOLUME_REQUIRED(&cdesc))
810193640Sariff					FEEDER_BUILD(volume);
811193640Sariff				FEEDER_BUILD(eq);
812193640Sariff			}
813193640Sariff			FEEDER_BUILD(rate);
814193640Sariff		}
815193640Sariff		if (FEEDMATRIX_REQUIRED(&cdesc))
816193640Sariff			FEEDER_BUILD(matrix);
817193640Sariff		if (FEEDVOLUME_REQUIRED(&cdesc))
818193640Sariff			FEEDER_BUILD(volume);
819193640Sariff		if (FEEDRATE_UP(&cdesc)) {
820193640Sariff			if (FEEDEQ_REQUIRED(&cdesc) &&
821193640Sariff			    !FEEDEQ_VALIDRATE(&cdesc, target))
822193640Sariff				FEEDER_BUILD(eq);
823193640Sariff			FEEDER_BUILD(rate);
824193640Sariff		}
825193640Sariff		if (FEEDEQ_REQUIRED(&cdesc))
826193640Sariff			FEEDER_BUILD(eq);
827193640Sariff	}
828193640Sariff
829193640Sariff	if (FEEDFORMAT_REQUIRED(&cdesc))
830193640Sariff		FEEDER_BUILD(format);
831193640Sariff
832193640Sariff	if (c->direction == PCMDIR_REC && (c->flags & CHN_F_HAS_VCHAN))
833193640Sariff		FEEDER_BUILD(mixer);
834193640Sariff
835193640Sariff	sndbuf_setfmt(c->bufsoft, c->format);
836193640Sariff	sndbuf_setspd(c->bufsoft, c->speed);
837193640Sariff
838193640Sariff	sndbuf_setfmt(c->bufhard, hwfmt);
839193640Sariff
840193640Sariff	chn_syncstate(c);
841193640Sariff
842193640Sariff	return (0);
843193640Sariff}
844