1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* feeder_volume, a long 'Lost Technology' rather than a new feature. */ 30 31 #ifdef _KERNEL 32 #ifdef HAVE_KERNEL_OPTION_HEADERS 33 #include "opt_snd.h" 34 #endif 35 #include <dev/sound/pcm/sound.h> 36 #include <dev/sound/pcm/pcm.h> 37 #include "feeder_if.h" 38 39 #define SND_USE_FXDIV 40 #include "snd_fxdiv_gen.h" 41 #endif 42 43 typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t); 44 45 #define FEEDVOLUME_CALC8(s, v) (SND_VOL_CALC_SAMPLE((intpcm_t) \ 46 (s) << 8, v) >> 8) 47 #define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v) 48 #define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) 49 #define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) 50 51 #define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN) \ 52 static void \ 53 feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \ 54 uint32_t channels, uint8_t *dst, uint32_t count) \ 55 { \ 56 intpcm##BIT##_t v; \ 57 intpcm_t x; \ 58 uint32_t i; \ 59 \ 60 dst += count * PCM_##BIT##_BPS * channels; \ 61 do { \ 62 i = channels; \ 63 do { \ 64 dst -= PCM_##BIT##_BPS; \ 65 i--; \ 66 x = pcm_sample_read_calc(dst, \ 67 AFMT_##SIGN##BIT##_##ENDIAN); \ 68 v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \ 69 x = pcm_clamp_calc(v, \ 70 AFMT_##SIGN##BIT##_##ENDIAN); \ 71 pcm_sample_write(dst, x, \ 72 AFMT_##SIGN##BIT##_##ENDIAN); \ 73 } while (i != 0); \ 74 } while (--count != 0); \ 75 } 76 77 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 78 FEEDVOLUME_DECLARE(S, 16, LE) 79 FEEDVOLUME_DECLARE(S, 32, LE) 80 #endif 81 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 82 FEEDVOLUME_DECLARE(S, 16, BE) 83 FEEDVOLUME_DECLARE(S, 32, BE) 84 #endif 85 #ifdef SND_FEEDER_MULTIFORMAT 86 FEEDVOLUME_DECLARE(S, 8, NE) 87 FEEDVOLUME_DECLARE(S, 24, LE) 88 FEEDVOLUME_DECLARE(S, 24, BE) 89 FEEDVOLUME_DECLARE(U, 8, NE) 90 FEEDVOLUME_DECLARE(U, 16, LE) 91 FEEDVOLUME_DECLARE(U, 24, LE) 92 FEEDVOLUME_DECLARE(U, 32, LE) 93 FEEDVOLUME_DECLARE(U, 16, BE) 94 FEEDVOLUME_DECLARE(U, 24, BE) 95 FEEDVOLUME_DECLARE(U, 32, BE) 96 FEEDVOLUME_DECLARE(F, 32, LE) 97 FEEDVOLUME_DECLARE(F, 32, BE) 98 #endif 99 100 struct feed_volume_info { 101 uint32_t bps, channels; 102 feed_volume_t apply; 103 int volume_class; 104 int state; 105 int matrix[SND_CHN_MAX]; 106 }; 107 108 #define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \ 109 { \ 110 AFMT_##SIGN##BIT##_##ENDIAN, \ 111 feed_volume_##SIGN##BIT##ENDIAN \ 112 } 113 114 static const struct { 115 uint32_t format; 116 feed_volume_t apply; 117 } feed_volume_info_tab[] = { 118 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 119 FEEDVOLUME_ENTRY(S, 16, LE), 120 FEEDVOLUME_ENTRY(S, 32, LE), 121 #endif 122 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 123 FEEDVOLUME_ENTRY(S, 16, BE), 124 FEEDVOLUME_ENTRY(S, 32, BE), 125 #endif 126 #ifdef SND_FEEDER_MULTIFORMAT 127 FEEDVOLUME_ENTRY(S, 8, NE), 128 FEEDVOLUME_ENTRY(S, 24, LE), 129 FEEDVOLUME_ENTRY(S, 24, BE), 130 FEEDVOLUME_ENTRY(U, 8, NE), 131 FEEDVOLUME_ENTRY(U, 16, LE), 132 FEEDVOLUME_ENTRY(U, 24, LE), 133 FEEDVOLUME_ENTRY(U, 32, LE), 134 FEEDVOLUME_ENTRY(U, 16, BE), 135 FEEDVOLUME_ENTRY(U, 24, BE), 136 FEEDVOLUME_ENTRY(U, 32, BE), 137 FEEDVOLUME_ENTRY(F, 32, LE), 138 FEEDVOLUME_ENTRY(F, 32, BE), 139 #endif 140 }; 141 142 #define FEEDVOLUME_TAB_SIZE ((int32_t) \ 143 (sizeof(feed_volume_info_tab) / \ 144 sizeof(feed_volume_info_tab[0]))) 145 146 static int 147 feed_volume_init(struct pcm_feeder *f) 148 { 149 struct feed_volume_info *info; 150 struct pcmchan_matrix *m; 151 uint32_t i; 152 int ret; 153 154 if (f->desc.in != f->desc.out || 155 AFMT_CHANNEL(f->desc.in) > SND_CHN_MAX) 156 return (EINVAL); 157 158 for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) { 159 if (AFMT_ENCODING(f->desc.in) == 160 feed_volume_info_tab[i].format) { 161 info = malloc(sizeof(*info), M_DEVBUF, 162 M_NOWAIT | M_ZERO); 163 if (info == NULL) 164 return (ENOMEM); 165 166 info->bps = AFMT_BPS(f->desc.in); 167 info->channels = AFMT_CHANNEL(f->desc.in); 168 info->apply = feed_volume_info_tab[i].apply; 169 info->volume_class = SND_VOL_C_PCM; 170 info->state = FEEDVOLUME_ENABLE; 171 172 f->data = info; 173 m = feeder_matrix_default_channel_map(info->channels); 174 if (m == NULL) { 175 free(info, M_DEVBUF); 176 return (EINVAL); 177 } 178 179 ret = feeder_volume_apply_matrix(f, m); 180 if (ret != 0) 181 free(info, M_DEVBUF); 182 183 return (ret); 184 } 185 } 186 187 return (EINVAL); 188 } 189 190 static int 191 feed_volume_free(struct pcm_feeder *f) 192 { 193 struct feed_volume_info *info; 194 195 info = f->data; 196 free(info, M_DEVBUF); 197 198 f->data = NULL; 199 200 return (0); 201 } 202 203 static int 204 feed_volume_set(struct pcm_feeder *f, int what, int value) 205 { 206 struct feed_volume_info *info; 207 struct pcmchan_matrix *m; 208 int ret; 209 210 info = f->data; 211 ret = 0; 212 213 switch (what) { 214 case FEEDVOLUME_CLASS: 215 if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END) 216 return (EINVAL); 217 info->volume_class = value; 218 break; 219 case FEEDVOLUME_CHANNELS: 220 if (value < SND_CHN_MIN || value > SND_CHN_MAX) 221 return (EINVAL); 222 m = feeder_matrix_default_channel_map(value); 223 if (m == NULL) 224 return (EINVAL); 225 ret = feeder_volume_apply_matrix(f, m); 226 break; 227 case FEEDVOLUME_STATE: 228 if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS)) 229 return (EINVAL); 230 info->state = value; 231 break; 232 default: 233 return (EINVAL); 234 } 235 236 return (ret); 237 } 238 239 static int 240 feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 241 uint32_t count, void *source) 242 { 243 int temp_vol[SND_CHN_T_VOL_MAX]; 244 struct feed_volume_info *info; 245 struct snd_mixer *m; 246 struct snddev_info *d; 247 uint32_t j, align; 248 int i, *matrix; 249 uint8_t *dst; 250 const int16_t *vol; 251 const int8_t *muted; 252 bool master_muted = false; 253 254 /* 255 * Fetch filter data operation. 256 */ 257 info = f->data; 258 259 if (info->state == FEEDVOLUME_BYPASS) 260 return (FEEDER_FEED(f->source, c, b, count, source)); 261 262 vol = c->volume[SND_VOL_C_VAL(info->volume_class)]; 263 muted = c->muted[SND_VOL_C_VAL(info->volume_class)]; 264 matrix = info->matrix; 265 266 /* 267 * First, let see if we really need to apply gain at all. 268 */ 269 j = 0; 270 i = info->channels; 271 while (i--) { 272 if (vol[matrix[i]] != SND_VOL_FLAT || 273 muted[matrix[i]] != 0) { 274 j = 1; 275 break; 276 } 277 } 278 279 /* Nope, just bypass entirely. */ 280 if (j == 0) 281 return (FEEDER_FEED(f->source, c, b, count, source)); 282 283 /* Check if any controls are muted. */ 284 d = (c != NULL) ? c->parentsnddev : NULL; 285 m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : NULL; 286 287 if (m != NULL) 288 master_muted = (mix_getmutedevs(m) & (1 << SND_VOL_C_MASTER)); 289 290 for (j = 0; j != SND_CHN_T_VOL_MAX; j++) 291 temp_vol[j] = (muted[j] || master_muted) ? 0 : vol[j]; 292 293 dst = b; 294 align = info->bps * info->channels; 295 296 do { 297 if (count < align) 298 break; 299 300 j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), 301 align); 302 if (j == 0) 303 break; 304 305 info->apply(temp_vol, matrix, info->channels, dst, j); 306 307 j *= align; 308 dst += j; 309 count -= j; 310 311 } while (count != 0); 312 313 return (dst - b); 314 } 315 316 static kobj_method_t feeder_volume_methods[] = { 317 KOBJMETHOD(feeder_init, feed_volume_init), 318 KOBJMETHOD(feeder_free, feed_volume_free), 319 KOBJMETHOD(feeder_set, feed_volume_set), 320 KOBJMETHOD(feeder_feed, feed_volume_feed), 321 KOBJMETHOD_END 322 }; 323 324 FEEDER_DECLARE(feeder_volume, FEEDER_VOLUME); 325 326 /* Extern */ 327 328 /* 329 * feeder_volume_apply_matrix(): For given matrix map, apply its configuration 330 * to feeder_volume matrix structure. There are 331 * possibilites that feeder_volume be inserted 332 * before or after feeder_matrix, which in this 333 * case feeder_volume must be in a good terms 334 * with _current_ matrix. 335 */ 336 int 337 feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m) 338 { 339 struct feed_volume_info *info; 340 uint32_t i; 341 342 if (f == NULL || f->class->type != FEEDER_VOLUME || f->data == NULL || 343 m == NULL || m->channels < SND_CHN_MIN || 344 m->channels > SND_CHN_MAX) 345 return (EINVAL); 346 347 info = f->data; 348 349 for (i = 0; i < nitems(info->matrix); i++) { 350 if (i < m->channels) 351 info->matrix[i] = m->map[i].type; 352 else 353 info->matrix[i] = SND_CHN_T_FL; 354 } 355 356 info->channels = m->channels; 357 358 return (0); 359 } 360