Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 19 Jan 2012 01:55:48 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r230326 - head/sys/dev/sound/pci/hda
Message-ID:  <201201190155.q0J1tmNQ066343@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201201190155.q0J1tmNQ066343>