12061Sjkh/*- 250479Speter * SPDX-License-Identifier: BSD-2-Clause 32061Sjkh * 438666Sjb * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 532427Sjb * All rights reserved. 6111131Sru * 7111131Sru * Redistribution and use in source and binary forms, with or without 8217733Sbz * modification, are permitted provided that the following conditions 9217733Sbz * are met: 1038666Sjb * 1. Redistributions of source code must retain the above copyright 1138666Sjb * notice, this list of conditions and the following disclaimer. 1238666Sjb * 2. Redistributions in binary form must reproduce the above copyright 13159363Strhodes * notice, this list of conditions and the following disclaimer in the 1464049Salex * documentation and/or other materials provided with the distribution. 1564049Salex * 16116679Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1766071Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18116679Ssimokawa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1973504Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20204661Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21232907Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22158962Snetchild * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23223148Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24169597Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25169597Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26169597Sdes * SUCH DAMAGE. 27169597Sdes */ 28231821Spluknet 29169597Sdes/* feeder_volume, a long 'Lost Technology' rather than a new feature. */ 30169597Sdes 31169597Sdes#ifdef _KERNEL 32217815Sbz#ifdef HAVE_KERNEL_OPTION_HEADERS 33217815Sbz#include "opt_snd.h" 34218524Sjhb#endif 35264811Sbrueffer#include <dev/sound/pcm/sound.h> 36264811Sbrueffer#include <dev/sound/pcm/pcm.h> 37264811Sbrueffer#include "feeder_if.h" 38264811Sbrueffer 39253002Salfred#define SND_USE_FXDIV 40253002Salfred#include "snd_fxdiv_gen.h" 41253002Salfred#endif 42253002Salfred 43253002Salfredtypedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t); 44253003Salfred 4532427Sjb#define FEEDVOLUME_CALC8(s, v) (SND_VOL_CALC_SAMPLE((intpcm_t) \ 4638666Sjb (s) << 8, v) >> 8) 47108451Sschweikh#define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v) 4838666Sjb#define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) 4938666Sjb#define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) 5038666Sjb 5138666Sjb#define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN) \ 5217308Speterstatic void \ 53217273Simpfeed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \ 54217294Simp uint32_t channels, uint8_t *dst, uint32_t count) \ 5519175Sbde{ \ 5696205Sjwd intpcm##BIT##_t v; \ 57217297Simp intpcm_t x; \ 58217297Simp uint32_t i; \ 5938042Sbde \ 6096205Sjwd dst += count * PCM_##BIT##_BPS * channels; \ 6196205Sjwd do { \ 6238042Sbde i = channels; \ 6396205Sjwd do { \ 64159363Strhodes dst -= PCM_##BIT##_BPS; \ 65159363Strhodes i--; \ 6617308Speter x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ 6796205Sjwd v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \ 6896205Sjwd x = PCM_CLAMP_##SIGN##BIT(v); \ 6917308Speter _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ 70148330Snetchild } while (i != 0); \ 71148330Snetchild } while (--count != 0); \ 72148330Snetchild} 73148330Snetchild 74159831Sobrien#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 75148330SnetchildFEEDVOLUME_DECLARE(S, 16, LE) 76148330SnetchildFEEDVOLUME_DECLARE(S, 32, LE) 77148330Snetchild#endif 78251107Screes#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 79251107ScreesFEEDVOLUME_DECLARE(S, 16, BE) 80148330SnetchildFEEDVOLUME_DECLARE(S, 32, BE) 81148330Snetchild#endif 8296205Sjwd#ifdef SND_FEEDER_MULTIFORMAT 8396205SjwdFEEDVOLUME_DECLARE(S, 8, NE) 8496205SjwdFEEDVOLUME_DECLARE(S, 24, LE) 85162147SruFEEDVOLUME_DECLARE(S, 24, BE) 86162147SruFEEDVOLUME_DECLARE(U, 8, NE) 8798723SdillonFEEDVOLUME_DECLARE(U, 16, LE) 8898723SdillonFEEDVOLUME_DECLARE(U, 24, LE) 8998723SdillonFEEDVOLUME_DECLARE(U, 32, LE) 9038666SjbFEEDVOLUME_DECLARE(U, 16, BE) 9138666SjbFEEDVOLUME_DECLARE(U, 24, BE) 9217308SpeterFEEDVOLUME_DECLARE(U, 32, BE) 93123311Speter#endif 94123311Speter 95123311Speterstruct feed_volume_info { 96123311Speter uint32_t bps, channels; 97175833Sjhb feed_volume_t apply; 98175833Sjhb int volume_class; 99169597Sdes int state; 100169597Sdes int matrix[SND_CHN_MAX]; 101169597Sdes}; 102169597Sdes 103219177Snwhitehorn#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \ 104219177Snwhitehorn { \ 105238051Sobrien AFMT_##SIGN##BIT##_##ENDIAN, \ 106219177Snwhitehorn feed_volume_##SIGN##BIT##ENDIAN \ 107219177Snwhitehorn } 108158962Snetchild 109156840Srustatic const struct { 110123311Speter uint32_t format; 111137288Speter feed_volume_t apply; 112209128Sraj} feed_volume_info_tab[] = { 113209128Sraj#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 114156740Sru FEEDVOLUME_ENTRY(S, 16, LE), 1152061Sjkh FEEDVOLUME_ENTRY(S, 32, LE), 11697769Sru#endif 11797252Sru#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 118119579Sru FEEDVOLUME_ENTRY(S, 16, BE), 11997252Sru FEEDVOLUME_ENTRY(S, 32, BE), 12095730Sru#endif 12195793Sru#ifdef SND_FEEDER_MULTIFORMAT 122111617Sru FEEDVOLUME_ENTRY(S, 8, NE), 12395730Sru FEEDVOLUME_ENTRY(S, 24, LE), 124116679Ssimokawa FEEDVOLUME_ENTRY(S, 24, BE), 12595730Sru FEEDVOLUME_ENTRY(U, 8, NE), 126116679Ssimokawa FEEDVOLUME_ENTRY(U, 16, LE), 12795730Sru FEEDVOLUME_ENTRY(U, 24, LE), 128110035Sru FEEDVOLUME_ENTRY(U, 32, LE), 129107516Sru FEEDVOLUME_ENTRY(U, 16, BE), 130138921Sru FEEDVOLUME_ENTRY(U, 24, BE), 131156145Syar FEEDVOLUME_ENTRY(U, 32, BE) 132138921Sru#endif 133133942Sru}; 134133942Sru 135156145Syar#define FEEDVOLUME_TAB_SIZE ((int32_t) \ 136133942Sru (sizeof(feed_volume_info_tab) / \ 137253616Ssjg sizeof(feed_volume_info_tab[0]))) 138253616Ssjg 139253616Ssjgstatic int 140253616Ssjgfeed_volume_init(struct pcm_feeder *f) 141253616Ssjg{ 142253616Ssjg struct feed_volume_info *info; 143253616Ssjg struct pcmchan_matrix *m; 144253616Ssjg uint32_t i; 145253616Ssjg int ret; 146253616Ssjg 147253616Ssjg if (f->desc->in != f->desc->out || 148253616Ssjg AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX) 149253616Ssjg return (EINVAL); 150253616Ssjg 151253616Ssjg for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) { 152253616Ssjg if (AFMT_ENCODING(f->desc->in) == 153253616Ssjg feed_volume_info_tab[i].format) { 154253616Ssjg info = malloc(sizeof(*info), M_DEVBUF, 155253616Ssjg M_NOWAIT | M_ZERO); 156253616Ssjg if (info == NULL) 157253616Ssjg return (ENOMEM); 158253616Ssjg 159117229Sru info->bps = AFMT_BPS(f->desc->in); 160253616Ssjg info->channels = AFMT_CHANNEL(f->desc->in); 161253616Ssjg info->apply = feed_volume_info_tab[i].apply; 162253616Ssjg info->volume_class = SND_VOL_C_PCM; 16354324Smarcel info->state = FEEDVOLUME_ENABLE; 164253616Ssjg 165253616Ssjg f->data = info; 166218130Simp m = feeder_matrix_default_channel_map(info->channels); 167218130Simp if (m == NULL) { 168233644Sjmallett free(info, M_DEVBUF); 169218130Simp return (EINVAL); 170218130Simp } 171239272Sgonzo 172218130Simp ret = feeder_volume_apply_matrix(f, m); 173233644Sjmallett if (ret != 0) 174218130Simp free(info, M_DEVBUF); 175233644Sjmallett 176233644Sjmallett return (ret); 177233644Sjmallett } 178218130Simp } 179233644Sjmallett 180233644Sjmallett return (EINVAL); 181218130Simp} 182218130Simp 183218130Simpstatic int 184218130Simpfeed_volume_free(struct pcm_feeder *f) 185218130Simp{ 186218130Simp struct feed_volume_info *info; 187218130Simp 188218130Simp info = f->data; 189218130Simp if (info != NULL) 190218130Simp free(info, M_DEVBUF); 191218130Simp 192218130Simp f->data = NULL; 193218130Simp 194218130Simp return (0); 195218130Simp} 196218130Simp 19717308Speterstatic int 198119519Smarcelfeed_volume_set(struct pcm_feeder *f, int what, int value) 199119519Smarcel{ 200119519Smarcel struct feed_volume_info *info; 201119519Smarcel struct pcmchan_matrix *m; 202119519Smarcel int ret; 203119519Smarcel 204119579Sru info = f->data; 205119519Smarcel ret = 0; 206119519Smarcel 207119519Smarcel switch (what) { 208119519Smarcel case FEEDVOLUME_CLASS: 209119519Smarcel if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END) 210126031Sgad return (EINVAL); 211126024Sgad info->volume_class = value; 212126024Sgad break; 213126024Sgad case FEEDVOLUME_CHANNELS: 214126024Sgad if (value < SND_CHN_MIN || value > SND_CHN_MAX) 215126024Sgad return (EINVAL); 216126024Sgad m = feeder_matrix_default_channel_map(value); 217126024Sgad if (m == NULL) 218227771Sgjb return (EINVAL); 219126024Sgad ret = feeder_volume_apply_matrix(f, m); 220126024Sgad break; 221227769Sgjb case FEEDVOLUME_STATE: 222227771Sgjb if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS)) 223227771Sgjb return (EINVAL); 224126024Sgad info->state = value; 225126024Sgad break; 226126031Sgad default: 227126024Sgad return (EINVAL); 228126024Sgad break; 229126024Sgad } 230172744Sdelphij 231126024Sgad return (ret); 232126024Sgad} 233126024Sgad 234133376Shartistatic int 235126024Sgadfeed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 236126024Sgad uint32_t count, void *source) 237172744Sdelphij{ 238126024Sgad int temp_vol[SND_CHN_T_VOL_MAX]; 239126024Sgad struct feed_volume_info *info; 240125885Sgad uint32_t j, align; 241125885Sgad int i, *matrix; 24238666Sjb uint8_t *dst; 24317308Speter const int16_t *vol; 244119519Smarcel const int8_t *muted; 245251750Ssjg 246251750Ssjg /* 247251750Ssjg * Fetch filter data operation. 248251750Ssjg */ 249254417Ssjg info = f->data; 250251750Ssjg 251251750Ssjg if (info->state == FEEDVOLUME_BYPASS) 252119579Sru return (FEEDER_FEED(f->source, c, b, count, source)); 253218206Simp 2542302Spaul vol = c->volume[SND_VOL_C_VAL(info->volume_class)]; 255265449Sbrooks muted = c->muted[SND_VOL_C_VAL(info->volume_class)]; 256265449Sbrooks matrix = info->matrix; 257265449Sbrooks 258265449Sbrooks /* 259265449Sbrooks * First, let see if we really need to apply gain at all. 26039206Sjkh */ 261265449Sbrooks j = 0; 262265449Sbrooks i = info->channels; 263265449Sbrooks while (i--) { 264265449Sbrooks if (vol[matrix[i]] != SND_VOL_FLAT || 265265449Sbrooks muted[matrix[i]] != 0) { 266265449Sbrooks j = 1; 267133945Sru break; 268240403Sobrien } 269177609Sru } 270177609Sru 271177609Sru /* Nope, just bypass entirely. */ 272133945Sru if (j == 0) 273132358Smarkm return (FEEDER_FEED(f->source, c, b, count, source)); 27417308Speter 27554324Smarcel /* Check if any controls are muted. */ 27654324Smarcel for (j = 0; j != SND_CHN_T_VOL_MAX; j++) 277132234Smarcel temp_vol[j] = muted[j] ? 0 : vol[j]; 278132234Smarcel 279132234Smarcel dst = b; 280132234Smarcel align = info->bps * info->channels; 28154324Smarcel 28254324Smarcel do { 28354324Smarcel if (count < align) 284118531Sru break; 28554324Smarcel 28654324Smarcel j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), 28754324Smarcel align); 28854324Smarcel if (j == 0) 28954324Smarcel break; 29054324Smarcel 291133376Sharti info->apply(temp_vol, matrix, info->channels, dst, j); 29254324Smarcel 293133376Sharti j *= align; 294133376Sharti dst += j; 29554324Smarcel count -= j; 29654324Smarcel 29754324Smarcel } while (count != 0); 29854324Smarcel 29954324Smarcel return (dst - b); 300133376Sharti} 30154324Smarcel 30254324Smarcelstatic struct pcm_feederdesc feeder_volume_desc[] = { 30354324Smarcel { FEEDER_VOLUME, 0, 0, 0, 0 }, 304118531Sru { 0, 0, 0, 0, 0 } 305118531Sru}; 30654324Smarcel 307132234Smarcelstatic kobj_method_t feeder_volume_methods[] = { 308132234Smarcel KOBJMETHOD(feeder_init, feed_volume_init), 309132234Smarcel KOBJMETHOD(feeder_free, feed_volume_free), 310132234Smarcel KOBJMETHOD(feeder_set, feed_volume_set), 311132234Smarcel KOBJMETHOD(feeder_feed, feed_volume_feed), 312132588Skensmith KOBJMETHOD_END 313132358Smarkm}; 314132234Smarcel 315132358SmarkmFEEDER_DECLARE(feeder_volume, NULL); 316132234Smarcel 317132234Smarcel/* Extern */ 318132234Smarcel 31954324Smarcel/* 32054324Smarcel * feeder_volume_apply_matrix(): For given matrix map, apply its configuration 32195730Sru * to feeder_volume matrix structure. There are 32295730Sru * possibilites that feeder_volume be inserted 32395730Sru * before or after feeder_matrix, which in this 32495730Sru * case feeder_volume must be in a good terms 32595730Sru * with _current_ matrix. 32695730Sru */ 32795730Sruint 32838666Sjbfeeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m) 329107374Sru{ 33017308Speter struct feed_volume_info *info; 331253616Ssjg uint32_t i; 332253616Ssjg 333253616Ssjg if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME || 33455678Smarcel f->data == NULL || m == NULL || m->channels < SND_CHN_MIN || 335253616Ssjg m->channels > SND_CHN_MAX) 336253616Ssjg return (EINVAL); 337253616Ssjg 338143032Sharti info = f->data; 339138515Sharti 340117793Sru for (i = 0; i < nitems(info->matrix); i++) { 341110035Sru if (i < m->channels) 342174564Simp info->matrix[i] = m->map[i].type; 343110035Sru else 344241298Smarcel info->matrix[i] = SND_CHN_T_FL; 3452061Sjkh } 34617308Speter 347107516Sru info->channels = m->channels; 348174539Simp 349174539Simp return (0); 35055678Smarcel} 351253616Ssjg