The JZ4780 I2S can feed either the internal audio codec or the HDMI

transmitter, but not both at the same time. This patch:

 - Adds a dev.pcm.0.internal_codec sysctl node for selecting between
   internal and external codec
 - Changes playback sample rate from 96 kHz to 48 kHz for HDMI compatibility
 - Enables i2s clock on codec access

Reviewed by:		br
Differential Revision:	https://reviews.freebsd.org/D8960
This commit is contained in:
Jared McNeill 2016-12-29 14:00:10 +00:00
parent 1357722508
commit 7fd348080f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=310776
3 changed files with 80 additions and 13 deletions

View File

@ -81,6 +81,7 @@ struct aic_softc {
void *ih;
struct xdma_channel *xchan;
xdma_controller_t *xdma_tx;
int internal_codec;
};
/* Channel registers */
@ -120,7 +121,7 @@ struct aic_rate {
};
static struct aic_rate rate_map[] = {
{ 96000 },
{ 48000 },
/* TODO: add more frequences */
{ 0 },
};
@ -355,7 +356,6 @@ aic_start(struct sc_pcminfo *scp)
int reg;
sc = scp->sc;
sc->pos = 0;
/* Ensure clock enabled. */
reg = READ4(sc, I2SCR);
@ -387,10 +387,6 @@ aic_stop(struct sc_pcminfo *scp)
xdma_terminate(sc->xchan);
sc->pos = 0;
bzero(sc->buf_base, sc->dma_size);
return (0);
}
@ -411,6 +407,8 @@ aicchan_trigger(kobj_t obj, void *data, int go)
case PCMTRIG_START:
ch->run = 1;
sc->pos = 0;
aic_start(scp);
break;
@ -421,6 +419,10 @@ aicchan_trigger(kobj_t obj, void *data, int go)
aic_stop(scp);
sc->pos = 0;
bzero(sc->buf_base, sc->dma_size);
break;
}
@ -448,7 +450,7 @@ static uint32_t aic_pfmt[] = {
0
};
static struct pcmchan_caps aic_pcaps = {96000, 96000, aic_pfmt, 0};
static struct pcmchan_caps aic_pcaps = {48000, 48000, aic_pfmt, 0};
static struct pcmchan_caps *
aicchan_getcaps(kobj_t obj, void *data)
@ -583,16 +585,13 @@ aic_configure_clocks(struct aic_softc *sc)
static int
aic_configure(struct aic_softc *sc)
{
int internal_codec;
int reg;
internal_codec = 1;
WRITE4(sc, AICFR, AICFR_RST);
/* Configure AIC */
reg = 0;
if (internal_codec) {
if (sc->internal_codec) {
reg |= (AICFR_ICDC);
} else {
reg |= (AICFR_SYNCD | AICFR_BCKD);
@ -609,6 +608,48 @@ aic_configure(struct aic_softc *sc)
return (0);
}
static int
sysctl_hw_pcm_internal_codec(SYSCTL_HANDLER_ARGS)
{
struct sc_pcminfo *scp;
struct sc_chinfo *ch;
struct aic_softc *sc;
int error, val;
if (arg1 == NULL)
return (EINVAL);
scp = arg1;
sc = scp->sc;
ch = &scp->chan[0];
snd_mtxlock(sc->lock);
val = sc->internal_codec;
error = sysctl_handle_int(oidp, &val, 0, req);
if (error || req->newptr == NULL) {
snd_mtxunlock(sc->lock);
return (error);
}
if (val < 0 || val > 1) {
snd_mtxunlock(sc->lock);
return (EINVAL);
}
if (sc->internal_codec != val) {
sc->internal_codec = val;
if (ch->run)
aic_stop(scp);
aic_configure(sc);
if (ch->run)
aic_start(scp);
}
snd_mtxunlock(sc->lock);
return (0);
}
static int
aic_probe(device_t dev)
{
@ -635,6 +676,7 @@ aic_attach(device_t dev)
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
sc->dev = dev;
sc->pos = 0;
sc->internal_codec = 1;
/* Get xDMA controller */
sc->xdma_tx = xdma_ofw_get(sc->dev, "tx");
@ -718,6 +760,13 @@ aic_attach(device_t dev)
mixer_init(dev, &aicmixer_class, scp);
/* Create device sysctl node. */
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, "internal_codec", CTLTYPE_INT | CTLFLAG_RW,
scp, 0, sysctl_hw_pcm_internal_codec, "I",
"use internal audio codec");
return (0);
}

View File

@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$");
#include <dev/gpio/gpiobusvar.h>
#include <dev/extres/clk/clk.h>
#include <mips/ingenic/jz4780_common.h>
#include <mips/ingenic/jz4780_codec.h>
@ -64,6 +66,7 @@ struct codec_softc {
struct resource *res[1];
bus_space_tag_t bst;
bus_space_handle_t bsh;
clk_t clk;
};
static struct resource_spec codec_spec[] = {
@ -81,6 +84,8 @@ codec_write(struct codec_softc *sc, uint32_t reg, uint32_t val)
{
uint32_t tmp;
clk_enable(sc->clk);
tmp = (reg << RGADW_RGADDR_S);
tmp |= (val << RGADW_RGDIN_S);
tmp |= RGADW_RGWR;
@ -90,6 +95,8 @@ codec_write(struct codec_softc *sc, uint32_t reg, uint32_t val)
while(READ4(sc, CODEC_RGADW) & RGADW_RGWR)
;
clk_disable(sc->clk);
return (0);
}
@ -98,11 +105,15 @@ codec_read(struct codec_softc *sc, uint32_t reg)
{
uint32_t tmp;
clk_enable(sc->clk);
tmp = (reg << RGADW_RGADDR_S);
WRITE4(sc, CODEC_RGADW, tmp);
tmp = READ4(sc, CODEC_RGDATA);
clk_disable(sc->clk);
return (tmp);
}
@ -223,6 +234,12 @@ codec_attach(device_t dev)
sc->bst = rman_get_bustag(sc->res[0]);
sc->bsh = rman_get_bushandle(sc->res[0]);
if (clk_get_by_ofw_name(dev, 0, "i2s", &sc->clk) != 0) {
device_printf(dev, "could not get i2s clock\n");
bus_release_resources(dev, codec_spec, sc->res);
return (ENXIO);
}
/* Initialize codec. */
reg = codec_read(sc, CR_VIC);
reg &= ~(VIC_SB_SLEEP | VIC_SB);
@ -236,7 +253,7 @@ codec_attach(device_t dev)
DELAY(10000);
/* I2S, 16-bit, 96 kHz. */
/* I2S, 16-bit, 48 kHz. */
reg = codec_read(sc, AICR_DAC);
reg &= ~(AICR_DAC_SB | DAC_ADWL_M);
reg |= DAC_ADWL_16;
@ -246,7 +263,7 @@ codec_attach(device_t dev)
DELAY(10000);
reg = FCR_DAC_96;
reg = FCR_DAC_48;
codec_write(sc, FCR_DAC, reg);
DELAY(10000);

View File

@ -74,6 +74,7 @@
#define VIC_SB (1 << 0) /* complete power-down */
#define CR_CK 0x1C /* Clock Control Register */
#define FCR_DAC 0x1D /* DAC Frequency Control Register */
#define FCR_DAC_48 8 /* 48 kHz. */
#define FCR_DAC_96 10 /* 96 kHz. */
#define FCR_ADC 0x20 /* ADC Frequency Control Register */
#define CR_TIMER_MSB 0x21 /* MSB of programmable counter */