From b4ea3c5a3c5f689bb0a48ddf14c75182c0ae0938 Mon Sep 17 00:00:00 2001 From: jmcneill Date: Sat, 10 Jan 2015 12:16:28 +0000 Subject: [PATCH] From Petri Laakso : - Audio output driver for imx23 - Supporting code for audio driver --- sys/arch/arm/imx/imx23_digfilt.c | 1130 +++++++++++++++++++++++++++ sys/arch/arm/imx/imx23_digfiltreg.h | 382 +++++++++ sys/arch/arm/imx/imx23_digfiltvar.h | 35 + sys/arch/arm/imx/imx23_rtc.c | 199 +++++ sys/arch/arm/imx/imx23_rtcreg.h | 3 +- sys/arch/arm/imx/imx23_rtcvar.h | 37 + 6 files changed, 1785 insertions(+), 1 deletion(-) create mode 100644 sys/arch/arm/imx/imx23_digfilt.c create mode 100644 sys/arch/arm/imx/imx23_digfiltreg.h create mode 100644 sys/arch/arm/imx/imx23_digfiltvar.h create mode 100644 sys/arch/arm/imx/imx23_rtc.c create mode 100644 sys/arch/arm/imx/imx23_rtcvar.h diff --git a/sys/arch/arm/imx/imx23_digfilt.c b/sys/arch/arm/imx/imx23_digfilt.c new file mode 100644 index 000000000000..2b95b32fd566 --- /dev/null +++ b/sys/arch/arm/imx/imx23_digfilt.c @@ -0,0 +1,1130 @@ +/* $Id: imx23_digfilt.c,v 1.1 2015/01/10 12:16:28 jmcneill Exp $ */ + +/* + * Copyright (c) 2014 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Petri Laakso. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Autoconf. */ +static int digfilt_match(device_t, cfdata_t, void *); +static void digfilt_attach(device_t, device_t, void *); +static int digfilt_activate(device_t, enum devact); + +/* Audio driver interface. */ +static int digfilt_drain(void *); +static int digfilt_query_encoding(void *, struct audio_encoding *); +static int digfilt_set_params(void *, int, int, audio_params_t *, + audio_params_t *, stream_filter_list_t *, + stream_filter_list_t *); +static int digfilt_round_blocksize(void *, int, int, const audio_params_t *); +static int digfilt_init_output(void *, void *, int ); +static int digfilt_start_output(void *, void *, int, void (*)(void *), void *); +static int digfilt_halt_output(void *); +static int digfilt_getdev(void *, struct audio_device *); +static int digfilt_set_port(void *, mixer_ctrl_t *); +static int digfilt_get_port(void *, mixer_ctrl_t *); +static int digfilt_query_devinfo(void *, mixer_devinfo_t *); +static void *digfilt_allocm(void *, int, size_t); +static void digfilt_freem(void *, void *, size_t); +static size_t digfilt_round_buffersize(void *, int, size_t); +static int digfilt_get_props(void *); +static void digfilt_get_locks(void *, kmutex_t **, kmutex_t **); + +/* IRQs */ +static int dac_error_intr(void *); +static int dac_dma_intr(void *); + +struct digfilt_softc; + +/* Audio out. */ +static void *digfilt_ao_alloc_dmachain(void *, size_t); +static void digfilt_ao_apply_mutes(struct digfilt_softc *); +static void digfilt_ao_init(struct digfilt_softc *); +static void digfilt_ao_reset(struct digfilt_softc *); +static void digfilt_ao_set_rate(struct digfilt_softc *, int); + +/* Audio in. */ +#if 0 +static void digfilt_ai_reset(struct digfilt_softc *); +#endif + +#define DIGFILT_DMA_NSEGS 1 +#define DIGFILT_BLOCKSIZE_MAX 4096 +#define DIGFILT_BLOCKSIZE_ROUND 512 +#define DIGFILT_DMA_CHAIN_LENGTH 3 +#define DIGFILT_DMA_CHANNEL 1 +#define DIGFILT_MUTE_DAC 1 +#define DIGFILT_MUTE_HP 2 +#define DIGFILT_MUTE_LINE 4 +#define DIGFILT_SOFT_RST_LOOP 455 /* At least 1 us. */ + +#define AO_RD(sc, reg) \ + bus_space_read_4(sc->sc_iot, sc->sc_aohdl, (reg)) +#define AO_WR(sc, reg, val) \ + bus_space_write_4(sc->sc_iot, sc->sc_aohdl, (reg), (val)) +#define AI_RD(sc, reg) \ + bus_space_read_4(sc->sc_iot, sc->sc_aihdl, (reg)) +#define AI_WR(sc, reg, val) \ + bus_space_write_4(sc->sc_iot, sc->sc_aihdl, (reg), (val)) + +struct digfilt_softc { + device_t sc_dev; + device_t sc_audiodev; + struct audio_format sc_format; + struct audio_encoding_set *sc_encodings; + bus_space_handle_t sc_aihdl; + bus_space_handle_t sc_aohdl; + apbdma_softc_t sc_dmac; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamp; + bus_dmamap_t sc_c_dmamp; + bus_dma_segment_t sc_ds[DIGFILT_DMA_NSEGS]; + bus_dma_segment_t sc_c_ds[DIGFILT_DMA_NSEGS]; + bus_space_handle_t sc_hdl; + kmutex_t sc_intr_lock; + bus_space_tag_t sc_iot; + kmutex_t sc_lock; + audio_params_t sc_pparam; + void *sc_buffer; + void *sc_dmachain; + void *sc_intarg; + void (*sc_intr)(void*); + uint8_t sc_mute; + uint8_t sc_cmd_index; +}; + +CFATTACH_DECL3_NEW(digfilt, + sizeof(struct digfilt_softc), + digfilt_match, + digfilt_attach, + NULL, + digfilt_activate, + NULL, + NULL, + 0); + +static const struct audio_hw_if digfilt_hw_if = { + .open = NULL, + .close = NULL, + .drain = digfilt_drain, + .query_encoding = digfilt_query_encoding, + .set_params = digfilt_set_params, + .round_blocksize = digfilt_round_blocksize, + .commit_settings = NULL, + .init_output = digfilt_init_output, + .init_input = NULL, + .start_output = digfilt_start_output, + .start_input = NULL, + .halt_output = digfilt_halt_output, + .speaker_ctl = NULL, + .getdev = digfilt_getdev, + .setfd = NULL, + .set_port = digfilt_set_port, + .get_port = digfilt_get_port, + .query_devinfo = digfilt_query_devinfo, + .allocm = digfilt_allocm, + .freem = digfilt_freem, + .round_buffersize = digfilt_round_buffersize, + .mappage = NULL, + .get_props = digfilt_get_props, + .trigger_output = NULL, + .trigger_input = NULL, + .dev_ioctl = NULL, + .get_locks = digfilt_get_locks +}; + +enum { + DIGFILT_OUTPUT_CLASS, + DIGFILT_OUTPUT_DAC_VOLUME, + DIGFILT_OUTPUT_DAC_MUTE, + DIGFILT_OUTPUT_HP_VOLUME, + DIGFILT_OUTPUT_HP_MUTE, + DIGFILT_OUTPUT_LINE_VOLUME, + DIGFILT_OUTPUT_LINE_MUTE, + DIGFILT_ENUM_LAST +}; + +static int +digfilt_match(device_t parent, cfdata_t match, void *aux) +{ + struct apb_attach_args *aa = aux; + + if (aa->aa_addr == HW_DIGFILT_BASE && aa->aa_size == HW_DIGFILT_SIZE) + return 1; + else + return 0; +} + +static void +digfilt_attach(device_t parent, device_t self, void *aux) +{ + struct apb_softc *sc_parent = device_private(parent); + struct digfilt_softc *sc = device_private(self); + struct apb_attach_args *aa = aux; + static int digfilt_attached = 0; + int error; + uint32_t v; + void *intr; + + sc->sc_dev = self; + sc->sc_iot = aa->aa_iot; + sc->sc_dmat = aa->aa_dmat; + + /* This driver requires DMA functionality from the bus. + * Parent bus passes handle to the DMA controller instance. */ + if (sc_parent->dmac == NULL) { + aprint_error_dev(sc->sc_dev, "DMA functionality missing\n"); + return; + } + sc->sc_dmac = device_private(sc_parent->dmac); + + if (aa->aa_addr == HW_DIGFILT_BASE && digfilt_attached) { + aprint_error_dev(sc->sc_dev, "DIGFILT already attached\n"); + return; + } + + /* Allocate DMA for audio buffer. */ + error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, DIGFILT_DMA_NSEGS, + MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp); + if (error) { + aprint_error_dev(sc->sc_dev, + "Unable to allocate DMA handle\n"); + return; + } + + /* Allocate for DMA chain. */ + error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, DIGFILT_DMA_NSEGS, + MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_c_dmamp); + if (error) { + aprint_error_dev(sc->sc_dev, + "Unable to allocate DMA handle\n"); + return; + } + + /* Map DIGFILT bus space. */ + if (bus_space_map(sc->sc_iot, HW_DIGFILT_BASE, HW_DIGFILT_SIZE, 0, + &sc->sc_hdl)) { + aprint_error_dev(sc->sc_dev, + "Unable to map DIGFILT bus space\n"); + return; + } + + /* Map AUDIOOUT subregion from parent bus space. */ + if (bus_space_subregion(sc->sc_iot, sc->sc_hdl, + (HW_AUDIOOUT_BASE - HW_DIGFILT_BASE), HW_AUDIOOUT_SIZE, + &sc->sc_aohdl)) { + aprint_error_dev(sc->sc_dev, + "Unable to submap AUDIOOUT bus space\n"); + return; + } + + /* Map AUDIOIN subregion from parent bus space. */ + if (bus_space_subregion(sc->sc_iot, sc->sc_hdl, + (HW_AUDIOIN_BASE - HW_DIGFILT_BASE), HW_AUDIOIN_SIZE, + &sc->sc_aihdl)) { + aprint_error_dev(sc->sc_dev, + "Unable to submap AUDIOIN bus space\n"); + return; + } + + /* Enable clocks to the DIGFILT block. */ + clkctrl_en_filtclk(); + delay(10); + + digfilt_ao_reset(sc); /* Reset AUDIOOUT. */ + /* Not yet: digfilt_ai_reset(sc); */ + + v = AO_RD(sc, HW_AUDIOOUT_VERSION); + aprint_normal(": DIGFILT Block v%" __PRIuBIT ".%" __PRIuBIT + ".%" __PRIuBIT "\n", + __SHIFTOUT(v, HW_AUDIOOUT_VERSION_MAJOR), + __SHIFTOUT(v, HW_AUDIOOUT_VERSION_MINOR), + __SHIFTOUT(v, HW_AUDIOOUT_VERSION_STEP)); + + digfilt_ao_init(sc); + digfilt_ao_set_rate(sc, 44100); /* Default sample rate 44.1 kHz. */ + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); + + /* HW supported formats. */ + sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD; + sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE; + sc->sc_format.validbits = 16; + sc->sc_format.precision = 16; + sc->sc_format.channels = 2; + sc->sc_format.channel_mask = AUFMT_STEREO; + sc->sc_format.frequency_type = 8; + sc->sc_format.frequency[0] = 8000; + sc->sc_format.frequency[1] = 11025; + sc->sc_format.frequency[2] = 12000; + sc->sc_format.frequency[3] = 16000; + sc->sc_format.frequency[4] = 22050; + sc->sc_format.frequency[5] = 24000; + sc->sc_format.frequency[6] = 32000; + sc->sc_format.frequency[7] = 44100; + + if (auconv_create_encodings(&sc->sc_format, 1, &sc->sc_encodings)) { + aprint_error_dev(self, "could not create encodings\n"); + return; + } + + sc->sc_audiodev = audio_attach_mi(&digfilt_hw_if, sc, sc->sc_dev); + + /* Default mutes. */ + sc->sc_mute = DIGFILT_MUTE_LINE; + digfilt_ao_apply_mutes(sc); + + /* Allocate DMA safe memory for the DMA chain. */ + sc->sc_dmachain = digfilt_ao_alloc_dmachain(sc, + sizeof(struct apbdma_command) * DIGFILT_DMA_CHAIN_LENGTH); + if (sc->sc_dmachain == NULL) { + aprint_error_dev(self, "digfilt_ao_alloc_dmachain failed\n"); + return; + } + + intr = intr_establish(IRQ_DAC_DMA, IPL_SCHED, IST_LEVEL, dac_dma_intr, + sc); + if (intr == NULL) { + aprint_error_dev(sc->sc_dev, + "Unable to establish IRQ for DAC_DMA\n"); + return; + } + + intr = intr_establish(IRQ_DAC_ERROR, IPL_SCHED, IST_LEVEL, + dac_error_intr, sc); + if (intr == NULL) { + aprint_error_dev(sc->sc_dev, + "Unable to establish IRQ for DAC_ERROR\n"); + return; + } + + /* Initialize DMA channel. */ + apbdma_chan_init(sc->sc_dmac, DIGFILT_DMA_CHANNEL); + + digfilt_attached = 1; + + return; +} + +static int +digfilt_activate(device_t self, enum devact act) +{ + return EOPNOTSUPP; +} + +static int +digfilt_drain(void *priv) +{ + + struct digfilt_softc *sc = priv; + + apbdma_wait(sc->sc_dmac, 1); + sc->sc_cmd_index = 0; + + return 0; +} + +static int +digfilt_query_encoding(void *priv, struct audio_encoding *ae) +{ + struct digfilt_softc *sc = priv; + return auconv_query_encoding(sc->sc_encodings, ae); +} + +static int +digfilt_set_params(void *priv, int setmode, int usemode, + audio_params_t *play, audio_params_t *rec, + stream_filter_list_t *pfil, stream_filter_list_t *rfil) +{ + struct digfilt_softc *sc = priv; + int index; + + if (play && (setmode & AUMODE_PLAY)) { + index = auconv_set_converter(&sc->sc_format, 1, + AUMODE_PLAY, play, true, pfil); + if (index < 0) + return EINVAL; + sc->sc_pparam = pfil->req_size > 0 ? + pfil->filters[0].param : + *play; + + /* At this point bitrate should be figured out. */ + digfilt_ao_set_rate(sc, sc->sc_pparam.sample_rate); + } + + return 0; +} + +static int +digfilt_round_blocksize(void *priv, int bs, int mode, +const audio_params_t *param) +{ + int blocksize; + + if (bs > DIGFILT_BLOCKSIZE_MAX) + blocksize = DIGFILT_BLOCKSIZE_MAX; + else + blocksize = bs & ~(DIGFILT_BLOCKSIZE_ROUND-1); + + return blocksize; +} + +static int +digfilt_init_output(void *priv, void *buffer, int size) +{ + struct digfilt_softc *sc = priv; + apbdma_command_t dma_cmd; + int i; + dma_cmd = sc->sc_dmachain; + sc->sc_cmd_index = 0; + + /* + * Build circular DMA command chain template for later use. + */ + for (i = 0; i < DIGFILT_DMA_CHAIN_LENGTH; i++) { + /* Last entry loops back to first. */ + if (i == DIGFILT_DMA_CHAIN_LENGTH - 1) + dma_cmd[i].next = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr); + else + dma_cmd[i].next = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr + (sizeof(struct apbdma_command) * (1 + i))); + + dma_cmd[i].control = __SHIFTIN(DIGFILT_BLOCKSIZE_MAX, APBDMA_CMD_XFER_COUNT) | + __SHIFTIN(1, APBDMA_CMD_CMDPIOWORDS) | + APBDMA_CMD_SEMAPHORE | + APBDMA_CMD_IRQONCMPLT | + APBDMA_CMD_CHAIN | + __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND); + + dma_cmd[i].buffer = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr); + + dma_cmd[i].pio_words[0] = HW_AUDIOOUT_CTRL_WORD_LENGTH | + HW_AUDIOOUT_CTRL_FIFO_ERROR_IRQ_EN | + HW_AUDIOOUT_CTRL_RUN; + + } + + apbdma_chan_set_chain(sc->sc_dmac, DIGFILT_DMA_CHANNEL, sc->sc_c_dmamp); + + return 0; +} + +static int +digfilt_start_output(void *priv, void *start, int bs, void (*intr)(void*), void *intarg) +{ + struct digfilt_softc *sc = priv; + apbdma_command_t dma_cmd; + bus_addr_t offset; + + sc->sc_intr = intr; + sc->sc_intarg = intarg; + dma_cmd = sc->sc_dmachain; + + offset = (bus_addr_t)start - (bus_addr_t)sc->sc_buffer; + + dma_cmd[sc->sc_cmd_index].buffer = + (void *)((bus_addr_t)sc->sc_dmamp->dm_segs[0].ds_addr + offset); + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, offset, bs, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, sc->sc_c_dmamp, + sizeof(struct apbdma_command) * sc->sc_cmd_index, sizeof(struct apbdma_command), BUS_DMASYNC_PREWRITE); + + sc->sc_cmd_index++; + if (sc->sc_cmd_index > DIGFILT_DMA_CHAIN_LENGTH - 1) + sc->sc_cmd_index = 0; + + apbdma_run(sc->sc_dmac, DIGFILT_DMA_CHANNEL); + + return 0; +} + +static int +digfilt_halt_output(void *priv) +{ + return 0; +} + +static int +digfilt_getdev(void *priv, struct audio_device *ad) +{ + struct digfilt_softc *sc = priv; + + strncpy(ad->name, device_xname(sc->sc_dev), MAX_AUDIO_DEV_LEN); + strncpy(ad->version, "", MAX_AUDIO_DEV_LEN); + strncpy(ad->config, "", MAX_AUDIO_DEV_LEN); + + return 0; +} + +static int +digfilt_set_port(void *priv, mixer_ctrl_t *mc) +{ + struct digfilt_softc *sc = priv; + uint32_t val; + uint8_t nvol; + + switch (mc->dev) { + case DIGFILT_OUTPUT_DAC_VOLUME: + val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME); + val &= ~(HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT | + HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT); + + /* DAC volume field is 8 bits. */ + nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + if (nvol > 0xff) + nvol = 0xff; + + val |= __SHIFTIN(nvol, HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT); + + nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; + if (nvol > 0xff) + nvol = 0xff; + + val |= __SHIFTIN(nvol, HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT); + + AO_WR(sc, HW_AUDIOOUT_DACVOLUME, val); + + return 0; + + case DIGFILT_OUTPUT_HP_VOLUME: + val = AO_RD(sc, HW_AUDIOOUT_HPVOL); + val &= ~(HW_AUDIOOUT_HPVOL_VOL_LEFT | + HW_AUDIOOUT_HPVOL_VOL_RIGHT); + + /* HP volume field is 7 bits. */ + nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + if (nvol > 0x7f) + nvol = 0x7f; + + nvol = ~nvol; + val |= __SHIFTIN(nvol, HW_AUDIOOUT_HPVOL_VOL_LEFT); + + nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; + if (nvol > 0x7f) + nvol = 0x7f; + + nvol = ~nvol; + val |= __SHIFTIN(nvol, HW_AUDIOOUT_HPVOL_VOL_RIGHT); + + AO_WR(sc, HW_AUDIOOUT_HPVOL, val); + + return 0; + + case DIGFILT_OUTPUT_LINE_VOLUME: + return 1; + + case DIGFILT_OUTPUT_DAC_MUTE: + if (mc->un.ord) + sc->sc_mute |= DIGFILT_MUTE_DAC; + else + sc->sc_mute &= ~DIGFILT_MUTE_DAC; + + digfilt_ao_apply_mutes(sc); + + return 0; + + case DIGFILT_OUTPUT_HP_MUTE: + if (mc->un.ord) + sc->sc_mute |= DIGFILT_MUTE_HP; + else + sc->sc_mute &= ~DIGFILT_MUTE_HP; + + digfilt_ao_apply_mutes(sc); + + return 0; + + case DIGFILT_OUTPUT_LINE_MUTE: + if (mc->un.ord) + sc->sc_mute |= DIGFILT_MUTE_LINE; + else + sc->sc_mute &= ~DIGFILT_MUTE_LINE; + + digfilt_ao_apply_mutes(sc); + + return 0; + } + + return ENXIO; +} + +static int +digfilt_get_port(void *priv, mixer_ctrl_t *mc) +{ + struct digfilt_softc *sc = priv; + uint32_t val; + uint8_t nvol; + + switch (mc->dev) { + case DIGFILT_OUTPUT_DAC_VOLUME: + val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME); + + nvol = __SHIFTOUT(val, HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT); + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol; + + nvol = __SHIFTOUT(val, HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT); + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol; + + return 0; + + case DIGFILT_OUTPUT_HP_VOLUME: + val = AO_RD(sc, HW_AUDIOOUT_HPVOL); + + nvol = __SHIFTOUT(val, HW_AUDIOOUT_HPVOL_VOL_LEFT); + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = ~nvol & 0x7f; + + nvol = __SHIFTOUT(val, HW_AUDIOOUT_HPVOL_VOL_RIGHT); + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = ~nvol & 0x7f; + + return 0; + + case DIGFILT_OUTPUT_LINE_VOLUME: + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 255; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 255; + + return 0; + + case DIGFILT_OUTPUT_DAC_MUTE: + val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME); + + mc->un.ord = (val & (HW_AUDIOOUT_DACVOLUME_MUTE_LEFT | + HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT)) ? 1 : 0; + + return 0; + + case DIGFILT_OUTPUT_HP_MUTE: + val = AO_RD(sc, HW_AUDIOOUT_HPVOL); + + mc->un.ord = (val & HW_AUDIOOUT_HPVOL_MUTE) ? 1 : 0; + + return 0; + + case DIGFILT_OUTPUT_LINE_MUTE: + val = AO_RD(sc, HW_AUDIOOUT_SPEAKERCTRL); + + mc->un.ord = (val & HW_AUDIOOUT_SPEAKERCTRL_MUTE) ? 1 : 0; + + return 0; + } + + return ENXIO; +} + +static int +digfilt_query_devinfo(void *priv, mixer_devinfo_t *di) +{ + + switch (di->index) { + case DIGFILT_OUTPUT_CLASS: + di->mixer_class = DIGFILT_OUTPUT_CLASS; + strcpy(di->label.name, AudioCoutputs); + di->type = AUDIO_MIXER_CLASS; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + + case DIGFILT_OUTPUT_DAC_VOLUME: + di->mixer_class = DIGFILT_OUTPUT_CLASS; + strcpy(di->label.name, AudioNdac); + di->type = AUDIO_MIXER_VALUE; + di->prev = AUDIO_MIXER_LAST; + di->next = DIGFILT_OUTPUT_DAC_MUTE; + di->un.v.num_channels = 2; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + + case DIGFILT_OUTPUT_DAC_MUTE: + di->mixer_class = DIGFILT_OUTPUT_CLASS; + di->type = AUDIO_MIXER_ENUM; + di->prev = DIGFILT_OUTPUT_DAC_VOLUME; + di->next = AUDIO_MIXER_LAST; +mute: + strlcpy(di->label.name, AudioNmute, sizeof(di->label.name)); + di->un.e.num_mem = 2; + strlcpy(di->un.e.member[0].label.name, AudioNon, + sizeof(di->un.e.member[0].label.name)); + di->un.e.member[0].ord = 1; + strlcpy(di->un.e.member[1].label.name, AudioNoff, + sizeof(di->un.e.member[1].label.name)); + di->un.e.member[1].ord = 0; + return 0; + + case DIGFILT_OUTPUT_HP_VOLUME: + di->mixer_class = DIGFILT_OUTPUT_CLASS; + strcpy(di->label.name, AudioNheadphone); + di->type = AUDIO_MIXER_VALUE; + di->prev = AUDIO_MIXER_LAST; + di->next = DIGFILT_OUTPUT_HP_MUTE; + di->un.v.num_channels = 2; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + + case DIGFILT_OUTPUT_HP_MUTE: + di->mixer_class = DIGFILT_OUTPUT_CLASS; + di->type = AUDIO_MIXER_ENUM; + di->prev = DIGFILT_OUTPUT_HP_VOLUME; + di->next = AUDIO_MIXER_LAST; + goto mute; + + case DIGFILT_OUTPUT_LINE_VOLUME: + di->mixer_class = DIGFILT_OUTPUT_CLASS; + strcpy(di->label.name, AudioNline); + di->type = AUDIO_MIXER_VALUE; + di->prev = AUDIO_MIXER_LAST; + di->next = DIGFILT_OUTPUT_LINE_MUTE; + di->un.v.num_channels = 2; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + + case DIGFILT_OUTPUT_LINE_MUTE: + di->mixer_class = DIGFILT_OUTPUT_CLASS; + di->type = AUDIO_MIXER_ENUM; + di->prev = DIGFILT_OUTPUT_LINE_VOLUME; + di->next = AUDIO_MIXER_LAST; + goto mute; + } + + return ENXIO; +} + +static void * +digfilt_allocm(void *priv, int direction, size_t size) +{ + struct digfilt_softc *sc = priv; + int rsegs; + int error; + + sc->sc_buffer = NULL; + + /* + * AUMODE_PLAY is DMA from memory to device. + */ + if (direction != AUMODE_PLAY) + return NULL; + + error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &sc->sc_ds[0], DIGFILT_DMA_NSEGS, &rsegs, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_dev, + "bus_dmamem_alloc: %d\n", error); + goto out; + } + + error = bus_dmamem_map(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS, size, &sc->sc_buffer, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error); + goto dmamem_free; + } + + /* After load sc_dmamp is valid. */ + error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, sc->sc_buffer, size, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE); + if (error) { + aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error); + goto dmamem_unmap; + } + + memset(sc->sc_buffer, 0x00, size); + + return sc->sc_buffer; + +dmamem_unmap: + bus_dmamem_unmap(sc->sc_dmat, sc->sc_buffer, size); +dmamem_free: + bus_dmamem_free(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS); +out: + return NULL; +} + +static void +digfilt_freem(void *priv, void *kvap, size_t size) +{ + struct digfilt_softc *sc = priv; + + bus_dmamem_unmap(sc->sc_dmat, kvap, size); + bus_dmamem_free(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS); + + return; +} + +static size_t +digfilt_round_buffersize(void *hdl, int direction, size_t bs) +{ + int bufsize; + + bufsize = bs & ~(DIGFILT_BLOCKSIZE_MAX-1); + + return bufsize; +} + +static int +digfilt_get_props(void *sc) +{ + return (AUDIO_PROP_PLAYBACK | AUDIO_PROP_INDEPENDENT); +} + +static void +digfilt_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) +{ + struct digfilt_softc *sc = priv; + + *intr = &sc->sc_intr_lock; + *thread = &sc->sc_lock; + + return; +} + +/* + * IRQ for DAC error. + */ +static int +dac_error_intr(void *arg) +{ + struct digfilt_softc *sc = arg; + AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ); + return 1; +} + +/* + * IRQ from DMA. + */ +static int +dac_dma_intr(void *arg) +{ + struct digfilt_softc *sc = arg; + + unsigned int dma_err; + + mutex_enter(&sc->sc_intr_lock); + + dma_err = apbdma_intr_status(sc->sc_dmac, DIGFILT_DMA_CHANNEL); + + if (dma_err) { + apbdma_ack_error_intr(sc->sc_dmac, DIGFILT_DMA_CHANNEL); + } + + sc->sc_intr(sc->sc_intarg); + apbdma_ack_intr(sc->sc_dmac, DIGFILT_DMA_CHANNEL); + + mutex_exit(&sc->sc_intr_lock); + + /* Return 1 to acknowledge IRQ. */ + return 1; +} + +static void * +digfilt_ao_alloc_dmachain(void *priv, size_t size) +{ + struct digfilt_softc *sc = priv; + int rsegs; + int error; + void *kvap; + + kvap = NULL; + + error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &sc->sc_c_ds[0], DIGFILT_DMA_NSEGS, &rsegs, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_dev, + "bus_dmamem_alloc: %d\n", error); + goto out; + } + + error = bus_dmamem_map(sc->sc_dmat, sc->sc_c_ds, DIGFILT_DMA_NSEGS, size, &kvap, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error); + goto dmamem_free; + } + + /* After load sc_c_dmamp is valid. */ + error = bus_dmamap_load(sc->sc_dmat, sc->sc_c_dmamp, kvap, size, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE); + if (error) { + aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error); + goto dmamem_unmap; + } + + memset(kvap, 0x00, size); + + return kvap; + +dmamem_unmap: + bus_dmamem_unmap(sc->sc_dmat, kvap, size); +dmamem_free: + bus_dmamem_free(sc->sc_dmat, sc->sc_c_ds, DIGFILT_DMA_NSEGS); +out: + + return kvap; +} + +static void +digfilt_ao_apply_mutes(struct digfilt_softc *sc) +{ + + /* DAC. */ + if (sc->sc_mute & DIGFILT_MUTE_DAC) { + AO_WR(sc, HW_AUDIOOUT_DACVOLUME_SET, + HW_AUDIOOUT_DACVOLUME_MUTE_LEFT | + HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT + ); + + } else { + AO_WR(sc, HW_AUDIOOUT_DACVOLUME_CLR, + HW_AUDIOOUT_DACVOLUME_MUTE_LEFT | + HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT + ); + } + + /* HP. */ + if (sc->sc_mute & DIGFILT_MUTE_HP) + AO_WR(sc, HW_AUDIOOUT_HPVOL_SET, HW_AUDIOOUT_HPVOL_MUTE); + else + AO_WR(sc, HW_AUDIOOUT_HPVOL_CLR, HW_AUDIOOUT_HPVOL_MUTE); + + /* Line. */ + if (sc->sc_mute & DIGFILT_MUTE_LINE) + AO_WR(sc, HW_AUDIOOUT_SPEAKERCTRL_SET, + HW_AUDIOOUT_SPEAKERCTRL_MUTE); + else + AO_WR(sc, HW_AUDIOOUT_SPEAKERCTRL_CLR, + HW_AUDIOOUT_SPEAKERCTRL_MUTE); + + return; +} + +/* + * Initialize audio system. + */ +static void +digfilt_ao_init(struct digfilt_softc *sc) +{ + + AO_WR(sc, HW_AUDIOOUT_ANACLKCTRL_CLR, HW_AUDIOOUT_ANACLKCTRL_CLKGATE); + while ((AO_RD(sc, HW_AUDIOOUT_ANACLKCTRL) & + HW_AUDIOOUT_ANACLKCTRL_CLKGATE)); + + /* Hold headphones outputs at ground. */ + AO_WR(sc, HW_AUDIOOUT_ANACTRL_SET, HW_AUDIOOUT_ANACTRL_HP_HOLD_GND); + + /* Remove pulldown resistors on headphone outputs. */ + rtc_release_gnd(1); + + /* Release pull down */ + AO_WR(sc, HW_AUDIOOUT_ANACTRL_CLR, HW_AUDIOOUT_ANACTRL_HP_HOLD_GND); + + AO_WR(sc, HW_AUDIOOUT_ANACTRL_SET, HW_AUDIOOUT_ANACTRL_HP_CLASSAB); + + /* Enable Modules. */ + AO_WR(sc, HW_AUDIOOUT_PWRDN_CLR, + HW_AUDIOOUT_PWRDN_RIGHT_ADC | + HW_AUDIOOUT_PWRDN_DAC | + HW_AUDIOOUT_PWRDN_CAPLESS | + HW_AUDIOOUT_PWRDN_HEADPHONE + ); + + return; +} + +/* + * Reset the AUDIOOUT block. + * + * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block" + */ +static void +digfilt_ao_reset(struct digfilt_softc *sc) +{ + unsigned int loop; + + /* Prepare for soft-reset by making sure that SFTRST is not currently + * asserted. Also clear CLKGATE so we can wait for its assertion below. + */ + AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_SFTRST); + + /* Wait at least a microsecond for SFTRST to deassert. */ + loop = 0; + while ((AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_SFTRST) || + (loop < DIGFILT_SOFT_RST_LOOP)) + loop++; + + /* Clear CLKGATE so we can wait for its assertion below. */ + AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_CLKGATE); + + /* Soft-reset the block. */ + AO_WR(sc, HW_AUDIOOUT_CTRL_SET, HW_AUDIOOUT_CTRL_SFTRST); + + /* Wait until clock is in the gated state. */ + while (!(AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_CLKGATE)); + + /* Bring block out of reset. */ + AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_SFTRST); + + loop = 0; + while ((AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_SFTRST) || + (loop < DIGFILT_SOFT_RST_LOOP)) + loop++; + + AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_CLKGATE); + + /* Wait until clock is in the NON-gated state. */ + while (AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_CLKGATE); + + return; +} + +static void +digfilt_ao_set_rate(struct digfilt_softc *sc, int sr) +{ + uint32_t val; + + + val = AO_RD(sc, HW_AUDIOOUT_DACSRR); + + + val &= ~(HW_AUDIOOUT_DACSRR_BASEMULT | HW_AUDIOOUT_DACSRR_SRC_HOLD | + HW_AUDIOOUT_DACSRR_SRC_INT | HW_AUDIOOUT_DACSRR_SRC_FRAC); + + switch(sr) { + case 8000: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + case 11025: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + case 12000: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x0F, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x13FF, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + case 16000: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + case 22050: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + case 24000: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x0F, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x13FF, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + case 32000: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x0, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + default: + aprint_error_dev(sc->sc_dev, "uknown sample rate: %d\n", sr); + case 44100: + val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | + __SHIFTIN(0x0, HW_AUDIOOUT_DACSRR_SRC_HOLD) | + __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) | + __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC)); + break; + } + + AO_WR(sc, HW_AUDIOOUT_DACSRR, val); + + val = AO_RD(sc, HW_AUDIOOUT_DACSRR); + + return; +} +#if 0 +/* + * Reset the AUDIOIN block. + * + * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block" + */ +static void +digfilt_ai_reset(struct digfilt_softc *sc) +{ + unsigned int loop; + + /* Prepare for soft-reset by making sure that SFTRST is not currently + * asserted. Also clear CLKGATE so we can wait for its assertion below. + */ + AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_SFTRST); + + /* Wait at least a microsecond for SFTRST to deassert. */ + loop = 0; + while ((AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_SFTRST) || + (loop < DIGFILT_SOFT_RST_LOOP)) + loop++; + + /* Clear CLKGATE so we can wait for its assertion below. */ + AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_CLKGATE); + + /* Soft-reset the block. */ + AI_WR(sc, HW_AUDIOIN_CTRL_SET, HW_AUDIOIN_CTRL_SFTRST); + + /* Wait until clock is in the gated state. */ + while (!(AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_CLKGATE)); + + /* Bring block out of reset. */ + AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_SFTRST); + + loop = 0; + while ((AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_SFTRST) || + (loop < DIGFILT_SOFT_RST_LOOP)) + loop++; + + AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_CLKGATE); + + /* Wait until clock is in the NON-gated state. */ + while (AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_CLKGATE); + + return; +} +#endif diff --git a/sys/arch/arm/imx/imx23_digfiltreg.h b/sys/arch/arm/imx/imx23_digfiltreg.h new file mode 100644 index 000000000000..f5104ebb895a --- /dev/null +++ b/sys/arch/arm/imx/imx23_digfiltreg.h @@ -0,0 +1,382 @@ +/* $Id: imx23_digfiltreg.h,v 1.1 2015/01/10 12:16:28 jmcneill Exp $ */ + +/* + * Copyright (c) 2014 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Petri Laakso. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ARM_IMX_IMX23_AUDIOOUTREG_H_ +#define _ARM_IMX_IMX23_AUDIOOUTREG_H_ + +#include + +#define HW_DIGFILT_BASE 0x80048000 +#define HW_DIGFILT_SIZE 0x8000 /* 32 kB */ + +#define HW_AUDIOOUT_BASE 0x80048000 +#define HW_AUDIOOUT_SIZE 0x2000 /* 8 kB */ + +#define HW_AUDIOIN_BASE 0x8004C000 +#define HW_AUDIOIN_SIZE 0x2000 /* 8 kB */ + +/* + * AUDIOIN Control Register. + */ +#define HW_AUDIOIN_CTRL 0x000 +#define HW_AUDIOIN_CTRL_SET 0x004 +#define HW_AUDIOIN_CTRL_CLR 0x008 +#define HW_AUDIOIN_CTRL_TOG 0x00C + +#define HW_AUDIOIN_CTRL_SFTRST __BIT(31) +#define HW_AUDIOIN_CTRL_CLKGATE __BIT(30) +#define HW_AUDIOIN_CTRL_RSRVD3 __BITS(29, 21) +#define HW_AUDIOIN_CTRL_DMAWAIT_COUNT __BITS(20, 16) +#define HW_AUDIOIN_CTRL_RSRVD1 __BITS(15, 11) +#define HW_AUDIOIN_CTRL_LR_SWAP __BIT(10) +#define HW_AUDIOIN_CTRL_EDGE_SYNC __BIT(9) +#define HW_AUDIOIN_CTRL_INVERT_1BIT __BIT(8) +#define HW_AUDIOIN_CTRL_OFFSET_ENABLE __BIT(7) +#define HW_AUDIOIN_CTRL_HPF_ENABLE __BIT(6) +#define HW_AUDIOIN_CTRL_WORD_LENGTH __BIT(5) +#define HW_AUDIOIN_CTRL_LOOPBACK __BIT(4) +#define HW_AUDIOIN_CTRL_FIFO_UNDERFLOW_IRQ __BIT(3) +#define HW_AUDIOIN_CTRL_FIFO_OVERFLOW_IRQ __BIT(2) +#define HW_AUDIOIN_CTRL_FIFO_ERROR_IRQ_EN __BIT(1) +#define HW_AUDIOIN_CTRL_RUN __BIT(0) + +/* + * AUDIOOUT Control Register. + */ +#define HW_AUDIOOUT_CTRL 0x000 +#define HW_AUDIOOUT_CTRL_SET 0x004 +#define HW_AUDIOOUT_CTRL_CLR 0x008 + +#define HW_AUDIOOUT_CTRL_SFTRST __BIT(31) +#define HW_AUDIOOUT_CTRL_CLKGATE __BIT(30) +#define HW_AUDIOOUT_CTRL_RSRVD4 __BITS(29, 21) +#define HW_AUDIOOUT_CTRL_DMAWAIT_COUNT __BITS(20, 16) +#define HW_AUDIOOUT_CTRL_RSRVD3 __BIT(15) +#define HW_AUDIOOUT_CTRL_LR_SWAP __BIT(14) +#define HW_AUDIOOUT_CTRL_EDGE_SYNC __BIT(13) +#define HW_AUDIOOUT_CTRL_INVERT_1BIT __BIT(12) +#define HW_AUDIOOUT_CTRL_RSRVD2 __BITS(11, 10) +#define HW_AUDIOOUT_CTRL_SS3D_EFFECT __BITS(9, 8) +#define HW_AUDIOOUT_CTRL_RSRVD1 __BIT(7) +#define HW_AUDIOOUT_CTRL_WORD_LENGTH __BIT(6) +#define HW_AUDIOOUT_CTRL_DAC_ZERO_ENABLE __BIT(5) +#define HW_AUDIOOUT_CTRL_LOOPBACK __BIT(4) +#define HW_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ __BIT(3) +#define HW_AUDIOOUT_CTRL_FIFO_OVERFLOW_IRQ __BIT(2) +#define HW_AUDIOOUT_CTRL_FIFO_ERROR_IRQ_EN __BIT(1) +#define HW_AUDIOOUT_CTRL_RUN __BIT(0) + +/* + * AUDIOOUT Status Register. + */ +#define HW_AUDIOOUT_STAT 0x010 +#define HW_AUDIOOUT_STAT_SET 0x014 +#define HW_AUDIOOUT_STAT_CLR 0x018 +#define HW_AUDIOOUT_STAT_TOG 0x01C + +#define HW_AUDIOOUT_STAT_DAC_PRESENT __BIT(31) +#define HW_AUDIOOUT_STAT_RSRVD1 __BITS(30, 0) + +/* + * AUDIOOUT Sample Rate Register. + */ +#define HW_AUDIOOUT_DACSRR 0x020 +#define HW_AUDIOOUT_DACSRR_SET 0x024 +#define HW_AUDIOOUT_DACSRR_CLR 0x028 +#define HW_AUDIOOUT_DACSRR_TOG 0x02C + +#define HW_AUDIOOUT_DACSRR_OSR __BIT(31) +#define HW_AUDIOOUT_DACSRR_BASEMULT __BITS(30, 28) +#define HW_AUDIOOUT_DACSRR_RSRVD2 __BIT(27) +#define HW_AUDIOOUT_DACSRR_SRC_HOLD __BITS(26, 24) +#define HW_AUDIOOUT_DACSRR_RSRVD1 __BITS(23, 21) +#define HW_AUDIOOUT_DACSRR_SRC_INT __BITS(20, 16) +#define HW_AUDIOOUT_DACSRR_RSRVD0 __BITS(15, 13) +#define HW_AUDIOOUT_DACSRR_SRC_FRAC __BITS(12, 0) + +/* + * AUDIOOUT Volume Register. + */ +#define HW_AUDIOOUT_DACVOLUME 0x030 +#define HW_AUDIOOUT_DACVOLUME_SET 0x034 +#define HW_AUDIOOUT_DACVOLUME_CLR 0x038 +#define HW_AUDIOOUT_DACVOLUME_TOG 0x03C + +#define HW_AUDIOOUT_DACVOLUME_RSRVD4 __BITS(31, 29) +#define HW_AUDIOOUT_DACVOLUME_VOLUME_UPDATE_LEFT __BIT(28) +#define HW_AUDIOOUT_DACVOLUME_RSRVD3 __BITS(27, 26) +#define HW_AUDIOOUT_DACVOLUME_EN_ZCD __BIT(25) +#define HW_AUDIOOUT_DACVOLUME_MUTE_LEFT __BIT(24) +#define HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT __BITS(23, 16) +#define HW_AUDIOOUT_DACVOLUME_RSRVD2 __BITS(15, 13) +#define HW_AUDIOOUT_DACVOLUME_VOLUME_UPDATE_RIGHT __BIT(12) +#define HW_AUDIOOUT_DACVOLUME_RSRVD1 __BITS(11, 9) +#define HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT __BIT(8) +#define HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT __BITS(7, 0) + +/* + * AUDIOOUT Debug Register. + */ +#define HW_AUDIOOUT_DACDEBUG 0x040 +#define HW_AUDIOOUT_DACDEBUG_SET 0x044 +#define HW_AUDIOOUT_DACDEBUG_CLR 0x048 +#define HW_AUDIOOUT_DACDEBUG_TOG 0x04C + +#define HW_AUDIOOUT_DACDEBUG_ENABLE_DACDMA __BIT(31) +#define HW_AUDIOOUT_DACDEBUG_RSRVD2 __BITS(30, 12) +#define HW_AUDIOOUT_DACDEBUG_RAM_SS __BITS(11, 8) +#define HW_AUDIOOUT_DACDEBUG_RSRVD1 __BITS(7, 6) +#define HW_AUDIOOUT_DACDEBUG_SET_INTERRUPT1_CLK_CROSS __BIT(5) +#define HW_AUDIOOUT_DACDEBUG_SET_INTERRUPT0_CLK_CROSS __BIT(4) +#define HW_AUDIOOUT_DACDEBUG_SET_INTERRUPT1_HAND_SHAKE __BIT(3) +#define HW_AUDIOOUT_DACDEBUG_SET_INTERRUPT0_HAND_SHAKE __BIT(2) +#define HW_AUDIOOUT_DACDEBUG_DMA_PREQ __BIT(1) +#define HW_AUDIOOUT_DACDEBUG_FIFO_STATUS __BIT(0) + +/* + * Headphone Volume and Select Control Register. + */ +#define HW_AUDIOOUT_HPVOL 0x050 +#define HW_AUDIOOUT_HPVOL_SET 0x054 +#define HW_AUDIOOUT_HPVOL_CLR 0x058 +#define HW_AUDIOOUT_HPVOL_TOG 0x05C + +#define HW_AUDIOOUT_HPVOL_RSRVD5 __BITS(31, 29) +#define HW_AUDIOOUT_HPVOL_VOLUME_UPDATE_PENDING __BIT(28) +#define HW_AUDIOOUT_HPVOL_RSRVD4 __BITS(27, 26) +#define HW_AUDIOOUT_HPVOL_EN_MSTR_ZCD __BIT(25) +#define HW_AUDIOOUT_HPVOL_MUTE __BIT(24) +#define HW_AUDIOOUT_HPVOL_RSRVD3 __BITS(23, 17) +#define HW_AUDIOOUT_HPVOL_SELECT __BIT(16) +#define HW_AUDIOOUT_HPVOL_RSRVD2 __BIT(15) +#define HW_AUDIOOUT_HPVOL_VOL_LEFT __BITS(14, 8) +#define HW_AUDIOOUT_HPVOL_RSRVD1 __BIT(7) +#define HW_AUDIOOUT_HPVOL_VOL_RIGHT __BITS(6, 0) + +/* + * Reserved Register. + */ +#define HW_AUDIOOUT_RESERVED 0x060 +#define HW_AUDIOOUT_RESERVED_SET 0x064 +#define HW_AUDIOOUT_RESERVED_CLR 0x068 +#define HW_AUDIOOUT_RESERVED_TOG 0x06C + +#define HW_AUDIOOUT_RESERVED_RSRVD1 __BITS(31, 0) + +/* + * Audio Power-Down Control Register. + */ +#define HW_AUDIOOUT_PWRDN 0x070 +#define HW_AUDIOOUT_PWRDN_SET 0x074 +#define HW_AUDIOOUT_PWRDN_CLR 0x078 +#define HW_AUDIOOUT_PWRDN_TOG 0x07C + +#define HW_AUDIOOUT_PWRDN_RSRVD7 __BITS(31, 25) +#define HW_AUDIOOUT_PWRDN_SPEAKER __BIT(24) +#define HW_AUDIOOUT_PWRDN_RSRVD6 __BITS(23, 21) +#define HW_AUDIOOUT_PWRDN_SELFBIAS __BIT(20) +#define HW_AUDIOOUT_PWRDN_RSRVD5 __BITS(19, 17) +#define HW_AUDIOOUT_PWRDN_RIGHT_ADC __BIT(16) +#define HW_AUDIOOUT_PWRDN_RSRVD4 __BITS(15, 13) +#define HW_AUDIOOUT_PWRDN_DAC __BIT(12) +#define HW_AUDIOOUT_PWRDN_RSRVD3 __BITS(11, 9) +#define HW_AUDIOOUT_PWRDN_ADC __BIT(8) +#define HW_AUDIOOUT_PWRDN_RSRVD2 __BITS(7, 5) +#define HW_AUDIOOUT_PWRDN_CAPLESS __BIT(4) +#define HW_AUDIOOUT_PWRDN_RSRVD1 __BITS(3, 1) +#define HW_AUDIOOUT_PWRDN_HEADPHONE __BIT(0) + +/* + * AUDIOOUT Reference Control Register. + */ +#define HW_AUDIOOUT_REFCTRL 0x080 +#define HW_AUDIOOUT_REFCTRL_SET 0x084 +#define HW_AUDIOOUT_REFCTRL_CLR 0x088 +#define HW_AUDIOOUT_REFCTRL_TOG 0x08C + +#define HW_AUDIOOUT_REFCTRL_RSRVD4 __BITS(31, 27) +#define HW_AUDIOOUT_REFCTRL_FASTSETTLING __BIT(26) +#define HW_AUDIOOUT_REFCTRL_RAISE_REF __BIT(25) +#define HW_AUDIOOUT_REFCTRL_XTAL_BGR_BIAS __BIT(24) +#define HW_AUDIOOUT_REFCTRL_RSRVD3 __BIT(23) +#define HW_AUDIOOUT_REFCTRL_VBG_ADJ __BITS(22, 20) +#define HW_AUDIOOUT_REFCTRL_LOW_PWR __BIT(19) +#define HW_AUDIOOUT_REFCTRL_LW_REF __BIT(18) +#define HW_AUDIOOUT_REFCTRL_BIAS_CTRL __BITS(17, 16) +#define HW_AUDIOOUT_REFCTRL_RSRVD2 __BIT(15) +#define HW_AUDIOOUT_REFCTRL_VDDXTAL_TO_VDDD __BIT(14) +#define HW_AUDIOOUT_REFCTRL_ADJ_ADC __BIT(13) +#define HW_AUDIOOUT_REFCTRL_ADJ_VAG __BIT(12) +#define HW_AUDIOOUT_REFCTRL_ADC_REFVAL __BITS(11, 8) +#define HW_AUDIOOUT_REFCTRL_VAG_VAL __BITS(7, 4) +#define HW_AUDIOOUT_REFCTRL_RSRVD1 __BIT(3) +#define HW_AUDIOOUT_REFCTRL_DAC_ADJ __BIT(2, 0) + +/* + * Miscellaneous Audio Controls Register. + */ +#define HW_AUDIOOUT_ANACTRL 0x090 +#define HW_AUDIOOUT_ANACTRL_SET 0x094 +#define HW_AUDIOOUT_ANACTRL_CLR 0x098 +#define HW_AUDIOOUT_ANACTRL_TOG 0x09C + +#define HW_AUDIOOUT_ANACTRL_RSRVD8 __BITS(31, 29) +#define HW_AUDIOOUT_ANACTRL_SHORT_CM_STS __BIT(28) +#define HW_AUDIOOUT_ANACTRL_RSRVD7 __BITS(27, 25) +#define HW_AUDIOOUT_ANACTRL_SHORT_LR_STS __BIT(24) +#define HW_AUDIOOUT_ANACTRL_RSRVD6 __BITS(23, 22) +#define HW_AUDIOOUT_ANACTRL_SHORTMODE_CM __BIT(21, 20) +#define HW_AUDIOOUT_ANACTRL_RSRVD5 __BIT(19) +#define HW_AUDIOOUT_ANACTRL_SHORTMODE_LR __BITS(18, 17) +#define HW_AUDIOOUT_ANACTRL_RSRVD4 __BITS(16, 15) +#define HW_AUDIOOUT_ANACTRL_SHORT_LVLADJL __BITS(14, 12) +#define HW_AUDIOOUT_ANACTRL_RSRVD3 __BIT(11) +#define HW_AUDIOOUT_ANACTRL_SHORT_LVLADJR __BITS(10, 8) +#define HW_AUDIOOUT_ANACTRL_RSRVD2 __BITS(7, 6) +#define HW_AUDIOOUT_ANACTRL_HP_HOLD_GND __BIT(5) +#define HW_AUDIOOUT_ANACTRL_HP_CLASSAB __BIT(4) +#define HW_AUDIOOUT_ANACTRL_RSRVD1 __BITS(3, 0) + +/* + * Miscellaneous Test Audio Controls Register. + */ +#define HW_AUDIOOUT_TEST 0x0a0 +#define HW_AUDIOOUT_TEST_SET 0x0a4 +#define HW_AUDIOOUT_TEST_CLR 0x0a8 +#define HW_AUDIOOUT_TEST_TOG 0x0aC + +#define HW_AUDIOOUT_TEST_RSRVD4 __BIT(31) +#define HW_AUDIOOUT_TEST_HP_ANTIPOP __BITS(30, 28) +#define HW_AUDIOOUT_TEST_RSRVD3 __BIT(27) +#define HW_AUDIOOUT_TEST_TM_ADCIN_TOHP __BIT(26) +#define HW_AUDIOOUT_TEST_TM_LOOP __BIT(25) +#define HW_AUDIOOUT_TEST_TM_HPCOMMON __BIT(24) +#define HW_AUDIOOUT_TEST_HP_I1_ADJ __BITS(23, 22) +#define HW_AUDIOOUT_TEST_HP_IALL_ADJ __BITS(21, 20) +#define HW_AUDIOOUT_TEST_RSRVD2 __BITS(19, 14) +#define HW_AUDIOOUT_TEST_VAG_CLASSA __BIT(13) +#define HW_AUDIOOUT_TEST_VAG_DOUBLE_I __BIT(12) +#define HW_AUDIOOUT_TEST_RSRVD1 __BITS(11, 4) +#define HW_AUDIOOUT_TEST_ADCTODAC_LOOP __BIT(3) +#define HW_AUDIOOUT_TEST_DAC_CLASSA __BIT(2) +#define HW_AUDIOOUT_TEST_DAC_DOUBLE_I __BIT(1) +#define HW_AUDIOOUT_TEST_DAC_DIS_RTZ __BIT(0) + +/* + * BIST Control and Status Register. + */ +#define HW_AUDIOOUT_BISTCTRL 0x0b0 +#define HW_AUDIOOUT_BISTCTRL_SET 0x0b4 +#define HW_AUDIOOUT_BISTCTRL_CLR 0x0b8 +#define HW_AUDIOOUT_BISTCTRL_TOG 0x0bC + +#define HW_AUDIOOUT_BISTCTRL_RSVD0 __BITS(31, 4) +#define HW_AUDIOOUT_BISTCTRL_FAIL __BIT(3) +#define HW_AUDIOOUT_BISTCTRL_PASS __BIT(2) +#define HW_AUDIOOUT_BISTCTRL_DONE __BIT(1) +#define HW_AUDIOOUT_BISTCTRL_START __BIT(0) + +/* + * Hardware BIST Status 0 Register. + */ +#define HW_AUDIOOUT_BISTSTAT0 0x0c0 +#define HW_AUDIOOUT_BISTSTAT0_SET 0x0c4 +#define HW_AUDIOOUT_BISTSTAT0_CLR 0x0c8 +#define HW_AUDIOOUT_BISTSTAT0_TOG 0x0cC + +#define HW_AUDIOOUT_BISTSTAT0_RSVD0 __BITS(31, 24) +#define HW_AUDIOOUT_BISTSTAT0_DATA __BITS(23, 0) + +/* + * Hardware AUDIOUT BIST Status 1 Register. + */ +#define HW_AUDIOOUT_BISTSTAT1 0x0d0 +#define HW_AUDIOOUT_BISTSTAT1_SET 0x0d4 +#define HW_AUDIOOUT_BISTSTAT1_CLR 0x0d8 +#define HW_AUDIOOUT_BISTSTAT1_TOG 0x0dC + +#define HW_AUDIOOUT_BISTSTAT1_RSVD1 __BITS(31, 29) +#define HW_AUDIOOUT_BISTSTAT1_STATE __BITS(28, 24) +#define HW_AUDIOOUT_BISTSTAT1_RSVD0 __BITS(23, 8) +#define HW_AUDIOOUT_BISTSTAT1_ADDR __BITS(7, 0) + +/* + * Analog Clock Control Register. + */ +#define HW_AUDIOOUT_ANACLKCTRL 0x0e0 +#define HW_AUDIOOUT_ANACLKCTRL_SET 0x0e4 +#define HW_AUDIOOUT_ANACLKCTRL_CLR 0x0e8 +#define HW_AUDIOOUT_ANACLKCTRL_TOG 0x0eC + +#define HW_AUDIOOUT_ANACLKCTRL_CLKGATE __BIT(31) +#define HW_AUDIOOUT_ANACLKCTRL_RSRVD3 __BITS(30, 5) +#define HW_AUDIOOUT_ANACLKCTRL_INVERT_DACCLK __BIT(4) +#define HW_AUDIOOUT_ANACLKCTRL_RSRVD2 __BIT(3) +#define HW_AUDIOOUT_ANACLKCTRL_DACDIV __BITS(2, 0) + +/* + * AUDIOOUT Write Data Register. + */ +#define HW_AUDIOOUT_DATA 0x0f0 +#define HW_AUDIOOUT_DATA_SET 0x0f4 +#define HW_AUDIOOUT_DATA_CLR 0x0f8 +#define HW_AUDIOOUT_DATA_TOG 0x0fC + +#define HW_AUDIOOUT_DATA_HIGH __BITS(31, 16) +#define HW_AUDIOOUT_DATA_LOW __BITS(15, 0) + +/* + * AUDIOOUT Speaker Control Register. + */ +#define HW_AUDIOOUT_SPEAKERCTRL 0x100 +#define HW_AUDIOOUT_SPEAKERCTRL_SET 0x104 +#define HW_AUDIOOUT_SPEAKERCTRL_CLR 0x108 +#define HW_AUDIOOUT_SPEAKERCTRL_TOG 0x10C + +#define HW_AUDIOOUT_SPEAKERCTRL_RSRVD2 __BITS(31, 25) +#define HW_AUDIOOUT_SPEAKERCTRL_MUTE __BIT(24) +#define HW_AUDIOOUT_SPEAKERCTRL_I1_ADJ __BITS(23, 22) +#define HW_AUDIOOUT_SPEAKERCTRL_IALL_ADJ __BITS(21, 20) +#define HW_AUDIOOUT_SPEAKERCTRL_RSRVD1 __BITS(19, 16) +#define HW_AUDIOOUT_SPEAKERCTRL_POSDRIVER __BITS(15, 14) +#define HW_AUDIOOUT_SPEAKERCTRL_NEGDRIVER __BITS(13, 12) +#define HW_AUDIOOUT_SPEAKERCTRL_RSRVD0 __BITS(11, 0) + +/* + * AUDIOOUT Version Register. + */ +#define HW_AUDIOOUT_VERSION 0x200 + +#define HW_AUDIOOUT_VERSION_MAJOR __BITS(31, 24) +#define HW_AUDIOOUT_VERSION_MINOR __BITS(23, 16) +#define HW_AUDIOOUT_VERSION_STEP __BITS(15, 0) + +#endif /* !_ARM_IMX_IMX23_AUDIOOUTREG_H_ */ diff --git a/sys/arch/arm/imx/imx23_digfiltvar.h b/sys/arch/arm/imx/imx23_digfiltvar.h new file mode 100644 index 000000000000..30b1c4186e1b --- /dev/null +++ b/sys/arch/arm/imx/imx23_digfiltvar.h @@ -0,0 +1,35 @@ +/* $Id: imx23_digfiltvar.h,v 1.1 2015/01/10 12:16:28 jmcneill Exp $ */ + +/* + * Copyright (c) 2014 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Petri Laakso. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ARM_IMX_IMX23_AUDIOOUTVAR_H_ +#define _ARM_IMX_IMX23_AUDIOOUTVAR_H_ + +#endif /* !_ARM_IMX_IMX23_AUDIOOUTVAR_H_ */ diff --git a/sys/arch/arm/imx/imx23_rtc.c b/sys/arch/arm/imx/imx23_rtc.c new file mode 100644 index 000000000000..35c5bb9f5a18 --- /dev/null +++ b/sys/arch/arm/imx/imx23_rtc.c @@ -0,0 +1,199 @@ +/* $Id: imx23_rtc.c,v 1.1 2015/01/10 12:16:28 jmcneill Exp $ */ + +/* +* Copyright (c) 2014 The NetBSD Foundation, Inc. +* All rights reserved. +* +* This code is derived from software contributed to The NetBSD Foundation +* by Petri Laakso. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct rtc_softc { + device_t sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_hdl; +} *rtc_softc_t; + +static int rtc_match(device_t, cfdata_t, void *); +static void rtc_attach(device_t, device_t, void *); +static int rtc_activate(device_t, enum devact); + +static void rtc_init(struct rtc_softc *); +static void rtc_reset(struct rtc_softc *); + +static rtc_softc_t _sc = NULL; + +CFATTACH_DECL3_NEW(rtc, + sizeof(struct rtc_softc), + rtc_match, + rtc_attach, + NULL, + rtc_activate, + NULL, + NULL, + 0 +); + +#define RTC_RD(sc, reg) \ + bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg)) +#define RTC_WR(sc, reg, val) \ + bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val)) + +#define RTC_SOFT_RST_LOOP 455 /* At least 1 us ... */ + +static int +rtc_match(device_t parent, cfdata_t match, void *aux) +{ + struct apb_attach_args *aa = aux; + + if ((aa->aa_addr == HW_RTC_BASE) && + (aa->aa_size == HW_RTC_BASE_SIZE)) + return 1; + + return 0; +} + +static void +rtc_attach(device_t parent, device_t self, void *aux) +{ + struct rtc_softc *sc = device_private(self); + struct apb_attach_args *aa = aux; + static int rtc_attached = 0; + + sc->sc_dev = self; + sc->sc_iot = aa->aa_iot; + + if (rtc_attached) { + aprint_error_dev(sc->sc_dev, "rtc is already attached\n"); + return; + } + + if (bus_space_map(sc->sc_iot, aa->aa_addr, aa->aa_size, 0, + &sc->sc_hdl)) + { + aprint_error_dev(sc->sc_dev, "Unable to map bus space\n"); + return; + } + + + rtc_init(sc); + rtc_reset(sc); + + aprint_normal("\n"); + + rtc_attached = 1; + + return; +} + +static int +rtc_activate(device_t self, enum devact act) +{ + + return EOPNOTSUPP; +} + +static void +rtc_init(struct rtc_softc *sc) +{ + _sc = sc; + return; +} + +/* + * Remove pulldown resistors on the headphone outputs. + */ +void +rtc_release_gnd(int val) +{ + struct rtc_softc *sc = _sc; + + if (sc == NULL) { + aprint_error("rtc is not initalized"); + return; + } + if(val) + RTC_WR(sc, HW_RTC_PERSISTENT0_SET, (1<<19)); + else + RTC_WR(sc, HW_RTC_PERSISTENT0_CLR, (1<<19)); + + return; +} + +/* + * Reset the RTC block. + * + * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block" + */ +static void +rtc_reset(struct rtc_softc *sc) +{ + unsigned int loop; + + /* Prepare for soft-reset by making sure that SFTRST is not currently + * asserted. Also clear CLKGATE so we can wait for its assertion below. + */ + RTC_WR(sc, HW_RTC_CTRL_CLR, HW_RTC_CTRL_SFTRST); + + /* Wait at least a microsecond for SFTRST to deassert. */ + loop = 0; + while ((RTC_RD(sc, HW_RTC_CTRL) & HW_RTC_CTRL_SFTRST) || + (loop < RTC_SOFT_RST_LOOP)) + loop++; + + /* Clear CLKGATE so we can wait for its assertion below. */ + RTC_WR(sc, HW_RTC_CTRL_CLR, HW_RTC_CTRL_CLKGATE); + + /* Soft-reset the block. */ + RTC_WR(sc, HW_RTC_CTRL_SET, HW_RTC_CTRL_SFTRST); + + /* Wait until clock is in the gated state. */ + while (!(RTC_RD(sc, HW_RTC_CTRL) & HW_RTC_CTRL_CLKGATE)); + + /* Bring block out of reset. */ + RTC_WR(sc, HW_RTC_CTRL_CLR, HW_RTC_CTRL_SFTRST); + + loop = 0; + while ((RTC_RD(sc, HW_RTC_CTRL) & HW_RTC_CTRL_SFTRST) || + (loop < RTC_SOFT_RST_LOOP)) + loop++; + + RTC_WR(sc, HW_RTC_CTRL_CLR, HW_RTC_CTRL_CLKGATE); + + /* Wait until clock is in the NON-gated state. */ + while (RTC_RD(sc, HW_RTC_CTRL) & HW_RTC_CTRL_CLKGATE); + + return; +} diff --git a/sys/arch/arm/imx/imx23_rtcreg.h b/sys/arch/arm/imx/imx23_rtcreg.h index 09f42348ee2d..55f4bf5d7ea4 100644 --- a/sys/arch/arm/imx/imx23_rtcreg.h +++ b/sys/arch/arm/imx/imx23_rtcreg.h @@ -1,4 +1,4 @@ -/* $Id: imx23_rtcreg.h,v 1.1 2012/11/20 19:06:13 jkunz Exp $ */ +/* $Id: imx23_rtcreg.h,v 1.2 2015/01/10 12:16:28 jmcneill Exp $ */ /* * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -35,6 +35,7 @@ #include #define HW_RTC_BASE 0x8005C000 +#define HW_RTC_BASE_SIZE 0x2000 /* * Real-Time Clock Control Register. diff --git a/sys/arch/arm/imx/imx23_rtcvar.h b/sys/arch/arm/imx/imx23_rtcvar.h new file mode 100644 index 000000000000..b465d8922ee5 --- /dev/null +++ b/sys/arch/arm/imx/imx23_rtcvar.h @@ -0,0 +1,37 @@ +/* $Id: imx23_rtcvar.h,v 1.1 2015/01/10 12:16:28 jmcneill Exp $ */ + +/* + * Copyright (c) 2014 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Petri Laakso. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ARM_IMX_IMX23_RTCVAR_H_ +#define _ARM_IMX_IMX23_RTCVAR_H_ + +void rtc_release_gnd(int); + +#endif /* !_ARM_IMX_IMX23_RTCVAR_H_ */