aboutsummaryrefslogtreecommitdiff
path: root/sys/mips/ingenic
diff options
context:
space:
mode:
authorJared McNeill <jmcneill@FreeBSD.org>2016-12-29 14:00:10 +0000
committerJared McNeill <jmcneill@FreeBSD.org>2016-12-29 14:00:10 +0000
commit7fd348080f409e470e3a61234fe1f6864f073d35 (patch)
treea2369b02d5a5edda110257a9e7aa54b59c36c159 /sys/mips/ingenic
parent1357722508cf71b513b8fb34cf69cd5c9eec323c (diff)
downloadsrc-7fd348080f409e470e3a61234fe1f6864f073d35.tar.gz
src-7fd348080f409e470e3a61234fe1f6864f073d35.zip
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
Notes
Notes: svn path=/head/; revision=310776
Diffstat (limited to 'sys/mips/ingenic')
-rw-r--r--sys/mips/ingenic/jz4780_aic.c71
-rw-r--r--sys/mips/ingenic/jz4780_codec.c21
-rw-r--r--sys/mips/ingenic/jz4780_codec.h1
3 files changed, 80 insertions, 13 deletions
diff --git a/sys/mips/ingenic/jz4780_aic.c b/sys/mips/ingenic/jz4780_aic.c
index aa771d849ef0..d80fecaae6c8 100644
--- a/sys/mips/ingenic/jz4780_aic.c
+++ b/sys/mips/ingenic/jz4780_aic.c
@@ -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);
@@ -610,6 +609,48 @@ aic_configure(struct aic_softc *sc)
}
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);
}
diff --git a/sys/mips/ingenic/jz4780_codec.c b/sys/mips/ingenic/jz4780_codec.c
index 0a6b82b9d172..ccd56807020a 100644
--- a/sys/mips/ingenic/jz4780_codec.c
+++ b/sys/mips/ingenic/jz4780_codec.c
@@ -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);
diff --git a/sys/mips/ingenic/jz4780_codec.h b/sys/mips/ingenic/jz4780_codec.h
index 6f5f6ffeec07..151a2f4c78ee 100644
--- a/sys/mips/ingenic/jz4780_codec.h
+++ b/sys/mips/ingenic/jz4780_codec.h
@@ -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 */