diff options
author | Jacob Meuser <jakemsr@cvs.openbsd.org> | 2008-10-25 22:30:44 +0000 |
---|---|---|
committer | Jacob Meuser <jakemsr@cvs.openbsd.org> | 2008-10-25 22:30:44 +0000 |
commit | fe8114f730e5473eba5ee55c81c541e29edbeea8 (patch) | |
tree | 545de71dde8d0a2e03bc8556eeaa4beb581824dd /sys/dev/pci/azalia.c | |
parent | d54880b2f6a9ada7238d4417b82adb8ce1f2ae77 (diff) |
audio(9) says low level drivers are allowed to change the requested
values of the audio_params structure during AUDIO_SETINFO if the
hardware cannot be set to exactly the requested mode.
some drivers do this sometimes. others always return EINVAL if there
isn't an exact match.
be more consistent. only return EINVAL if an absurd parameter was
requested, otherwise return a supported set of parameters, as close
as possible to what was requested.
with/ok ratchov@
Diffstat (limited to 'sys/dev/pci/azalia.c')
-rw-r--r-- | sys/dev/pci/azalia.c | 212 |
1 files changed, 134 insertions, 78 deletions
diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c index 02c8677e734..d4a52b4c4dd 100644 --- a/sys/dev/pci/azalia.c +++ b/sys/dev/pci/azalia.c @@ -1,4 +1,4 @@ -/* $OpenBSD: azalia.c,v 1.61 2008/10/23 02:06:53 jakemsr Exp $ */ +/* $OpenBSD: azalia.c,v 1.62 2008/10/25 22:30:43 jakemsr Exp $ */ /* $NetBSD: azalia.c,v 1.20 2006/05/07 08:31:44 kent Exp $ */ /*- @@ -277,6 +277,10 @@ int azalia_params2fmt(const audio_params_t *, uint16_t *); int azalia_create_encodings(struct audio_format *, int, struct audio_encoding_set **); +int azalia_match_format(codec_t *, audio_params_t *); +int azalia_set_params_sub(codec_t *, int, audio_params_t *); + + /* variables */ struct cfattach azalia_ca = { sizeof(azalia_t), azalia_pci_match, azalia_pci_attach, @@ -2209,99 +2213,151 @@ azalia_get_default_params(void *addr, int mode, struct audio_params *params) } int -azalia_set_params(void *v, int smode, int umode, audio_params_t *p, - audio_params_t *r) +azalia_match_format(codec_t *codec, audio_params_t *par) { - azalia_t *az; - codec_t *codec; - void (*pswcode)(void *, u_char *, int) = NULL; - void (*rswcode)(void *, u_char *, int) = NULL; + int i; + + for (i = 0; i < codec->nformats; i++) { + if (par->encoding != codec->formats[i].encoding) + continue; + if (par->precision != codec->formats[i].precision) + continue; + if (par->channels != codec->formats[i].channels) + continue; + break; + } + + return (i); +} + +int +azalia_set_params_sub(codec_t *codec, int mode, audio_params_t *par) +{ + void (*swcode)(void *, u_char *, int) = NULL; + char *cmode; int i, j; + uint ochan, oenc, opre; - az = v; - codec = &az->codecs[az->codecno]; - if (smode & AUMODE_RECORD && r != NULL) { - for (i = 0; i < codec->nformats; i++) { - if (r->encoding != codec->formats[i].encoding) - continue; - if (r->precision != codec->formats[i].precision) - continue; - if (r->channels != codec->formats[i].channels) - continue; - break; - } - /* find a 2 channel format and emulate mono */ - if (i == codec->nformats && r->channels == 1) { - r->factor = 2; - rswcode = linear16_decimator; - for (i = 0; i < codec->nformats; i++) { - if (r->encoding != codec->formats[i].encoding) - continue; - if (r->precision != codec->formats[i].precision) - continue; - if (codec->formats[i].channels != 2) - continue; - break; - } - } + if (mode == AUMODE_PLAY) + cmode = "play"; + else + cmode = "record"; - if (i == codec->nformats) { - DPRINTF(("%s: can't find record format %u/%u/%u\n", - __func__, r->encoding, r->precision, r->channels)); - return (EINVAL); - } - for (j = 0; j < codec->formats[i].frequency_type; j++) { - if (r->sample_rate != codec->formats[i].frequency[j]) - continue; - break; - } - if (j == codec->formats[i].frequency_type) { - DPRINTF(("%s: can't find record rate %u\n", - __func__, r->sample_rate)); - return (EINVAL); + ochan = par->channels; + oenc = par->encoding; + opre = par->precision; + + i = azalia_match_format(codec, par); + if (i == codec->nformats && par->channels == 1) { + /* find a 2 channel format and emulate mono */ + par->channels = 2; + i = azalia_match_format(codec, par); + if (i != codec->nformats) { + par->factor = 2; + if (mode == AUMODE_RECORD) + swcode = linear16_decimator; + else + swcode = noswap_bytes_mts; + par->channels = 1; } - r->sw_code = rswcode; } - if (smode & AUMODE_PLAY && p != NULL) { - for (i = 0; i < codec->nformats; i++) { - if (p->encoding != codec->formats[i].encoding) - continue; - if (p->precision != codec->formats[i].precision) - continue; - if (p->channels != codec->formats[i].channels) - continue; - break; - } + par->channels = ochan; + if (i == codec->nformats && (par->precision != 16 || par->encoding != + AUDIO_ENCODING_SLINEAR_LE)) { + /* try with default encoding/precision */ + par->encoding = AUDIO_ENCODING_SLINEAR_LE; + par->precision = 16; + i = azalia_match_format(codec, par); + } + if (i == codec->nformats && par->channels == 1) { /* find a 2 channel format and emulate mono */ - if (i == codec->nformats && p->channels == 1) { - p->factor = 2; - pswcode = noswap_bytes_mts; - for (i = 0; i < codec->nformats; i++) { - if (p->encoding != codec->formats[i].encoding) - continue; - if (p->precision != codec->formats[i].precision) - continue; - if (codec->formats[i].channels != 2) - continue; - break; - } + par->channels = 2; + i = azalia_match_format(codec, par); + if (i != codec->nformats) { + par->factor = 2; + if (mode == AUMODE_RECORD) + swcode = linear16_decimator; + else + swcode = noswap_bytes_mts; + par->channels = 1; } + } + par->channels = ochan; + if (i == codec->nformats && par->channels != 2) { + /* try with default channels */ + par->encoding = oenc; + par->precision = opre; + par->channels = 2; + i = azalia_match_format(codec, par); + } + /* try with default everything */ + if (i == codec->nformats) { + par->encoding = AUDIO_ENCODING_SLINEAR_LE; + par->precision = 16; + par->channels = 2; + i = azalia_match_format(codec, par); if (i == codec->nformats) { - DPRINTF(("%s: can't find playback format %u/%u/%u\n", - __func__, p->encoding, p->precision, p->channels)); - return (EINVAL); + DPRINTF(("%s: can't find %s format %u/%u/%u\n", + __func__, cmode, par->encoding, + par->precision, par->channels)); + return EINVAL; } + } + if (codec->formats[i].frequency_type == 0) { + DPRINTF(("%s: matched %s format %d has 0 frequencies\n", + __func__, cmode, i)); + return EINVAL; + } + + for (j = 0; j < codec->formats[i].frequency_type; j++) { + if (par->sample_rate != codec->formats[i].frequency[j]) + continue; + break; + } + if (j == codec->formats[i].frequency_type) { + /* try again with default */ + par->sample_rate = 48000; for (j = 0; j < codec->formats[i].frequency_type; j++) { - if (p->sample_rate != codec->formats[i].frequency[j]) + if (par->sample_rate != codec->formats[i].frequency[j]) continue; break; } if (j == codec->formats[i].frequency_type) { - DPRINTF(("%s: can't find playback rate %u\n", - __func__, p->sample_rate)); - return (EINVAL); + DPRINTF(("%s: can't find %s rate %u\n", + __func__, cmode, par->sample_rate)); + return EINVAL; } - p->sw_code = pswcode; + } + par->sw_code = swcode; + + return (0); +} + +int +azalia_set_params(void *v, int smode, int umode, audio_params_t *p, + audio_params_t *r) +{ + azalia_t *az; + codec_t *codec; + int ret; + + az = v; + codec = &az->codecs[az->codecno]; + if (codec->nformats == 0) { + DPRINTF(("%s: codec has no formats\n", __func__)); + return EINVAL; + } + + if (smode & AUMODE_RECORD && r != NULL) { + ret = azalia_set_params_sub(codec, AUMODE_RECORD, r); + if (ret) + return (ret); + } + + if (smode & AUMODE_PLAY && p != NULL) { + ret = azalia_set_params_sub(codec, AUMODE_PLAY, p); + if (ret) + return (ret); } return (0); |