sound(4): Implement mixer mute control for feeder channels.

PR:	258711
Differential Revision:	https://reviews.freebsd.org/D31636
MFC after:	1 week
Sponsored by:	NVIDIA Networking
This commit is contained in:
Hans Petter Selasky 2021-09-28 09:41:18 +02:00
parent 45d6fbaec2
commit 4a83ca1078
4 changed files with 167 additions and 52 deletions

View File

@ -1223,6 +1223,8 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER;
c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm;
memset(c->muted, 0, sizeof(c->muted));
chn_vpc_reset(c, SND_VOL_C_PCM, 1);
ret = ENODEV;
@ -1394,6 +1396,75 @@ chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt)
return (c->volume[vc][vt]);
}
int
chn_setmute_multi(struct pcm_channel *c, int vc, int mute)
{
int i, ret;
ret = 0;
for (i = 0; i < SND_CHN_T_MAX; i++) {
if ((1 << i) & SND_CHN_LEFT_MASK)
ret |= chn_setmute_matrix(c, vc, i, mute);
else if ((1 << i) & SND_CHN_RIGHT_MASK)
ret |= chn_setmute_matrix(c, vc, i, mute) << 8;
else
ret |= chn_setmute_matrix(c, vc, i, mute) << 16;
}
return (ret);
}
int
chn_setmute_matrix(struct pcm_channel *c, int vc, int vt, int mute)
{
int i;
KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
(vc == SND_VOL_C_MASTER || (vc & 1)) &&
(vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
("%s(): invalid mute matrix c=%p vc=%d vt=%d mute=%d",
__func__, c, vc, vt, mute));
CHN_LOCKASSERT(c);
mute = (mute != 0);
c->muted[vc][vt] = mute;
/*
* Do relative calculation here and store it into class + 1
* to ease the job of feeder_volume.
*/
if (vc == SND_VOL_C_MASTER) {
for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END;
vc += SND_VOL_C_STEP)
c->muted[SND_VOL_C_VAL(vc)][vt] = mute;
} else if (vc & 1) {
if (vt == SND_CHN_T_VOL_0DB) {
for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
i += SND_CHN_T_STEP) {
c->muted[SND_VOL_C_VAL(vc)][i] = mute;
}
} else {
c->muted[SND_VOL_C_VAL(vc)][vt] = mute;
}
}
return (mute);
}
int
chn_getmute_matrix(struct pcm_channel *c, int vc, int vt)
{
KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
(vt == SND_CHN_T_VOL_0DB ||
(vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
("%s(): invalid mute matrix c=%p vc=%d vt=%d",
__func__, c, vc, vt));
CHN_LOCKASSERT(c);
return (c->muted[vc][vt]);
}
struct pcmchan_matrix *
chn_getmatrix(struct pcm_channel *c)
{

View File

@ -166,7 +166,8 @@ struct pcm_channel {
struct pcmchan_matrix matrix;
struct pcmchan_matrix matrix_scratch;
int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
int16_t volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
int8_t muted[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
void *data1, *data2;
};
@ -271,6 +272,9 @@ int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
int center);
int chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val);
int chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt);
int chn_setmute_multi(struct pcm_channel *c, int vc, int mute);
int chn_setmute_matrix(struct pcm_channel *c, int vc, int vt, int mute);
int chn_getmute_matrix(struct pcm_channel *c, int vc, int vt);
void chn_vpc_reset(struct pcm_channel *c, int vc, int force);
int chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed);
int chn_setspeed(struct pcm_channel *c, uint32_t speed);
@ -307,6 +311,8 @@ int chn_syncdestroy(struct pcm_channel *c);
#define CHN_GETVOLUME(x, y, z) ((x)->volume[y][z])
#endif
#define CHN_GETMUTE(x, y, z) ((x)->muted[y][z])
#ifdef OSSV4_EXPERIMENT
int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
#endif

View File

