Module Name: src
Committed By: jmcneill
Date: Mon Sep 7 16:21:08 UTC 2009
Modified Files:
src/sys/dev/pci/hdaudio: hdaudio.c hdaudio_afg.c hdaudiovar.h
Log Message:
Add support for formats other than 16bit/48kHz.
To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/pci/hdaudio/hdaudio.c
cvs rdiff -u -r1.5 -r1.6 src/sys/dev/pci/hdaudio/hdaudio_afg.c
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/pci/hdaudio/hdaudiovar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/hdaudio/hdaudio.c
diff -u src/sys/dev/pci/hdaudio/hdaudio.c:1.2 src/sys/dev/pci/hdaudio/hdaudio.c:1.3
--- src/sys/dev/pci/hdaudio/hdaudio.c:1.2 Sun Sep 6 17:33:53 2009
+++ src/sys/dev/pci/hdaudio/hdaudio.c Mon Sep 7 16:21:08 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: hdaudio.c,v 1.2 2009/09/06 17:33:53 sborrill Exp $ */
+/* $NetBSD: hdaudio.c,v 1.3 2009/09/07 16:21:08 jmcneill Exp $ */
/*
* Copyright (c) 2009 Precedence Technologies Ltd <[email protected]>
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hdaudio.c,v 1.2 2009/09/06 17:33:53 sborrill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hdaudio.c,v 1.3 2009/09/07 16:21:08 jmcneill Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -948,6 +948,77 @@
mutex_exit(&sc->sc_stream_mtx);
}
+/*
+ * Convert most of audio_params_t to stream fmt descriptor; noticably missing
+ * is the # channels bits, as this is encoded differently in codec and
+ * stream descriptors.
+ *
+ * TODO: validate that the stream and selected codecs can handle the fmt
+ */
+uint16_t
+hdaudio_stream_param(struct hdaudio_stream *st, const audio_params_t *param)
+{
+ uint16_t fmt = 0;
+
+ switch (param->sample_rate) {
+ case 8000:
+ fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
+ HDAUDIO_FMT_DIV(6);
+ break;
+ case 11025:
+ fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1) |
+ HDAUDIO_FMT_DIV(4);
+ break;
+ case 16000:
+ fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
+ HDAUDIO_FMT_DIV(3);
+ break;
+ case 22050:
+ fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1) |
+ HDAUDIO_FMT_DIV(2);
+ break;
+ case 32000:
+ fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(2) |
+ HDAUDIO_FMT_DIV(3);
+ break;
+ case 44100:
+ fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1);
+ break;
+ case 48000:
+ fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1);
+ break;
+ case 88200:
+ fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(2);
+ break;
+ case 96000:
+ fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(2);
+ break;
+ case 176400:
+ fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(4);
+ break;
+ case 192000:
+ fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(4);
+ break;
+ default:
+ return 0;
+ }
+
+ if (param->precision == 16 && param->validbits == 8)
+ fmt |= HDAUDIO_FMT_BITS_8_16;
+ else if (param->precision == 16 && param->validbits == 16)
+ fmt |= HDAUDIO_FMT_BITS_16_16;
+ else if (param->precision == 32 && param->validbits == 20)
+ fmt |= HDAUDIO_FMT_BITS_20_32;
+ else if (param->precision == 32 && param->validbits == 24)
+ fmt |= HDAUDIO_FMT_BITS_24_32;
+ else if (param->precision == 32 && param->validbits == 32)
+ fmt |= HDAUDIO_FMT_BITS_32_32;
+ else
+ return 0;
+
+ return fmt;
+}
+
void
hdaudio_stream_reset(struct hdaudio_stream *st)
{
@@ -996,6 +1067,7 @@
struct hdaudio_bdl_entry *bdl;
uint64_t dmaaddr;
uint32_t intctl;
+ uint16_t fmt;
uint8_t ctl0, ctl2;
int cnt, snum = st->st_shift;
@@ -1050,15 +1122,10 @@
/*
* Program stream format
- *
- * XXX 48kHz, 16-bit, stereo for now
*/
- hda_write2(sc, HDAUDIO_SD_FMT(snum),
- HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
- HDAUDIO_FMT_DIV(1) | HDAUDIO_FMT_BITS_16_16 |
- HDAUDIO_FMT_CHAN(params->channels));
-
- /* XXX program codecs for stream number */
+ fmt = hdaudio_stream_param(st, params) |
+ HDAUDIO_FMT_CHAN(params->channels);
+ hda_write2(sc, HDAUDIO_SD_FMT(snum), fmt);
/*
* Switch on interrupts for this stream
Index: src/sys/dev/pci/hdaudio/hdaudio_afg.c
diff -u src/sys/dev/pci/hdaudio/hdaudio_afg.c:1.5 src/sys/dev/pci/hdaudio/hdaudio_afg.c:1.6
--- src/sys/dev/pci/hdaudio/hdaudio_afg.c:1.5 Mon Sep 7 11:59:53 2009
+++ src/sys/dev/pci/hdaudio/hdaudio_afg.c Mon Sep 7 16:21:08 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: hdaudio_afg.c,v 1.5 2009/09/07 11:59:53 jmcneill Exp $ */
+/* $NetBSD: hdaudio_afg.c,v 1.6 2009/09/07 16:21:08 jmcneill Exp $ */
/*
* Copyright (c) 2009 Precedence Technologies Ltd <[email protected]>
@@ -60,7 +60,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hdaudio_afg.c,v 1.5 2009/09/07 11:59:53 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hdaudio_afg.c,v 1.6 2009/09/07 16:21:08 jmcneill Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -151,7 +151,7 @@
"Other"
};
-#define HDAUDIO_MAXFORMATS 10
+#define HDAUDIO_MAXFORMATS 24
#define HDAUDIO_MAXCONNECTIONS 32
#define HDAUDIO_MAXPINS 16
#define HDAUDIO_PARSE_MAXDEPTH 10
@@ -285,7 +285,7 @@
struct hdaudio_mixer *sc_mixers;
int sc_pchan, sc_rchan;
- int sc_curpchan, sc_currchan;
+ audio_params_t sc_pparam, sc_rparam;
struct callout sc_jack_callout;
@@ -378,12 +378,12 @@
hdaudio_afg_append_formats(struct hdaudio_audiodev *ad,
const struct audio_format *format)
{
- struct hdaudio_afg_softc *sc = ad->ad_sc;
- if (ad->ad_nformats >= HDAUDIO_MAXFORMATS) {
- hda_error(sc, "too many formats\n");
- return EINVAL;
+ if (ad->ad_nformats + 1 >= HDAUDIO_MAXFORMATS) {
+ hda_print1(ad->ad_sc, "[ENOMEM] ");
+ return ENOMEM;
}
ad->ad_formats[ad->ad_nformats++] = *format;
+
return 0;
}
@@ -2609,32 +2609,37 @@
int tag, chn, maxchan, c;
int i, j;
- fmt = HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
- HDAUDIO_FMT_DIV(1) | HDAUDIO_FMT_BITS_16_16;
+ KASSERT(mode == AUMODE_PLAY || mode == AUMODE_RECORD);
+
dfmt = COP_DIGITAL_CONVCTRL1_DIGEN; /* TODO: AC3 */
+ if (mode == AUMODE_PLAY)
+ fmt = hdaudio_stream_param(sc->sc_audiodev.ad_playback,
+ &sc->sc_pparam);
+ else
+ fmt = hdaudio_stream_param(sc->sc_audiodev.ad_capture,
+ &sc->sc_rparam);
+
for (i = 0; i < sc->sc_nassocs; i++) {
if (as[i].as_enable == false)
continue;
- if ((mode & (AUMODE_PLAY|AUMODE_RECORD)) == AUMODE_PLAY &&
- as[i].as_dir != HDAUDIO_PINDIR_OUT)
+ if (mode == AUMODE_PLAY && as[i].as_dir != HDAUDIO_PINDIR_OUT)
continue;
- if ((mode & (AUMODE_PLAY|AUMODE_RECORD)) == AUMODE_RECORD &&
- as[i].as_dir != HDAUDIO_PINDIR_IN)
+ if (mode == AUMODE_RECORD && as[i].as_dir != HDAUDIO_PINDIR_IN)
continue;
fmt &= ~HDAUDIO_FMT_CHAN_MASK;
if (as[i].as_dir == HDAUDIO_PINDIR_OUT &&
sc->sc_audiodev.ad_playback != NULL) {
tag = hdaudio_stream_tag(sc->sc_audiodev.ad_playback);
- fmt |= HDAUDIO_FMT_CHAN(sc->sc_curpchan);
- maxchan = sc->sc_curpchan;
+ fmt |= HDAUDIO_FMT_CHAN(sc->sc_pparam.channels);
+ maxchan = sc->sc_pparam.channels;
} else if (as[i].as_dir == HDAUDIO_PINDIR_IN &&
sc->sc_audiodev.ad_capture != NULL) {
tag = hdaudio_stream_tag(sc->sc_audiodev.ad_capture);
- fmt |= HDAUDIO_FMT_CHAN(sc->sc_currchan);
- maxchan = sc->sc_currchan;
+ fmt |= HDAUDIO_FMT_CHAN(sc->sc_rparam.channels);
+ maxchan = sc->sc_rparam.channels;
} else {
tag = 0;
if (as[i].as_dir == HDAUDIO_PINDIR_OUT) {
@@ -2732,40 +2737,85 @@
return nchans;
}
-static void
-hdaudio_afg_configure_encodings(struct hdaudio_afg_softc *sc)
+static bool
+hdaudio_afg_rate_supported(struct hdaudio_afg_softc *sc, u_int frequency)
{
- struct hdaudio_assoc *as = sc->sc_assocs;
- struct audio_format f;
- int nchan, i;
+ uint32_t caps = sc->sc_p.pcm_size_rate;
- sc->sc_pchan = sc->sc_rchan = 0;
-
- for (nchan = 0, i = 0; i < sc->sc_nassocs; i++) {
- nchan = hdaudio_afg_assoc_count_channels(sc, &as[i],
- HDAUDIO_PINDIR_OUT);
- if (nchan > sc->sc_pchan)
- sc->sc_pchan = nchan;
+#define ISFREQOK(shift) ((caps & (1 << (shift))) ? true : false)
+ switch (frequency) {
+ case 8000:
+ return ISFREQOK(0);
+ case 11025:
+ return ISFREQOK(1);
+ case 16000:
+ return ISFREQOK(2);
+ case 22050:
+ return ISFREQOK(3);
+ case 32000:
+ return ISFREQOK(4);
+ case 44100:
+ return ISFREQOK(5);
+ case 48000:
+ return true; /* Must be supported by all codecs */
+ case 88200:
+ return ISFREQOK(7);
+ case 96000:
+ return ISFREQOK(8);
+ case 176400:
+ return ISFREQOK(9);
+ case 192000:
+ return ISFREQOK(10);
+ case 384000:
+ return ISFREQOK(11);
+ default:
+ return false;
}
- for (nchan = 0, i = 0; i < sc->sc_nassocs; i++) {
- nchan = hdaudio_afg_assoc_count_channels(sc, &as[i],
- HDAUDIO_PINDIR_IN);
- if (nchan > sc->sc_rchan)
- sc->sc_rchan = nchan;
+#undef ISFREQOK
+}
+
+static bool
+hdaudio_afg_bits_supported(struct hdaudio_afg_softc *sc, u_int bits)
+{
+ uint32_t caps = sc->sc_p.pcm_size_rate;
+#define ISBITSOK(shift) ((caps & (1 << (shift))) ? true : false)
+ switch (bits) {
+ case 8:
+ return ISBITSOK(16);
+ case 16:
+ return ISBITSOK(17);
+ case 20:
+ return ISBITSOK(18);
+ case 24:
+ return ISBITSOK(19);
+ case 32:
+ return ISBITSOK(20);
+ default:
+ return false;
}
+#undef ISBITSOK
+}
+
+static bool
+hdaudio_afg_probe_encoding(struct hdaudio_afg_softc *sc,
+ u_int minrate, u_int maxrate, u_int validbits, u_int precision)
+{
+ struct audio_format f;
- sc->sc_curpchan = sc->sc_pchan;
- sc->sc_currchan = sc->sc_rchan;
+ if (hdaudio_afg_bits_supported(sc, validbits) == false)
+ return false;
memset(&f, 0, sizeof(f));
f.driver_data = NULL;
f.mode = 0;
f.encoding = AUDIO_ENCODING_SLINEAR_LE;
- f.validbits = f.precision = 16; /* XXX */
+ f.validbits = validbits;
+ f.precision = precision;
f.channels = 0;
f.channel_mask = 0;
f.frequency_type = 0;
- f.frequency[0] = f.frequency[1] = 48000; /* XXX */
+ f.frequency[0] = minrate;
+ f.frequency[1] = maxrate;
#define HDAUDIO_INITFMT(ch, chmask) \
do { \
@@ -2789,8 +2839,66 @@
HDAUDIO_INITFMT(6, AUFMT_DOLBY_5_1);
HDAUDIO_INITFMT(8, AUFMT_SURROUND_7_1);
- hda_print(sc, "%d playback channels, %d capture channels\n",
- sc->sc_pchan, sc->sc_rchan);
+#undef HDAUDIO_INITFMT
+
+ return true;
+}
+
+
+static void
+hdaudio_afg_configure_encodings(struct hdaudio_afg_softc *sc)
+{
+ const u_int possible_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100,
+ 48000, 88200, 96000, 176500, 192000, /* 384000, */
+ };
+ struct hdaudio_assoc *as = sc->sc_assocs;
+ struct audio_format f;
+ u_int minrate, maxrate;
+ int nchan, i;
+
+ sc->sc_pchan = sc->sc_rchan = 0;
+ minrate = maxrate = 0;
+
+ for (nchan = 0, i = 0; i < sc->sc_nassocs; i++) {
+ nchan = hdaudio_afg_assoc_count_channels(sc, &as[i],
+ HDAUDIO_PINDIR_OUT);
+ if (nchan > sc->sc_pchan)
+ sc->sc_pchan = nchan;
+ }
+ for (nchan = 0, i = 0; i < sc->sc_nassocs; i++) {
+ nchan = hdaudio_afg_assoc_count_channels(sc, &as[i],
+ HDAUDIO_PINDIR_IN);
+ if (nchan > sc->sc_rchan)
+ sc->sc_rchan = nchan;
+ }
+ hda_print(sc, "%dch/%dch", sc->sc_pchan, sc->sc_rchan);
+
+ for (i = 0; __arraycount(possible_rates); i++)
+ if (hdaudio_afg_rate_supported(sc, possible_rates[i])) {
+ minrate = possible_rates[i];
+ break;
+ }
+ for (i = __arraycount(possible_rates) - 1; i >= 0; i--)
+ if (hdaudio_afg_rate_supported(sc, possible_rates[i])) {
+ maxrate = possible_rates[i];
+ break;
+ }
+ KASSERT(minrate > 0 && maxrate > 0); /* impossible */
+ hda_print1(sc, " %uHz", minrate);
+ if (minrate != maxrate)
+ hda_print1(sc, "-%uHz", maxrate);
+
+ if (hdaudio_afg_probe_encoding(sc, minrate, maxrate, 8, 16))
+ hda_print1(sc, " 8/16");
+ if (hdaudio_afg_probe_encoding(sc, minrate, maxrate, 16, 16))
+ hda_print1(sc, " 16/16");
+ if (hdaudio_afg_probe_encoding(sc, minrate, maxrate, 20, 32))
+ hda_print1(sc, " 20/32");
+ if (hdaudio_afg_probe_encoding(sc, minrate, maxrate, 24, 32))
+ hda_print1(sc, " 24/32");
+ if (hdaudio_afg_probe_encoding(sc, minrate, maxrate, 32, 32))
+ hda_print1(sc, " 32/32");
/*
* XXX JDM 20090614
@@ -2806,8 +2914,7 @@
hdaudio_afg_append_formats(&sc->sc_audiodev, &f);
}
-
-#undef HDAUDIO_INITFMT
+ hda_print1(sc, "\n");
}
static void
@@ -2957,6 +3064,7 @@
hdaudio_afg_attach(device_t parent, device_t self, void *opaque)
{
struct hdaudio_afg_softc *sc = device_private(self);
+ audio_params_t defparams;
prop_dictionary_t args = opaque;
uint64_t fgptr = 0;
uint8_t nid = 0;
@@ -3058,7 +3166,13 @@
HDAUDIO_STREAM_OSS, hdaudio_afg_stream_intr, &sc->sc_audiodev);
hda_debug(sc, "connecting streams\n");
- hdaudio_afg_stream_connect(sc, AUMODE_PLAY | AUMODE_RECORD);
+ defparams.channels = 2;
+ defparams.sample_rate = 48000;
+ defparams.precision = defparams.validbits = 16;
+ defparams.encoding = AUDIO_ENCODING_SLINEAR_LE;
+ sc->sc_pparam = sc->sc_rparam = defparams;
+ hdaudio_afg_stream_connect(sc, AUMODE_PLAY);
+ hdaudio_afg_stream_connect(sc, AUMODE_RECORD);
hda_debug(sc, "attaching audio device\n");
sc->sc_audiodev.ad_audiodev = audio_attach_mi(&hdaudio_afg_hw_if,
@@ -3125,7 +3239,8 @@
hda_delay(1000);
hdaudio_afg_commit(sc);
- hdaudio_afg_stream_connect(sc, AUMODE_PLAY | AUMODE_RECORD);
+ hdaudio_afg_stream_connect(sc, AUMODE_PLAY);
+ hdaudio_afg_stream_connect(sc, AUMODE_RECORD);
return true;
}
@@ -3150,12 +3265,16 @@
AUMODE_PLAY, play, TRUE, pfil);
if (index < 0)
return EINVAL;
+ ad->ad_sc->sc_pparam = *play;
+ hdaudio_afg_stream_connect(ad->ad_sc, AUMODE_PLAY);
}
if (rec && (setmode & AUMODE_RECORD)) {
index = auconv_set_converter(ad->ad_formats, ad->ad_nformats,
AUMODE_RECORD, rec, TRUE, rfil);
if (index < 0)
return EINVAL;
+ ad->ad_sc->sc_rparam = *rec;
+ hdaudio_afg_stream_connect(ad->ad_sc, AUMODE_RECORD);
}
return 0;
}
@@ -3419,7 +3538,7 @@
ad->ad_playbackintrarg = intrarg;
dmasize = (char *)end - (char *)start;
- ad->ad_sc->sc_curpchan = param->channels;
+ ad->ad_sc->sc_pparam = *param;
hdaudio_afg_stream_connect(ad->ad_sc, AUMODE_PLAY);
hdaudio_stream_start(ad->ad_playback, blksize, dmasize, param);
@@ -3442,7 +3561,7 @@
ad->ad_captureintrarg = intrarg;
dmasize = (char *)end - (char *)start;
- ad->ad_sc->sc_currchan = param->channels;
+ ad->ad_sc->sc_rparam = *param;
hdaudio_afg_stream_connect(ad->ad_sc, AUMODE_RECORD);
hdaudio_stream_start(ad->ad_capture, blksize, dmasize, param);
Index: src/sys/dev/pci/hdaudio/hdaudiovar.h
diff -u src/sys/dev/pci/hdaudio/hdaudiovar.h:1.3 src/sys/dev/pci/hdaudio/hdaudiovar.h:1.4
--- src/sys/dev/pci/hdaudio/hdaudiovar.h:1.3 Sun Sep 6 21:38:17 2009
+++ src/sys/dev/pci/hdaudio/hdaudiovar.h Mon Sep 7 16:21:08 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: hdaudiovar.h,v 1.3 2009/09/06 21:38:17 rmind Exp $ */
+/* $NetBSD: hdaudiovar.h,v 1.4 2009/09/07 16:21:08 jmcneill Exp $ */
/*
* Copyright (c) 2009 Precedence Technologies Ltd <[email protected]>
@@ -176,5 +176,6 @@
void hdaudio_stream_stop(struct hdaudio_stream *);
void hdaudio_stream_reset(struct hdaudio_stream *);
int hdaudio_stream_tag(struct hdaudio_stream *);
+uint16_t hdaudio_stream_param(struct hdaudio_stream *, const audio_params_t *);
#endif /* !_HDAUDIOVAR_H */