From owner-svn-src-head@FreeBSD.ORG Thu Jan 19 01:55:49 2012 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 0E113106566C; Thu, 19 Jan 2012 01:55:49 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id E5B318FC14; Thu, 19 Jan 2012 01:55:48 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q0J1tmJ0066351; Thu, 19 Jan 2012 01:55:48 GMT (envelope-from mav@svn.freebsd.org) Received: (from mav@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q0J1tmNQ066343; Thu, 19 Jan 2012 01:55:48 GMT (envelope-from mav@svn.freebsd.org) Message-Id: <201201190155.q0J1tmNQ066343@svn.freebsd.org> From: Alexander Motin Date: Thu, 19 Jan 2012 01:55:48 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r230326 - head/sys/dev/sound/pci/hda X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 19 Jan 2012 01:55:49 -0000 Author: mav Date: Thu Jan 19 01:55:48 2012 New Revision: 230326 URL: http://svn.freebsd.org/changeset/base/230326 Log: Two 192/24/8 playback streams overflow single mandatory output line (SDO) of HDA bus. Handle that from two directions: - Add support for "striping" (using several SDO lines), if supported. - Account HDA bus utilization and return error on new stream allocation attempt if remaining bandwidth is unsifficient. Most of HDA controllers have one SDO line with 46Mbps output bandwidth. NVIDIA GF210 has 2 lines - 92Mbps. NVIDIA GF520 has 4 lines - 184Mbps! MFC after: 2 months Sponsored by: iXsystems, Inc. Modified: head/sys/dev/sound/pci/hda/hdaa.c head/sys/dev/sound/pci/hda/hdaa.h head/sys/dev/sound/pci/hda/hdac.c head/sys/dev/sound/pci/hda/hdac_if.m head/sys/dev/sound/pci/hda/hdac_private.h head/sys/dev/sound/pci/hda/hdacc.c Modified: head/sys/dev/sound/pci/hda/hdaa.c ============================================================================== --- head/sys/dev/sound/pci/hda/hdaa.c Thu Jan 19 01:25:50 2012 (r230325) +++ head/sys/dev/sound/pci/hda/hdaa.c Thu Jan 19 01:55:48 2012 (r230326) @@ -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->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 * 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 *dev 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"); Modified: head/sys/dev/sound/pci/hda/hdaa.h ============================================================================== --- head/sys/dev/sound/pci/hda/hdaa.h Thu Jan 19 01:25:50 2012 (r230325) +++ head/sys/dev/sound/pci/hda/hdaa.h Thu Jan 19 01:55:48 2012 (r230326) @@ -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) \ Modified: head/sys/dev/sound/pci/hda/hdac.c ============================================================================== --- head/sys/dev/sound/pci/hda/hdac.c Thu Jan 19 01:25:50 2012 (r230325) +++ head/sys/dev/sound/pci/hda/hdac.c Thu Jan 19 01:55:48 2012 (r230326) @@ -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, } 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 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 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 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); Modified: head/sys/dev/sound/pci/hda/hdac_if.m ============================================================================== --- head/sys/dev/sound/pci/hda/hdac_if.m Thu Jan 19 01:25:50 2012 (r230325) +++ head/sys/dev/sound/pci/hda/hdac_if.m Thu Jan 19 01:55:48 2012 (r230326) @@ -44,6 +44,7 @@ METHOD int stream_alloc { device_t child; int dir; int format; + int stripe; uint32_t **dmapos; }; Modified: head/sys/dev/sound/pci/hda/hdac_private.h ============================================================================== --- head/sys/dev/sound/pci/hda/hdac_private.h Thu Jan 19 01:25:50 2012 (r230325) +++ head/sys/dev/sound/pci/hda/hdac_private.h Thu Jan 19 01:55:48 2012 (r230326) @@ -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]; }; Modified: head/sys/dev/sound/pci/hda/hdacc.c ============================================================================== --- head/sys/dev/sound/pci/hda/hdacc.c Thu Jan 19 01:25:50 2012 (r230325) +++ head/sys/dev/sound/pci/hda/hdacc.c Thu Jan 19 01:55:48 2012 (r230326) @@ -503,13 +503,13 @@ hdacc_codec_command(device_t dev, device 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);