@ -965,6 +965,7 @@ dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd,
struct snddev_info *d;
struct pcm_channel *rdch, *wrch;
int j, devtype, ret;
int left, right, center, mute;
d = dsp_get_info(dev);
if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC))
@ -1003,67 +1004,95 @@ dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd,
}
/* Final validation */
if (volch != NULL) {
CHN_LOCK(volch);
if (!(volch->feederflags & (1 << FEEDER_VOLUME))) {
CHN_UNLOCK(volch);
return (-1);
}
if (volch->direction == PCMDIR_PLAY)
wrch = volch;
else
rdch = volch;
if (volch == NULL)
return (EINVAL);
CHN_LOCK(volch);
if (!(volch->feederflags & (1 << FEEDER_VOLUME))) {
CHN_UNLOCK(volch);
return (EINVAL);
}
ret = EINVAL;
if (volch != NULL &&
((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) ||
(j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) {
if ((cmd & ~0xff) == MIXER_WRITE(0)) {
int left, right, center;
switch (cmd & ~0xff) {
case MIXER_WRITE(0):
switch (j) {
case SOUND_MIXER_MUTE:
if (volch->direction == PCMDIR_REC) {
chn_setmute_multi(volch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_RECLEV) != 0);
} else {
chn_setmute_multi(volch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_PCM) != 0);
}
break;
case SOUND_MIXER_PCM:
if (volch->direction != PCMDIR_PLAY)
break;
left = *(int *)arg & 0x7f;
right = ((*(int *)arg) >> 8) & 0x7f;
center = (left + right) >> 1;
chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right,
center);
} else if ((cmd & ~0xff) == MIXER_READ(0)) {
*(int *)arg = CHN_GETVOLUME(volch,
SND_VOL_C_PCM, SND_CHN_T_FL);
*(int *)arg |= CHN_GETVOLUME(volch,
SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
chn_setvolume_multi(volch, SND_VOL_C_PCM,
left, right, center);
break;
case SOUND_MIXER_RECLEV:
if (volch->direction != PCMDIR_REC)
break;
left = *(int *)arg & 0x7f;
right = ((*(int *)arg) >> 8) & 0x7f;
center = (left + right) >> 1;
chn_setvolume_multi(volch, SND_VOL_C_PCM,
left, right, center);
break;
default:
/* ignore all other mixer writes */
break;
}
ret = 0;
} else if (rdch != NULL || wrch != NULL) {
break;
case MIXER_READ(0):
switch (j) {
case SOUND_MIXER_MUTE:
mute = CHN_GETMUTE(volch, SND_VOL_C_PCM, SND_CHN_T_FL) ||
CHN_GETMUTE(volch, SND_VOL_C_PCM, SND_CHN_T_FR);
if (volch->direction == PCMDIR_REC) {
*(int *)arg = mute << SOUND_MIXER_RECLEV;
} else {
*(int *)arg = mute << SOUND_MIXER_PCM;
}
break;
case SOUND_MIXER_PCM:
if (volch->direction != PCMDIR_PLAY)
break;
*(int *)arg = CHN_GETVOLUME(volch,
SND_VOL_C_PCM, SND_CHN_T_FL);
*(int *)arg |= CHN_GETVOLUME(volch,
SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
break;
case SOUND_MIXER_RECLEV:
if (volch->direction != PCMDIR_REC)
break;
*(int *)arg = CHN_GETVOLUME(volch,
SND_VOL_C_PCM, SND_CHN_T_FL);
*(int *)arg |= CHN_GETVOLUME(volch,
SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
break;
case SOUND_MIXER_DEVMASK:
case SOUND_MIXER_CAPS:
case SOUND_MIXER_STEREODEVS:
if ((cmd & ~0xff) == MIXER_READ(0)) {
*(int *)arg = 0;
if (rdch != NULL)
*(int *)arg |= SOUND_MASK_RECLEV;
if (wrch != NULL)
*(int *)arg |= SOUND_MASK_PCM;
}
ret = 0;
break;
case SOUND_MIXER_RECMASK:
case SOUND_MIXER_RECSRC:
if ((cmd & ~0xff) == MIXER_READ(0))
*(int *)arg = 0;
ret = 0;
if (volch->direction == PCMDIR_REC)
*(int *)arg = SOUND_MASK_RECLEV;
else
*(int *)arg = SOUND_MASK_PCM;
break;
default:
*(int *)arg = 0;
break;
}
break;
default:
break;
}
if (volch != NULL)
CHN_UNLOCK(volch);
return (ret);
CHN_UNLOCK(volch);
return (0);
}
static int

View File

@ -237,10 +237,13 @@ static int
feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
int temp_vol[SND_CHN_T_VOL_MAX];
struct feed_volume_info *info;
uint32_t j, align;
int i, *vol, *matrix;
int i, *matrix;
uint8_t *dst;
const int16_t *vol;
const int8_t *muted;
/*
* Fetch filter data operation.
@ -251,6 +254,7 @@ feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
return (FEEDER_FEED(f->source, c, b, count, source));
vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
muted = c->muted[SND_VOL_C_VAL(info->volume_class)];
matrix = info->matrix;
/*
@ -258,17 +262,22 @@ feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
*/
j = 0;
i = info->channels;
do {
if (vol[matrix[--i]] != SND_VOL_FLAT) {
while (i--) {
if (vol[matrix[i]] != SND_VOL_FLAT ||
muted[matrix[i]] != 0) {
j = 1;
break;
}
} while (i != 0);
}
/* Nope, just bypass entirely. */
if (j == 0)
return (FEEDER_FEED(f->source, c, b, count, source));
/* Check if any controls are muted. */
for (j = 0; j != SND_CHN_T_VOL_MAX; j++)
temp_vol[j] = muted[j] ? 0 : vol[j];
dst = b;
align = info->bps * info->channels;
@ -281,7 +290,7 @@ feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
if (j == 0)
break;
info->apply(vol, matrix, info->channels, dst, j);
info->apply(temp_vol, matrix, info->channels, dst, j);
j *= align;
dst += j;