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:
parent
142940515e
commit
06ec0382c9
@ -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");
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -44,6 +44,7 @@ METHOD int stream_alloc {
|
||||
device_t child;
|
||||
int dir;
|
||||
int format;
|
||||
int stripe;
|
||||
uint32_t **dmapos;
|
||||
};
|
||||
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user