feeder_chain.c revision 193640
1139749Simp/*- 2113584Ssimokawa * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org> 3103285Sikob * All rights reserved. 4103285Sikob * 5103285Sikob * Redistribution and use in source and binary forms, with or without 6103285Sikob * modification, are permitted provided that the following conditions 7103285Sikob * are met: 8103285Sikob * 1. Redistributions of source code must retain the above copyright 9103285Sikob * notice, this list of conditions and the following disclaimer. 10103285Sikob * 2. Redistributions in binary form must reproduce the above copyright 11103285Sikob * notice, this list of conditions and the following disclaimer in the 12103285Sikob * documentation and/or other materials provided with the distribution. 13103285Sikob * 14103285Sikob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16103285Sikob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17103285Sikob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18103285Sikob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20103285Sikob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21103285Sikob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24103285Sikob * SUCH DAMAGE. 25103285Sikob */ 26103285Sikob 27103285Sikob#ifdef HAVE_KERNEL_OPTION_HEADERS 28103285Sikob#include "opt_snd.h" 29103285Sikob#endif 30103285Sikob 31103285Sikob#include <dev/sound/pcm/sound.h> 32103285Sikob 33103285Sikob#include "feeder_if.h" 34103285Sikob 35103285SikobSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_chain.c 193640 2009-06-07 19:12:08Z ariff $"); 36103285Sikob 37150968Sglebius/* chain state */ 38150968Sglebiusstruct feeder_chain_state { 39103285Sikob uint32_t afmt; /* audio format */ 40150968Sglebius uint32_t rate; /* sampling rate */ 41103285Sikob struct pcmchan_matrix *matrix; /* matrix map */ 42103285Sikob}; 43103285Sikob 44103285Sikob/* 45103285Sikob * chain descriptor that will be passed around from the beginning until the 46103285Sikob * end of chain process. 47103285Sikob */ 48103285Sikobstruct feeder_chain_desc { 49103285Sikob struct feeder_chain_state origin; /* original state */ 50103285Sikob struct feeder_chain_state current; /* current state */ 51103285Sikob struct feeder_chain_state target; /* target state */ 52113584Ssimokawa struct pcm_feederdesc desc; /* feeder descriptor */ 53103285Sikob uint32_t afmt_ne; /* prefered native endian */ 54103285Sikob int mode; /* chain mode */ 55103285Sikob int use_eq; /* need EQ? */ 56103285Sikob int use_matrix; /* need channel matrixing? */ 57103285Sikob int use_volume; /* need softpcmvol? */ 58147256Sbrooks int dummy; /* dummy passthrough */ 59127468Ssimokawa int expensive; /* possibly expensive */ 60127468Ssimokawa}; 61127468Ssimokawa 62127468Ssimokawa#define FEEDER_CHAIN_LEAN 0 63127468Ssimokawa#define FEEDER_CHAIN_16 1 64127468Ssimokawa#define FEEDER_CHAIN_32 2 65103285Sikob#define FEEDER_CHAIN_MULTI 3 66103285Sikob#define FEEDER_CHAIN_FULLMULTI 4 67103285Sikob#define FEEDER_CHAIN_LAST 5 68103285Sikob 69103285Sikob#if defined(SND_FEEDER_FULL_MULTIFORMAT) 70127468Ssimokawa#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_FULLMULTI 71103285Sikob#elif defined(SND_FEEDER_MULTIFORMAT) 72122161Ssimokawa#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_MULTI 73111942Ssimokawa#else 74103285Sikob#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_LEAN 75103285Sikob#endif 76124169Ssimokawa 77124169Ssimokawa/* 78124169Ssimokawa * List of prefered formats that might be required during 79103285Sikob * processing. It will be decided through snd_fmtbest(). 80124169Ssimokawa */ 81124169Ssimokawa 82124169Ssimokawa/* 'Lean' mode, signed 16 or 32 bit native endian. */ 83103285Sikobstatic uint32_t feeder_chain_formats_lean[] = { 84103285Sikob AFMT_S16_NE, AFMT_S32_NE, 85103285Sikob 0 86116139Ssimokawa}; 87122603Ssimokawa 88103285Sikob/* Force everything to signed 16 bit native endian. */ 89227293Sedstatic uint32_t feeder_chain_formats_16[] = { 90103285Sikob AFMT_S16_NE, 91103285Sikob 0 92227309Sed}; 93122603Ssimokawa 94103285Sikob/* Force everything to signed 32 bit native endian. */ 95103285Sikobstatic uint32_t feeder_chain_formats_32[] = { 96116139Ssimokawa AFMT_S32_NE, 97122603Ssimokawa 0 98122603Ssimokawa}; 99122603Ssimokawa 100103285Sikob/* Multiple choices, all except 8 bit. */ 101122603Ssimokawastatic uint32_t feeder_chain_formats_multi[] = { 102122603Ssimokawa AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE, 103122603Ssimokawa AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE, 104122603Ssimokawa AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE, 105103285Sikob 0 106103285Sikob}; 107103285Sikob 108193096Sattilio/* Everything that is convertible. */ 109103285Sikobstatic uint32_t feeder_chain_formats_fullmulti[] = { 110103285Sikob AFMT_S8, AFMT_U8, 111103285Sikob AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE, 112103285Sikob AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE, 113103285Sikob AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE, 114150789Sglebius 0 115193096Sattilio}; 116150789Sglebius 117103285Sikobstatic uint32_t *feeder_chain_formats[FEEDER_CHAIN_LAST] = { 118103285Sikob [FEEDER_CHAIN_LEAN] = feeder_chain_formats_lean, 119103285Sikob [FEEDER_CHAIN_16] = feeder_chain_formats_16, 120193096Sattilio [FEEDER_CHAIN_32] = feeder_chain_formats_32, 121103285Sikob [FEEDER_CHAIN_MULTI] = feeder_chain_formats_multi, 122150789Sglebius [FEEDER_CHAIN_FULLMULTI] = feeder_chain_formats_fullmulti 123150789Sglebius}; 124103285Sikob 125103285Sikobstatic int feeder_chain_mode = FEEDER_CHAIN_DEFAULT; 126103285Sikob 127121953Ssimokawa#if defined(_KERNEL) && defined(SND_DEBUG) && defined(SND_FEEDER_FULL_MULTIFORMAT) 128103285SikobTUNABLE_INT("hw.snd.feeder_chain_mode", &feeder_chain_mode); 129103285SikobSYSCTL_INT(_hw_snd, OID_AUTO, feeder_chain_mode, CTLFLAG_RW, 130103285Sikob &feeder_chain_mode, 0, 131103285Sikob "feeder chain mode " 132103285Sikob "(0=lean, 1=16bit, 2=32bit, 3=multiformat, 4=fullmultiformat)"); 133103285Sikob#endif 134103285Sikob 135103285Sikob/* 136103285Sikob * feeder_build_format(): Chain any format converter. 137103285Sikob */ 138103285Sikobstatic int 139103285Sikobfeeder_build_format(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 140108281Ssimokawa{ 141103285Sikob struct feeder_class *fc; 142103285Sikob struct pcm_feederdesc *desc; 143103285Sikob int ret; 144103285Sikob 145103285Sikob desc = &(cdesc->desc); 146103285Sikob desc->type = FEEDER_FORMAT; 147103285Sikob desc->in = 0; 148103285Sikob desc->out = 0; 149103285Sikob desc->flags = 0; 150147256Sbrooks 151103285Sikob fc = feeder_getclass(desc); 152147256Sbrooks if (fc == NULL) { 153147256Sbrooks device_printf(c->dev, 154147256Sbrooks "%s(): can't find feeder_format\n", __func__); 155109814Ssimokawa return (ENOTSUP); 156103285Sikob } 157103285Sikob 158103285Sikob desc->in = cdesc->current.afmt; 159103285Sikob desc->out = cdesc->target.afmt; 160103285Sikob 161170374Ssimokawa ret = chn_addfeeder(c, fc, desc); 162103285Sikob if (ret != 0) { 163103285Sikob device_printf(c->dev, 164103285Sikob "%s(): can't add feeder_format\n", __func__); 165103285Sikob return (ret); 166103285Sikob } 167124251Ssimokawa 168124251Ssimokawa c->feederflags |= 1 << FEEDER_FORMAT; 169124251Ssimokawa 170103285Sikob cdesc->current.afmt = cdesc->target.afmt; 171103285Sikob 172103285Sikob return (0); 173103285Sikob} 174103285Sikob 175103285Sikob/* 176103285Sikob * feeder_build_formatne(): Chain format converter that suite best for native 177103285Sikob * endian format. 178103285Sikob */ 179103285Sikobstatic int 180103285Sikobfeeder_build_formatne(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 181147256Sbrooks{ 182147256Sbrooks struct feeder_chain_state otarget; 183147256Sbrooks int ret; 184109814Ssimokawa 185147256Sbrooks if (cdesc->afmt_ne == 0 || 186109814Ssimokawa AFMT_ENCODING(cdesc->current.afmt) == cdesc->afmt_ne) 187109814Ssimokawa return (0); 188109814Ssimokawa 189109814Ssimokawa otarget = cdesc->target; 190109814Ssimokawa cdesc->target = cdesc->current; 191109814Ssimokawa cdesc->target.afmt = SND_FORMAT(cdesc->afmt_ne, 192109814Ssimokawa cdesc->current.matrix->channels, cdesc->current.matrix->ext); 193107653Ssimokawa 194107653Ssimokawa ret = feeder_build_format(c, cdesc); 195103285Sikob if (ret != 0) 196103285Sikob return (ret); 197103285Sikob 198147256Sbrooks cdesc->target = otarget; 199147256Sbrooks 200147256Sbrooks return (0); 201147256Sbrooks} 202147256Sbrooks 203103285Sikob/* 204103285Sikob * feeder_build_rate(): Chain sample rate converter. 205127468Ssimokawa */ 206121953Ssimokawastatic int 207122212Ssimokawafeeder_build_rate(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 208122212Ssimokawa{ 209122212Ssimokawa struct feeder_class *fc; 210122212Ssimokawa struct pcm_feeder *f; 211103285Sikob struct pcm_feederdesc *desc; 212132430Ssimokawa int ret; 213132430Ssimokawa 214132430Ssimokawa ret = feeder_build_formatne(c, cdesc); 215103285Sikob if (ret != 0) 216103285Sikob return (ret); 217170374Ssimokawa 218111942Ssimokawa desc = &(cdesc->desc); 219103285Sikob desc->type = FEEDER_RATE; 220103285Sikob desc->in = 0; 221127468Ssimokawa desc->out = 0; 222127468Ssimokawa desc->flags = 0; 223127468Ssimokawa 224106937Ssam fc = feeder_getclass(desc); 225108712Ssimokawa if (fc == NULL) { 226103285Sikob device_printf(c->dev, 227103285Sikob "%s(): can't find feeder_rate\n", __func__); 228103285Sikob return (ENOTSUP); 229103285Sikob } 230127468Ssimokawa 231151229Sglebius desc->in = cdesc->current.afmt; 232129552Syar desc->out = desc->in; 233108712Ssimokawa 234103285Sikob ret = chn_addfeeder(c, fc, desc); 235103285Sikob if (ret != 0) { 236122161Ssimokawa device_printf(c->dev, 237103285Sikob "%s(): can't add feeder_rate\n", __func__); 238103285Sikob return (ret); 239103285Sikob } 240103285Sikob 241103285Sikob f = c->feeder; 242103285Sikob 243103285Sikob /* 244103285Sikob * If in 'dummy' mode (possibly due to passthrough mode), set the 245147256Sbrooks * conversion quality to the lowest possible (should be fastest) since 246111942Ssimokawa * listener won't be hearing anything. Theoretically we can just 247111942Ssimokawa * disable it, but that will cause weird runtime behaviour: 248103285Sikob * application appear to play something that is either too fast or too 249103285Sikob * slow. 250103285Sikob */ 251103285Sikob if (cdesc->dummy != 0) { 252103285Sikob ret = FEEDER_SET(f, FEEDRATE_QUALITY, 0); 253103285Sikob if (ret != 0) { 254103285Sikob device_printf(c->dev, 255103285Sikob "%s(): can't set resampling quality\n", __func__); 256103285Sikob return (ret); 257113584Ssimokawa } 258113584Ssimokawa } 259111942Ssimokawa 260111942Ssimokawa ret = FEEDER_SET(f, FEEDRATE_SRC, cdesc->current.rate); 261111942Ssimokawa if (ret != 0) { 262111942Ssimokawa device_printf(c->dev, 263111942Ssimokawa "%s(): can't set source rate\n", __func__); 264111942Ssimokawa return (ret); 265111942Ssimokawa } 266111942Ssimokawa 267111942Ssimokawa ret = FEEDER_SET(f, FEEDRATE_DST, cdesc->target.rate); 268111942Ssimokawa if (ret != 0) { 269111942Ssimokawa device_printf(c->dev, 270111942Ssimokawa "%s(): can't set destination rate\n", __func__); 271111942Ssimokawa return (ret); 272111942Ssimokawa } 273103285Sikob 274103285Sikob c->feederflags |= 1 << FEEDER_RATE; 275103285Sikob 276148887Srwatson cdesc->current.rate = cdesc->target.rate; 277148887Srwatson 278148887Srwatson return (0); 279103285Sikob} 280148887Srwatson 281103285Sikob/* 282103285Sikob * feeder_build_matrix(): Chain channel matrixing converter. 283103285Sikob */ 284103285Sikobstatic int 285103285Sikobfeeder_build_matrix(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 286103285Sikob{ 287147256Sbrooks struct feeder_class *fc; 288103285Sikob struct pcm_feeder *f; 289103285Sikob struct pcm_feederdesc *desc; 290147256Sbrooks int ret; 291147256Sbrooks 292150789Sglebius ret = feeder_build_formatne(c, cdesc); 293150789Sglebius if (ret != 0) 294150789Sglebius return (ret); 295150789Sglebius 296150789Sglebius desc = &(cdesc->desc); 297103285Sikob desc->type = FEEDER_MATRIX; 298103285Sikob desc->in = 0; 299103285Sikob desc->out = 0; 300127468Ssimokawa desc->flags = 0; 301147256Sbrooks 302127468Ssimokawa fc = feeder_getclass(desc); 303147256Sbrooks if (fc == NULL) { 304147256Sbrooks device_printf(c->dev, 305108712Ssimokawa "%s(): can't find feeder_matrix\n", __func__); 306103285Sikob return (ENOTSUP); 307103285Sikob } 308170374Ssimokawa 309103285Sikob desc->in = cdesc->current.afmt; 310103285Sikob desc->out = SND_FORMAT(cdesc->current.afmt, 311103285Sikob cdesc->target.matrix->channels, cdesc->target.matrix->ext); 312103285Sikob 313103285Sikob ret = chn_addfeeder(c, fc, desc); 314103285Sikob if (ret != 0) { 315103285Sikob device_printf(c->dev, 316103285Sikob "%s(): can't add feeder_matrix\n", __func__); 317147256Sbrooks return (ret); 318103285Sikob } 319111942Ssimokawa 320113584Ssimokawa f = c->feeder; 321103285Sikob ret = feeder_matrix_setup(f, cdesc->current.matrix, 322103285Sikob cdesc->target.matrix); 323122161Ssimokawa if (ret != 0) { 324103285Sikob device_printf(c->dev, 325103285Sikob "%s(): feeder_matrix_setup() failed\n", __func__); 326103285Sikob return (ret); 327103285Sikob } 328103285Sikob 329103285Sikob c->feederflags |= 1 << FEEDER_MATRIX; 330170374Ssimokawa 331170374Ssimokawa cdesc->current.afmt = desc->out; 332170374Ssimokawa cdesc->current.matrix = cdesc->target.matrix; 333170374Ssimokawa cdesc->use_matrix = 0; 334170374Ssimokawa 335170374Ssimokawa return (0); 336103285Sikob} 337103285Sikob 338112400Ssimokawa/* 339103285Sikob * feeder_build_volume(): Chain soft volume. 340103285Sikob */ 341103285Sikobstatic int 342103285Sikobfeeder_build_volume(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 343122603Ssimokawa{ 344111942Ssimokawa struct feeder_class *fc; 345111942Ssimokawa struct pcm_feeder *f; 346111942Ssimokawa struct pcm_feederdesc *desc; 347113584Ssimokawa int ret; 348111942Ssimokawa 349113584Ssimokawa ret = feeder_build_formatne(c, cdesc); 350113584Ssimokawa if (ret != 0) 351111942Ssimokawa return (ret); 352111942Ssimokawa 353111942Ssimokawa desc = &(cdesc->desc); 354111942Ssimokawa desc->type = FEEDER_VOLUME; 355111942Ssimokawa desc->in = 0; 356111942Ssimokawa desc->out = 0; 357111942Ssimokawa desc->flags = 0; 358111942Ssimokawa 359111942Ssimokawa fc = feeder_getclass(desc); 360243857Sglebius if (fc == NULL) { 361113584Ssimokawa device_printf(c->dev, 362177599Sru "%s(): can't find feeder_volume\n", __func__); 363177599Sru return (ENOTSUP); 364177599Sru } 365111942Ssimokawa 366111942Ssimokawa desc->in = cdesc->current.afmt; 367111942Ssimokawa desc->out = desc->in; 368111942Ssimokawa 369111942Ssimokawa ret = chn_addfeeder(c, fc, desc); 370111942Ssimokawa if (ret != 0) { 371120660Ssimokawa device_printf(c->dev, 372111942Ssimokawa "%s(): can't add feeder_volume\n", __func__); 373111942Ssimokawa return (ret); 374167632Ssimokawa } 375111942Ssimokawa 376111942Ssimokawa f = c->feeder; 377103285Sikob 378103285Sikob /* 379103285Sikob * If in 'dummy' mode (possibly due to passthrough mode), set BYPASS 380103285Sikob * mode since listener won't be hearing anything. Theoretically we can 381103285Sikob * just disable it, but that will confuse volume per channel mixer. 382103285Sikob */ 383103285Sikob if (cdesc->dummy != 0) { 384103285Sikob ret = FEEDER_SET(f, FEEDVOLUME_STATE, FEEDVOLUME_BYPASS); 385148887Srwatson if (ret != 0) { 386148887Srwatson device_printf(c->dev, 387148887Srwatson "%s(): can't set volume bypass\n", __func__); 388148887Srwatson return (ret); 389103285Sikob } 390103285Sikob } 391148887Srwatson 392103285Sikob ret = feeder_volume_apply_matrix(f, cdesc->current.matrix); 393103285Sikob if (ret != 0) { 394103285Sikob device_printf(c->dev, 395103285Sikob "%s(): feeder_volume_apply_matrix() failed\n", __func__); 396103285Sikob return (ret); 397103285Sikob } 398103285Sikob 399103285Sikob c->feederflags |= 1 << FEEDER_VOLUME; 400103285Sikob 401103285Sikob cdesc->use_volume = 0; 402103285Sikob 403103285Sikob return (0); 404103285Sikob} 405103285Sikob 406103285Sikob/* 407103285Sikob * feeder_build_eq(): Chain parametric software equalizer. 408103285Sikob */ 409103285Sikobstatic int 410103285Sikobfeeder_build_eq(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 411148887Srwatson{ 412148887Srwatson struct feeder_class *fc; 413148887Srwatson struct pcm_feeder *f; 414103285Sikob struct pcm_feederdesc *desc; 415148887Srwatson int ret; 416103285Sikob 417103285Sikob ret = feeder_build_formatne(c, cdesc); 418148887Srwatson if (ret != 0) 419148887Srwatson return (ret); 420148887Srwatson 421103285Sikob desc = &(cdesc->desc); 422148887Srwatson desc->type = FEEDER_EQ; 423103285Sikob desc->in = 0; 424103285Sikob desc->out = 0; 425103285Sikob desc->flags = 0; 426103285Sikob 427103285Sikob fc = feeder_getclass(desc); 428103285Sikob if (fc == NULL) { 429103285Sikob device_printf(c->dev, 430103285Sikob "%s(): can't find feeder_eq\n", __func__); 431108712Ssimokawa return (ENOTSUP); 432103285Sikob } 433103285Sikob 434103285Sikob desc->in = cdesc->current.afmt; 435103285Sikob desc->out = desc->in; 436103285Sikob 437103285Sikob ret = chn_addfeeder(c, fc, desc); 438103285Sikob if (ret != 0) { 439103285Sikob device_printf(c->dev, 440103285Sikob "%s(): can't add feeder_eq\n", __func__); 441103285Sikob return (ret); 442103285Sikob } 443108712Ssimokawa 444150789Sglebius f = c->feeder; 445150789Sglebius 446150789Sglebius ret = FEEDER_SET(f, FEEDEQ_RATE, cdesc->current.rate); 447150789Sglebius if (ret != 0) { 448188394Sfjoe device_printf(c->dev, 449150789Sglebius "%s(): can't set rate on feeder_eq\n", __func__); 450150789Sglebius return (ret); 451150789Sglebius } 452150789Sglebius 453150789Sglebius c->feederflags |= 1 << FEEDER_EQ; 454150789Sglebius 455150789Sglebius cdesc->use_eq = 0; 456150789Sglebius 457150789Sglebius return (0); 458193096Sattilio} 459150789Sglebius 460150789Sglebius/* 461150789Sglebius * feeder_build_root(): Chain root feeder, the top, father of all. 462150789Sglebius */ 463150789Sglebiusstatic int 464150789Sglebiusfeeder_build_root(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 465150789Sglebius{ 466150789Sglebius struct feeder_class *fc; 467193096Sattilio int ret; 468150789Sglebius 469150789Sglebius fc = feeder_getclass(NULL); 470150789Sglebius if (fc == NULL) { 471150789Sglebius device_printf(c->dev, 472150789Sglebius "%s(): can't find feeder_root\n", __func__); 473127468Ssimokawa return (ENOTSUP); 474103285Sikob } 475108712Ssimokawa 476108712Ssimokawa ret = chn_addfeeder(c, fc, NULL); 477108712Ssimokawa if (ret != 0) { 478108712Ssimokawa device_printf(c->dev, 479108712Ssimokawa "%s(): can't add feeder_root\n", __func__); 480106937Ssam return (ret); 481106937Ssam } 482106937Ssam 483106937Ssam c->feederflags |= 1 << FEEDER_ROOT; 484127468Ssimokawa 485108712Ssimokawa c->feeder->desc->in = cdesc->current.afmt; 486108712Ssimokawa c->feeder->desc->out = cdesc->current.afmt; 487108712Ssimokawa 488103285Sikob return (0); 489103285Sikob} 490103285Sikob 491103285Sikob/* 492103285Sikob * feeder_build_mixer(): Chain software mixer for virtual channels. 493103285Sikob */ 494111942Ssimokawastatic int 495111942Ssimokawafeeder_build_mixer(struct pcm_channel *c, struct feeder_chain_desc *cdesc) 496111942Ssimokawa{ 497111942Ssimokawa struct feeder_class *fc; 498111942Ssimokawa struct pcm_feederdesc *desc; 499111942Ssimokawa int ret; 500111942Ssimokawa 501147256Sbrooks desc = &(cdesc->desc); 502111942Ssimokawa desc->type = FEEDER_MIXER; 503122161Ssimokawa desc->in = 0; 504111942Ssimokawa desc->out = 0; 505111942Ssimokawa desc->flags = 0; 506111942Ssimokawa 507111942Ssimokawa fc = feeder_getclass(desc); 508111942Ssimokawa if (fc == NULL) { 509113584Ssimokawa device_printf(c->dev, 510111942Ssimokawa "%s(): can't find feeder_mixer\n", __func__); 511170374Ssimokawa return (ENOTSUP); 512111942Ssimokawa } 513170374Ssimokawa 514111942Ssimokawa desc->in = cdesc->current.afmt; 515113584Ssimokawa desc->out = desc->in; 516113584Ssimokawa 517111942Ssimokawa ret = chn_addfeeder(c, fc, desc); 518111942Ssimokawa if (ret != 0) { 519111942Ssimokawa device_printf(c->dev, 520111942Ssimokawa "%s(): can't add feeder_mixer\n", __func__); 521111942Ssimokawa return (ret); 522103285Sikob } 523103285Sikob 524103285Sikob c->feederflags |= 1 << FEEDER_MIXER; 525103285Sikob 526103285Sikob return (0); 527122161Ssimokawa} 528103285Sikob 529103285Sikob/* Macrosses to ease our job doing stuffs later. */ 530103285Sikob#define FEEDER_BW(c, t) ((c)->t.matrix->channels * (c)->t.rate) 531103285Sikob 532122161Ssimokawa#define FEEDRATE_UP(c) ((c)->target.rate > (c)->current.rate) 533103285Sikob#define FEEDRATE_DOWN(c) ((c)->target.rate < (c)->current.rate) 534103285Sikob#define FEEDRATE_REQUIRED(c) (FEEDRATE_UP(c) || FEEDRATE_DOWN(c)) 535103285Sikob 536103285Sikob#define FEEDMATRIX_UP(c) ((c)->target.matrix->channels > \ 537103285Sikob (c)->current.matrix->channels) 538103285Sikob#define FEEDMATRIX_DOWN(c) ((c)->target.matrix->channels < \ 539103285Sikob (c)->current.matrix->channels) 540103285Sikob#define FEEDMATRIX_REQUIRED(c) (FEEDMATRIX_UP(c) || \ 541103285Sikob FEEDMATRIX_DOWN(c) || (c)->use_matrix != 0) 542103285Sikob 543103285Sikob#define FEEDFORMAT_REQUIRED(c) (AFMT_ENCODING((c)->current.afmt) != \ 544103285Sikob AFMT_ENCODING((c)->target.afmt)) 545103285Sikob 546103285Sikob#define FEEDVOLUME_REQUIRED(c) ((c)->use_volume != 0) 547148887Srwatson 548148887Srwatson#define FEEDEQ_VALIDRATE(c, t) (feeder_eq_validrate((c)->t.rate) != 0) 549148887Srwatson#define FEEDEQ_ECONOMY(c) (FEEDER_BW(c, current) < FEEDER_BW(c, target)) 550103285Sikob#define FEEDEQ_REQUIRED(c) ((c)->use_eq != 0 && \ 551148887Srwatson FEEDEQ_VALIDRATE(c, current)) 552103285Sikob 553103285Sikob#define FEEDFORMAT_NE_REQUIRED(c) \ 554103285Sikob ((c)->afmt_ne != AFMT_S32_NE && \ 555103285Sikob (((c)->mode == FEEDER_CHAIN_16 && \ 556148887Srwatson AFMT_ENCODING((c)->current.afmt) != AFMT_S16_NE) || \ 557148887Srwatson ((c)->mode == FEEDER_CHAIN_32 && \ 558148887Srwatson AFMT_ENCODING((c)->current.afmt) != AFMT_S32_NE) || \ 559103285Sikob (c)->mode == FEEDER_CHAIN_FULLMULTI || \ 560148887Srwatson ((c)->mode == FEEDER_CHAIN_MULTI && \ 561103285Sikob ((c)->current.afmt & AFMT_8BIT)) || \ 562103285Sikob ((c)->mode == FEEDER_CHAIN_LEAN && \ 563103285Sikob !((c)->current.afmt & (AFMT_S16_NE | AFMT_S32_NE))))) 564111942Ssimokawa 565111942Ssimokawaint 566111942Ssimokawafeeder_chain(struct pcm_channel *c) 567103285Sikob{ 568103285Sikob struct snddev_info *d; 569103285Sikob struct pcmchan_caps *caps; 570103285Sikob struct feeder_chain_desc cdesc; 571103285Sikob struct pcmchan_matrix *hwmatrix, *softmatrix; 572103285Sikob uint32_t hwfmt, softfmt; 573103285Sikob int ret; 574103285Sikob 575103285Sikob CHN_LOCKASSERT(c); 576103285Sikob 577103285Sikob /* Remove everything first. */ 578103285Sikob while (chn_removefeeder(c) == 0) 579103285Sikob ; 580170374Ssimokawa 581170374Ssimokawa KASSERT(c->feeder == NULL, ("feeder chain not empty")); 582170374Ssimokawa 583111942Ssimokawa /* clear and populate chain descriptor. */ 584111942Ssimokawa bzero(&cdesc, sizeof(cdesc)); 585170374Ssimokawa 586111942Ssimokawa switch (feeder_chain_mode) { 587170374Ssimokawa case FEEDER_CHAIN_LEAN: 588170374Ssimokawa case FEEDER_CHAIN_16: 589170374Ssimokawa case FEEDER_CHAIN_32: 590111942Ssimokawa#if defined(SND_FEEDER_MULTIFORMAT) || defined(SND_FEEDER_FULL_MULTIFORMAT) 591170374Ssimokawa case FEEDER_CHAIN_MULTI: 592170374Ssimokawa#endif 593170374Ssimokawa#if defined(SND_FEEDER_FULL_MULTIFORMAT) 594103285Sikob case FEEDER_CHAIN_FULLMULTI: 595170374Ssimokawa#endif 596170374Ssimokawa break; 597170374Ssimokawa default: 598170374Ssimokawa feeder_chain_mode = FEEDER_CHAIN_DEFAULT; 599103285Sikob break; 600170374Ssimokawa } 601127468Ssimokawa 602108712Ssimokawa cdesc.mode = feeder_chain_mode; 603108712Ssimokawa cdesc.expensive = 1; /* XXX faster.. */ 604127468Ssimokawa 605127468Ssimokawa#define VCHAN_PASSTHROUGH(c) (((c)->flags & (CHN_F_VIRTUAL | \ 606108712Ssimokawa CHN_F_PASSTHROUGH)) == \ 607103285Sikob (CHN_F_VIRTUAL | CHN_F_PASSTHROUGH)) 608103285Sikob 609243857Sglebius /* Get the best possible hardware format. */ 610120660Ssimokawa if (VCHAN_PASSTHROUGH(c)) 611129585Sdfr hwfmt = c->parentchannel->format; 612113584Ssimokawa else { 613103285Sikob caps = chn_getcaps(c); 614120660Ssimokawa if (caps == NULL || caps->fmtlist == NULL) { 615103285Sikob device_printf(c->dev, 616111942Ssimokawa "%s(): failed to get channel caps\n", __func__); 617103285Sikob return (ENODEV); 618103285Sikob } 619103285Sikob 620103285Sikob if ((c->format & AFMT_PASSTHROUGH) && 621103285Sikob !snd_fmtvalid(c->format, caps->fmtlist)) 622103285Sikob return (ENODEV); 623111942Ssimokawa 624103285Sikob hwfmt = snd_fmtbest(c->format, caps->fmtlist); 625103285Sikob if (hwfmt == 0 || !snd_fmtvalid(hwfmt, caps->fmtlist)) { 626103285Sikob device_printf(c->dev, 627103285Sikob "%s(): invalid hardware format 0x%08x\n", 628103285Sikob __func__, hwfmt); 629103285Sikob { 630111942Ssimokawa int i; 631111942Ssimokawa for (i = 0; caps->fmtlist[i] != 0; i++) 632103285Sikob printf("0x%08x\n", caps->fmtlist[i]); 633103285Sikob printf("Req: 0x%08x\n", c->format); 634103285Sikob } 635103285Sikob return (ENODEV); 636103285Sikob } 637103285Sikob } 638113584Ssimokawa 639103285Sikob /* 640103285Sikob * The 'hardware' possibly have different intepretation of channel 641111942Ssimokawa * matrixing, so get it first ..... 642111942Ssimokawa */ 643103285Sikob hwmatrix = CHANNEL_GETMATRIX(c->methods, c->devinfo, hwfmt); 644127468Ssimokawa if (hwmatrix == NULL) { 645111942Ssimokawa device_printf(c->dev, 646111942Ssimokawa "%s(): failed to acquire hw matrix [0x%08x]\n", 647103285Sikob __func__, hwfmt); 648103285Sikob return (ENODEV); 649147256Sbrooks } 650150789Sglebius /* ..... and rebuild hwfmt. */ 651170374Ssimokawa hwfmt = SND_FORMAT(hwfmt, hwmatrix->channels, hwmatrix->ext); 652111942Ssimokawa 653111942Ssimokawa /* Reset and rebuild default channel format/matrix map. */ 654113584Ssimokawa softfmt = c->format; 655111942Ssimokawa softmatrix = &c->matrix; 656111942Ssimokawa if (softmatrix->channels != AFMT_CHANNEL(softfmt) || 657111942Ssimokawa softmatrix->ext != AFMT_EXTCHANNEL(softfmt)) { 658111942Ssimokawa softmatrix = feeder_matrix_format_map(softfmt); 659119119Ssimokawa if (softmatrix == NULL) { 660243857Sglebius device_printf(c->dev, 661113584Ssimokawa "%s(): failed to acquire soft matrix [0x%08x]\n", 662113584Ssimokawa __func__, softfmt); 663113584Ssimokawa return (ENODEV); 664113584Ssimokawa } 665170374Ssimokawa c->matrix = *softmatrix; 666111942Ssimokawa c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; 667119119Ssimokawa } 668119119Ssimokawa softfmt = SND_FORMAT(softfmt, softmatrix->channels, softmatrix->ext); 669119119Ssimokawa if (softfmt != c->format) 670119119Ssimokawa device_printf(c->dev, 671119119Ssimokawa "%s(): WARNING: %s Soft format 0x%08x -> 0x%08x\n", 672119119Ssimokawa __func__, CHN_DIRSTR(c), c->format, softfmt); 673119119Ssimokawa 674111942Ssimokawa /* 675170374Ssimokawa * PLAY and REC are opposite. 676127468Ssimokawa */ 677111942Ssimokawa if (c->direction == PCMDIR_PLAY) { 678111942Ssimokawa cdesc.origin.afmt = softfmt; 679132429Ssimokawa cdesc.origin.matrix = softmatrix; 680132429Ssimokawa cdesc.origin.rate = c->speed; 681132429Ssimokawa cdesc.target.afmt = hwfmt; 682132429Ssimokawa cdesc.target.matrix = hwmatrix; 683108712Ssimokawa cdesc.target.rate = sndbuf_getspd(c->bufhard); 684103285Sikob } else { 685103285Sikob cdesc.origin.afmt = hwfmt; 686122161Ssimokawa cdesc.origin.matrix = hwmatrix; 687103285Sikob cdesc.origin.rate = sndbuf_getspd(c->bufhard); 688103285Sikob cdesc.target.afmt = softfmt; 689103285Sikob cdesc.target.matrix = softmatrix; 690103285Sikob cdesc.target.rate = c->speed; 691103285Sikob } 692103285Sikob 693103285Sikob d = c->parentsnddev; 694103285Sikob 695103285Sikob /* 696103285Sikob * If channel is in bitperfect or passthrough mode, make it appear 697103285Sikob * that 'origin' and 'target' identical, skipping mostly chain 698103285Sikob * procedures. 699103285Sikob */ 700127468Ssimokawa if (CHN_BITPERFECT(c) || (c->format & AFMT_PASSTHROUGH)) { 701127468Ssimokawa if (c->direction == PCMDIR_PLAY) 702127468Ssimokawa cdesc.origin = cdesc.target; 703106937Ssam else 704108712Ssimokawa cdesc.target = cdesc.origin; 705103285Sikob c->format = cdesc.target.afmt; 706103285Sikob c->speed = cdesc.target.rate; 707111942Ssimokawa } else { 708111942Ssimokawa /* hwfmt is not convertible, so 'dummy' it. */ 709103285Sikob if (hwfmt & AFMT_PASSTHROUGH) 710103285Sikob cdesc.dummy = 1; 711103285Sikob 712103285Sikob if ((softfmt & AFMT_CONVERTIBLE) && 713103285Sikob (((d->flags & SD_F_VPC) && !(c->flags & CHN_F_HAS_VCHAN)) || 714103285Sikob (!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) && 715103285Sikob !(c->flags & CHN_F_VIRTUAL)))) 716103285Sikob cdesc.use_volume = 1; 717103285Sikob 718103285Sikob if (feeder_matrix_compare(cdesc.origin.matrix, 719103285Sikob cdesc.target.matrix) != 0) 720103285Sikob cdesc.use_matrix = 1; 721103285Sikob 722103285Sikob /* Soft EQ only applicable for PLAY. */ 723103285Sikob if (cdesc.dummy == 0 && 724121953Ssimokawa c->direction == PCMDIR_PLAY && (d->flags & SD_F_EQ) && 725103285Sikob (((d->flags & SD_F_EQ_PC) && 726103285Sikob !(c->flags & CHN_F_HAS_VCHAN)) || 727103285Sikob (!(d->flags & SD_F_EQ_PC) && !(c->flags & CHN_F_VIRTUAL)))) 728103285Sikob cdesc.use_eq = 1; 729103285Sikob 730127468Ssimokawa if (FEEDFORMAT_NE_REQUIRED(&cdesc)) { 731127468Ssimokawa cdesc.afmt_ne = 732127468Ssimokawa (cdesc.dummy != 0) ? 733113506Smdodd snd_fmtbest(AFMT_ENCODING(softfmt), 734113506Smdodd feeder_chain_formats[cdesc.mode]) : 735113506Smdodd snd_fmtbest(AFMT_ENCODING(cdesc.target.afmt), 736 feeder_chain_formats[cdesc.mode]); 737 if (cdesc.afmt_ne == 0) { 738 device_printf(c->dev, 739 "%s(): snd_fmtbest failed!\n", __func__); 740 cdesc.afmt_ne = 741 (((cdesc.dummy != 0) ? softfmt : 742 cdesc.target.afmt) & 743 (AFMT_24BIT | AFMT_32BIT)) ? 744 AFMT_S32_NE : AFMT_S16_NE; 745 } 746 } 747 } 748 749 cdesc.current = cdesc.origin; 750 751 /* Build everything. */ 752 753 c->feederflags = 0; 754 755#define FEEDER_BUILD(t) do { \ 756 ret = feeder_build_##t(c, &cdesc); \ 757 if (ret != 0) \ 758 return (ret); \ 759 } while (0) 760 761 if (!(c->flags & CHN_F_HAS_VCHAN) || c->direction == PCMDIR_REC) 762 FEEDER_BUILD(root); 763 else if (c->direction == PCMDIR_PLAY && (c->flags & CHN_F_HAS_VCHAN)) 764 FEEDER_BUILD(mixer); 765 else 766 return (ENOTSUP); 767 768 /* 769 * The basic idea is: The smaller the bandwidth, the cheaper the 770 * conversion process, with following constraints:- 771 * 772 * 1) Almost all feeders work best in 16/32 native endian. 773 * 2) Try to avoid 8bit feeders due to poor dynamic range. 774 * 3) Avoid volume, format, matrix and rate in BITPERFECT or 775 * PASSTHROUGH mode. 776 * 4) Try putting volume before EQ or rate. Should help to 777 * avoid/reduce possible clipping. 778 * 5) EQ require specific, valid rate, unless it allow sloppy 779 * conversion. 780 */ 781 if (FEEDMATRIX_UP(&cdesc)) { 782 if (FEEDEQ_REQUIRED(&cdesc) && 783 (!FEEDEQ_VALIDRATE(&cdesc, target) || 784 (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc)))) 785 FEEDER_BUILD(eq); 786 if (FEEDRATE_REQUIRED(&cdesc)) 787 FEEDER_BUILD(rate); 788 FEEDER_BUILD(matrix); 789 if (FEEDVOLUME_REQUIRED(&cdesc)) 790 FEEDER_BUILD(volume); 791 if (FEEDEQ_REQUIRED(&cdesc)) 792 FEEDER_BUILD(eq); 793 } else if (FEEDMATRIX_DOWN(&cdesc)) { 794 FEEDER_BUILD(matrix); 795 if (FEEDVOLUME_REQUIRED(&cdesc)) 796 FEEDER_BUILD(volume); 797 if (FEEDEQ_REQUIRED(&cdesc) && 798 (!FEEDEQ_VALIDRATE(&cdesc, target) || 799 FEEDEQ_ECONOMY(&cdesc))) 800 FEEDER_BUILD(eq); 801 if (FEEDRATE_REQUIRED(&cdesc)) 802 FEEDER_BUILD(rate); 803 if (FEEDEQ_REQUIRED(&cdesc)) 804 FEEDER_BUILD(eq); 805 } else { 806 if (FEEDRATE_DOWN(&cdesc)) { 807 if (FEEDEQ_REQUIRED(&cdesc) && 808 !FEEDEQ_VALIDRATE(&cdesc, target)) { 809 if (FEEDVOLUME_REQUIRED(&cdesc)) 810 FEEDER_BUILD(volume); 811 FEEDER_BUILD(eq); 812 } 813 FEEDER_BUILD(rate); 814 } 815 if (FEEDMATRIX_REQUIRED(&cdesc)) 816 FEEDER_BUILD(matrix); 817 if (FEEDVOLUME_REQUIRED(&cdesc)) 818 FEEDER_BUILD(volume); 819 if (FEEDRATE_UP(&cdesc)) { 820 if (FEEDEQ_REQUIRED(&cdesc) && 821 !FEEDEQ_VALIDRATE(&cdesc, target)) 822 FEEDER_BUILD(eq); 823 FEEDER_BUILD(rate); 824 } 825 if (FEEDEQ_REQUIRED(&cdesc)) 826 FEEDER_BUILD(eq); 827 } 828 829 if (FEEDFORMAT_REQUIRED(&cdesc)) 830 FEEDER_BUILD(format); 831 832 if (c->direction == PCMDIR_REC && (c->flags & CHN_F_HAS_VCHAN)) 833 FEEDER_BUILD(mixer); 834 835 sndbuf_setfmt(c->bufsoft, c->format); 836 sndbuf_setspd(c->bufsoft, c->speed); 837 838 sndbuf_setfmt(c->bufhard, hwfmt); 839 840 chn_syncstate(c); 841 842 return (0); 843} 844