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:
parent
760b3e4651
commit
9797927a90
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user