diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c index 20720ad4323e..1c3f5b93aa62 100644 --- a/sys/dev/sound/pci/hda/hdaa.c +++ b/sys/dev/sound/pci/hda/hdaa.c @@ -1096,6 +1096,11 @@ hdaa_widget_parse(struct hdaa_widget *w) w->param.supp_pcm_size_rate = w->devinfo->supp_pcm_size_rate; } + if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) { + w->wclass.conv.stripecap = hda_command(dev, + HDA_CMD_GET_STRIPE_CONTROL(0, w->nid)) >> 20; + } else + w->wclass.conv.stripecap = 1; } else { w->param.supp_stream_formats = 0; w->param.supp_pcm_size_rate = 0; @@ -1388,6 +1393,18 @@ hdaa_stream_format(struct hdaa_chan *ch) return (fmt); } +static int +hdaa_allowed_stripes(uint16_t fmt) +{ + static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; + int size; + + size = bits[(fmt >> 4) & 0x03]; + size *= (fmt & 0x0f) + 1; + size *= ((fmt >> 11) & 0x07) + 1; + return (0xffffffffU >> (32 - fls(size / 8))); +} + static void hdaa_audio_setup(struct hdaa_chan *ch) { @@ -1462,6 +1479,10 @@ hdaa_audio_setup(struct hdaa_chan *ch) } hda_command(ch->devinfo->dev, HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], c)); + if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) { + hda_command(ch->devinfo->dev, + HDA_CMD_SET_STRIPE_CONTROL(0, w->nid, ch->stripectl)); + } cchn = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); if (cchn > 1 && chn < totalchn) { cchn = min(cchn, totalchn - chn - 1); @@ -1472,9 +1493,9 @@ hdaa_audio_setup(struct hdaa_chan *ch) device_printf(ch->pdevinfo->dev, "PCMDIR_%s: Stream setup nid=%d: " "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x, " - "chan_count=0x%02x\n", + "chan_count=0x%02x, stripe=%d\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->io[i], fmt, dfmt, c, cchn); + ch->io[i], fmt, dfmt, c, cchn, ch->stripectl); ); for (j = 0; j < 16; j++) { if (as->dacs[ch->asindex][j] != ch->io[i]) @@ -1658,11 +1679,12 @@ static int hdaa_channel_start(struct hdaa_chan *ch) { struct hdaa_devinfo *devinfo = ch->devinfo; + uint32_t fmt; - ch->ptr = 0; - ch->prevptr = 0; + fmt = hdaa_stream_format(ch); + ch->stripectl = fls(ch->stripecap & hdaa_allowed_stripes(fmt)) - 1; ch->sid = HDAC_STREAM_ALLOC(device_get_parent(devinfo->dev), devinfo->dev, - ch->dir == PCMDIR_PLAY ? 1 : 0, hdaa_stream_format(ch), &ch->dmapos); + ch->dir == PCMDIR_PLAY ? 1 : 0, fmt, ch->stripectl, &ch->dmapos); if (ch->sid <= 0) return (EBUSY); hdaa_audio_setup(ch); @@ -4464,6 +4486,7 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) ch->bit32 = 0; ch->pcmrates[0] = 48000; ch->pcmrates[1] = 0; + ch->stripecap = 0xff; ret = 0; channels = 0; @@ -4506,6 +4529,7 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) pcmcap &= w->param.supp_pcm_size_rate; } ch->io[ret++] = as[ch->as].dacs[ch->asindex][i]; + ch->stripecap &= w->wclass.conv.stripecap; /* Do not count redirection pin/dac channels. */ if (i == 15 && as[ch->as].hpredir >= 0) continue; @@ -5001,7 +5025,8 @@ hdaa_dump_nodes(struct hdaa_devinfo *devinfo) if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) printf(" PROC"); if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) - printf(" STRIPE"); + printf(" STRIPE(x%d)", + 1 << (fls(w->wclass.conv.stripecap) - 1)); j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); if (j == 1) printf(" STEREO"); diff --git a/sys/dev/sound/pci/hda/hdaa.h b/sys/dev/sound/pci/hda/hdaa.h index 7050ff82db68..3642d450624b 100644 --- a/sys/dev/sound/pci/hda/hdaa.h +++ b/sys/dev/sound/pci/hda/hdaa.h @@ -116,6 +116,9 @@ struct hdaa_widget { uint32_t cap; uint32_t ctrl; } pin; + struct { + uint8_t stripecap; + } conv; } wclass; }; @@ -201,7 +204,7 @@ struct hdaa_chan { struct hdaa_pcm_devinfo *pdevinfo; uint32_t spd, fmt, fmtlist[32], pcmrates[16]; uint32_t supp_stream_formats, supp_pcm_size_rate; - uint32_t ptr, prevptr, blkcnt, blksz; + uint32_t blkcnt, blksz; uint32_t *dmapos; uint32_t flags; int dir; @@ -212,6 +215,8 @@ struct hdaa_chan { int as; /* Number of association. */ int asindex; /* Index within association. */ nid_t io[16]; + uint8_t stripecap; /* AND of stripecap of all ios. */ + uint8_t stripectl; /* stripe to use to all ios. */ }; #define hdaa_codec_id(devinfo) \ diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index 9a8dcee08afd..402070dab488 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -1339,10 +1339,10 @@ sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) } static int -hdac_data_rate(uint16_t fmt) +hdac_mdata_rate(uint16_t fmt) { - static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; - int rate; + static const int mbits[8] = { 8, 16, 32, 32, 32, 32, 32, 32 }; + int rate, bits; if (fmt & (1 << 14)) rate = 44100; @@ -1350,8 +1350,24 @@ hdac_data_rate(uint16_t fmt) rate = 48000; rate *= ((fmt >> 11) & 0x07) + 1; rate /= ((fmt >> 8) & 0x07) + 1; - rate *= ((bits[(fmt >> 4) & 0x03]) * ((fmt & 0x0f) + 1) + 7) / 8; - return (rate); + bits = mbits[(fmt >> 4) & 0x03]; + bits *= (fmt & 0x0f) + 1; + return (rate * bits); +} + +static int +hdac_bdata_rate(uint16_t fmt, int output) +{ + static const int bbits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; + int rate, bits; + + rate = 48000; + rate *= ((fmt >> 11) & 0x07) + 1; + bits = bbits[(fmt >> 4) & 0x03]; + bits *= (fmt & 0x0f) + 1; + if (!output) + bits = ((bits + 7) & ~0x07) + 10; + return (rate * bits); } static void @@ -1369,7 +1385,7 @@ hdac_poll_reinit(struct hdac_softc *sc) if (s->running == 0) continue; pollticks = ((uint64_t)hz * s->blksz) / - hdac_data_rate(s->format); + (hdac_mdata_rate(s->format) / 8); pollticks >>= 1; if (pollticks > hz) pollticks = hz; @@ -1790,11 +1806,12 @@ hdac_find_stream(struct hdac_softc *sc, int dir, int stream) } static int -hdac_stream_alloc(device_t dev, device_t child, int dir, int format, +hdac_stream_alloc(device_t dev, device_t child, int dir, int format, int stripe, uint32_t **dmapos) { struct hdac_softc *sc = device_get_softc(dev); - int stream, ss; + nid_t cad = (uintptr_t)device_get_ivars(child); + int stream, ss, bw, maxbw, prevbw; /* Look for empty stream. */ ss = hdac_find_stream(sc, dir, 0); @@ -1803,6 +1820,28 @@ hdac_stream_alloc(device_t dev, device_t child, int dir, int format, if (ss < 0) return (0); + /* Check bus bandwidth. */ + bw = hdac_bdata_rate(format, dir); + if (dir == 1) { + bw *= 1 << (sc->num_sdo - stripe); + prevbw = sc->sdo_bw_used; + maxbw = 48000 * 960 * (1 << sc->num_sdo); + } else { + prevbw = sc->codecs[cad].sdi_bw_used; + maxbw = 48000 * 464; + } + HDA_BOOTHVERBOSE( + device_printf(dev, "%dKbps of %dKbps bandwidth used%s\n", + (bw + prevbw) / 1000, maxbw / 1000, + bw + prevbw > maxbw ? " -- OVERFLOW!" : ""); + ); + if (bw + prevbw > maxbw) + return (0); + if (dir == 1) + sc->sdo_bw_used += bw; + else + sc->codecs[cad].sdi_bw_used += bw; + /* Allocate stream number */ if (ss >= sc->num_iss + sc->num_oss) stream = 15 - (ss - sc->num_iss + sc->num_oss); @@ -1814,7 +1853,9 @@ hdac_stream_alloc(device_t dev, device_t child, int dir, int format, sc->streams[ss].dev = child; sc->streams[ss].dir = dir; sc->streams[ss].stream = stream; + sc->streams[ss].bw = bw; sc->streams[ss].format = format; + sc->streams[ss].stripe = stripe; if (dmapos != NULL) { if (sc->pos_dma.dma_vaddr != NULL) *dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + ss * 8); @@ -1828,11 +1869,16 @@ static void hdac_stream_free(device_t dev, device_t child, int dir, int stream) { struct hdac_softc *sc = device_get_softc(dev); + nid_t cad = (uintptr_t)device_get_ivars(child); int ss; ss = hdac_find_stream(sc, dir, stream); KASSERT(ss >= 0, ("Free for not allocated stream (%d/%d)\n", dir, stream)); + if (dir == 1) + sc->sdo_bw_used -= sc->streams[ss].bw; + else + sc->codecs[cad].sdi_bw_used -= sc->streams[ss].bw; sc->streams[ss].stream = 0; sc->streams[ss].dev = NULL; } @@ -1875,6 +1921,8 @@ hdac_stream_start(device_t dev, device_t child, ctl &= ~HDAC_SDCTL2_DIR; ctl &= ~HDAC_SDCTL2_STRM_MASK; ctl |= stream << HDAC_SDCTL2_STRM_SHIFT; + ctl &= ~HDAC_SDCTL2_STRIPE_MASK; + ctl |= sc->streams[ss].stripe << HDAC_SDCTL2_STRIPE_SHIFT; HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL2, ctl); HDAC_WRITE_2(&sc->mem, off + HDAC_SDFMT, sc->streams[ss].format); diff --git a/sys/dev/sound/pci/hda/hdac_if.m b/sys/dev/sound/pci/hda/hdac_if.m index 4fb77bd5bd12..f87e2edffdea 100644 --- a/sys/dev/sound/pci/hda/hdac_if.m +++ b/sys/dev/sound/pci/hda/hdac_if.m @@ -44,6 +44,7 @@ METHOD int stream_alloc { device_t child; int dir; int format; + int stripe; uint32_t **dmapos; }; diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h index 8532f83d9f7c..b8798d7e3b37 100644 --- a/sys/dev/sound/pci/hda/hdac_private.h +++ b/sys/dev/sound/pci/hda/hdac_private.h @@ -155,6 +155,8 @@ struct hdac_stream { int stream; int blksz; int running; + int bw; + int stripe; uint16_t format; }; @@ -206,6 +208,8 @@ struct hdac_softc { int unsolq_st; uint32_t unsolq[HDAC_UNSOLQ_MAX]; + int sdo_bw_used; + struct hdac_stream *streams; struct { @@ -216,6 +220,7 @@ struct hdac_softc { uint8_t stepping_id; int pending; uint32_t response; + int sdi_bw_used; } codecs[HDAC_CODEC_MAX]; }; diff --git a/sys/dev/sound/pci/hda/hdacc.c b/sys/dev/sound/pci/hda/hdacc.c index 47feb1b5803b..3336d4dd554b 100644 --- a/sys/dev/sound/pci/hda/hdacc.c +++ b/sys/dev/sound/pci/hda/hdacc.c @@ -503,13 +503,13 @@ hdacc_codec_command(device_t dev, device_t child, uint32_t verb) static int hdacc_stream_alloc(device_t dev, device_t child, int dir, int format, - uint32_t **dmapos) + int stripe, uint32_t **dmapos) { struct hdacc_softc *codec = device_get_softc(dev); int stream; stream = HDAC_STREAM_ALLOC(device_get_parent(dev), dev, - dir, format, dmapos); + dir, format, stripe, dmapos); if (stream > 0) codec->streams[dir][stream] = child; return (stream);