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.
This commit is contained in:
mav 2012-01-19 01:55:48 +00:00
parent 142940515e
commit 06ec0382c9
6 changed files with 101 additions and 17 deletions

View File

@ -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");

View File

@ -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) \

View File

@ -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);

View File

@ -44,6 +44,7 @@ METHOD int stream_alloc {
device_t child;
int dir;
int format;
int stripe;
uint32_t **dmapos;
};

View File

@ -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];
};

View File

@ -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);