/*- * Copyright (c) 2012 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Paul Fleischer * * 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 /*#define AUDIO_MINI2440_DEBUG*/ #ifdef AUDIO_MINI2440_DEBUG #define DPRINTF(x) do {printf x; } while (/*CONSTCOND*/0) #else #define DPRINTF(s) do {} while (/*CONSTCOND*/0) #endif struct uda_softc { device_t sc_dev; kmutex_t sc_lock; kmutex_t sc_intr_lock; struct uda1341_softc sc_uda1341; s3c2440_i2s_buf_t sc_play_buf; s3c2440_i2s_buf_t sc_rec_buf; void *sc_i2s_handle; }; int uda_ssio_open(void *, int); void uda_ssio_close(void *); int uda_ssio_query_format(void *, audio_format_query_t *); int uda_ssio_set_format(void *, int, const audio_params_t *, const audio_params_t *, audio_filter_reg_t *, audio_filter_reg_t *); int uda_ssio_start_output(void *, void *, int, void (*)(void *), void *); int uda_ssio_start_input(void *, void *, int, void (*)(void *), void *); int uda_ssio_halt_output(void *); int uda_ssio_halt_input(void *); int uda_ssio_getdev(void *, struct audio_device *ret); void* uda_ssio_allocm(void *, int, size_t); void uda_ssio_freem(void *, void *, size_t); size_t uda_ssio_round_buffersize(void *, int, size_t); int uda_ssio_get_props(void *); void uda_ssio_get_locks(void *, kmutex_t**, kmutex_t**); struct audio_hw_if uda1341_hw_if = { .open = uda_ssio_open, .close = uda_ssio_close, .query_format = uda_ssio_query_format, .set_format = uda_ssio_set_format, .start_output = uda_ssio_start_output, .start_input = uda_ssio_start_input, .halt_output = uda_ssio_halt_output, .halt_input = uda_ssio_halt_input, .getdev = uda_ssio_getdev, .set_port = uda1341_set_port, .get_port = uda1341_get_port, .query_devinfo = uda1341_query_devinfo, .allocm = uda_ssio_allocm, .freem = uda_ssio_freem, .round_buffersize = uda_ssio_round_buffersize, .get_props = uda_ssio_get_props, .get_locks = uda_ssio_get_locks }; static struct audio_device uda1341_device = { "MINI2240-UDA1341", "0.1", "uda_ssio" }; static const struct audio_format uda_ssio_formats[] = { { .mode = AUMODE_PLAY | AUMODE_RECORD, .encoding = AUDIO_ENCODING_SLINEAR_LE, .validbits = 16, .precision = 16, .channels = 2, .channel_mask = AUFMT_STEREO, .frequency_type = 6, .frequency = { 8000, 11025, 22050, 32000, 44100, 48000 }, } }; #define UDA_SSIO_NFORMATS __arraycount(uda_ssio_formats) void uda_ssio_l3_write(void *,int mode, int value); int uda_ssio_match(device_t, cfdata_t, void*); void uda_ssio_attach(device_t, device_t, void*); CFATTACH_DECL_NEW(udassio, sizeof(struct uda_softc), uda_ssio_match, uda_ssio_attach, NULL, NULL); int uda_ssio_match(device_t parent, cfdata_t match, void *aux) { DPRINTF(("%s\n", __func__)); /* Not quite sure how we can detect the UDA1341 chip */ return 1; } void uda_ssio_attach(device_t parent, device_t self, void *aux) { /* struct s3c2xx0_attach_args *sa = aux;*/ struct uda_softc *sc = device_private(self); struct s3c2xx0_softc *s3sc = s3c2xx0_softc; /* Shortcut */ struct s3c2440_i2s_attach_args *aa = aux; uint32_t reg; sc->sc_dev = self; sc->sc_play_buf = NULL; sc->sc_i2s_handle = aa->i2sa_handle; mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); s3c2440_i2s_set_intr_lock(aa->i2sa_handle, &sc->sc_intr_lock); /* arch/arm/s3c2xx0/s3c2440.c initializes the I2S subsystem for us */ /* Setup GPIO pins to output for L3 communication. GPB3 (L3DATA) will have to be switched to input when reading from the L3 bus. GPB2 - L3MODE GPB3 - L3DATA GPB4 - L3CLOCK TODO: Make this configurable */ reg = bus_space_read_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBCON); reg = GPIO_SET_FUNC(reg, 2, 1); reg = GPIO_SET_FUNC(reg, 3, 1); reg = GPIO_SET_FUNC(reg, 4, 1); bus_space_write_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBCON, reg); reg = bus_space_read_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT); reg = GPIO_SET_DATA(reg, 4, 1); reg = GPIO_SET_DATA(reg, 3, 0); reg = GPIO_SET_DATA(reg, 2, 1); bus_space_write_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT, reg); printf("\n"); /* uda1341_attach resets the uda1341 sc, so it has to be called before attributes are set on the sc.*/ uda1341_attach(&sc->sc_uda1341); /* Configure the UDA1341 Codec */ sc->sc_uda1341.parent = sc; sc->sc_uda1341.sc_l3_write = uda_ssio_l3_write; sc->sc_uda1341.sc_bus_format = UDA1341_BUS_MSB; /* Configure I2S controller */ s3c2440_i2s_set_bus_format(sc->sc_i2s_handle, S3C2440_I2S_BUS_MSB); // Attach audio_attach_mi(&uda1341_hw_if, &sc->sc_uda1341, self); } int uda_ssio_open(void *handle, int flags) { int retval; DPRINTF(("%s\n", __func__)); /* We only support write operations */ if (!(flags & FREAD) && !(flags & FWRITE)) return EINVAL; /* We can't do much more at this point than to ask the UDA1341 codec to initialize itself (for an unknown system clock) */ retval = uda1341_open(handle, flags); if (retval != 0) { return retval; } return 0; /* SUCCESS */ } void uda_ssio_close(void *handle) { DPRINTF(("%s\n", __func__)); uda1341_close(handle); } int uda_ssio_query_format(void *handle, audio_format_query_t *afp) { return audio_query_format(uda_ssio_formats, UDA_SSIO_NFORMATS, afp); } int uda_ssio_set_format(void *handle, int setmode, const audio_params_t *play, const audio_params_t *rec, audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) { struct uda1341_softc *uc = handle; struct uda_softc *sc = uc->parent; int retval; DPRINTF(("%s: setmode: %d\n", __func__, setmode)); /* *play and *rec are the identical because !AUDIO_PROP_INDEPENDENT. */ DPRINTF(("%s: %dHz, encoding: %d, precision: %d, channels: %d\n", __func__, play->sample_rate, play->encoding, play->precision, play->channels)); if (setmode == AUMODE_PLAY) { s3c2440_i2s_set_direction(sc->sc_i2s_handle, S3C2440_I2S_TRANSMIT); } else { s3c2440_i2s_set_direction(sc->sc_i2s_handle, S3C2440_I2S_RECEIVE); } s3c2440_i2s_set_sample_rate(sc->sc_i2s_handle, play->sample_rate); s3c2440_i2s_set_sample_width(sc->sc_i2s_handle, 16); /* It is vital that sc_system_clock is set PRIOR to calling uda1341_set_format. */ switch (s3c2440_i2s_get_master_clock(sc->sc_i2s_handle)) { case 384: uc->sc_system_clock = UDA1341_CLOCK_384; break; case 256: uc->sc_system_clock = UDA1341_CLOCK_256; break; default: return EINVAL; } retval = uda1341_set_format(handle, setmode, play, rec, pfil, rfil); if (retval != 0) { return retval; } /* Setup and enable I2S controller */ retval = s3c2440_i2s_commit(sc->sc_i2s_handle); if (retval != 0) { printf("Failed to setup I2S controller\n"); return retval; } return 0; } int uda_ssio_start_output(void *handle, void *block, int bsize, void (*intr)(void *), void *intrarg) { struct uda1341_softc *uc = handle; struct uda_softc *sc = uc->parent; return s3c2440_i2s_output(sc->sc_play_buf, block, bsize, intr, intrarg); } int uda_ssio_start_input(void *handle, void *block, int bsize, void (*intr)(void *), void *intrarg) { struct uda1341_softc *uc = handle; struct uda_softc *sc = uc->parent; return s3c2440_i2s_input(sc->sc_rec_buf, block, bsize, intr, intrarg); } int uda_ssio_halt_output(void *handle) { struct uda1341_softc *uc = handle; struct uda_softc *sc = uc->parent; return s3c2440_i2s_halt_output(sc->sc_play_buf); } int uda_ssio_halt_input(void *handle) { DPRINTF(("%s\n", __func__)); return 0; } int uda_ssio_getdev(void *handle, struct audio_device *ret) { *ret = uda1341_device; return 0; } void * uda_ssio_allocm(void *handle, int direction, size_t size) { struct uda1341_softc *uc = handle; struct uda_softc *sc = uc->parent; void *retval = NULL; DPRINTF(("%s\n", __func__)); if (direction == AUMODE_PLAY ) { if (sc->sc_play_buf != NULL) return NULL; s3c2440_i2s_alloc(sc->sc_i2s_handle, direction, size, 0x00, &sc->sc_play_buf); DPRINTF(("%s: addr of ring buffer: %p\n", __func__, sc->sc_play_buf->i2b_addr)); retval = sc->sc_play_buf->i2b_addr; } else if (direction == AUMODE_RECORD) { if (sc->sc_rec_buf != NULL) return NULL; s3c2440_i2s_alloc(sc->sc_i2s_handle, direction, size, 0x00, &sc->sc_rec_buf); DPRINTF(("%s: addr of ring buffer: %p\n", __func__, sc->sc_rec_buf->i2b_addr)); retval = sc->sc_rec_buf->i2b_addr; } DPRINTF(("buffer: %p", retval)); return retval; } void uda_ssio_freem(void *handle, void *ptr, size_t size) { struct uda1341_softc *uc = handle; struct uda_softc *sc = uc->parent; DPRINTF(("%s\n", __func__)); if (ptr == sc->sc_play_buf->i2b_addr) s3c2440_i2s_free(sc->sc_play_buf); else if (ptr == sc->sc_rec_buf->i2b_addr) s3c2440_i2s_free(sc->sc_rec_buf); } size_t uda_ssio_round_buffersize(void *handle, int direction, size_t bufsize) { DPRINTF(("%s: %d\n", __func__, (int)bufsize)); return bufsize; } int uda_ssio_get_props(void *handle) { return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE; } void uda_ssio_get_locks(void *handle, kmutex_t **intr, kmutex_t **thread) { struct uda1341_softc *uc = handle; struct uda_softc *sc = uc->parent; //struct uda_softc *sc = handle; *intr = &sc->sc_intr_lock; *thread = &sc->sc_lock; } void uda_ssio_l3_write(void *cookie, int mode, int value) { struct s3c2xx0_softc *s3sc = s3c2xx0_softc; /* Shortcut */ uint32_t reg; /* GPB2: L3MODE GPB3: L2DATA GPB4: L3CLOCK */ #define L3MODE 2 #define L3DATA 3 #define L3CLOCK 4 #define READ_GPIO() bus_space_read_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT) #define WRITE_GPIO(val) bus_space_write_4(s3sc->sc_iot, s3sc->sc_gpio_ioh, GPIO_PBDAT, val) #define DELAY_TIME 1 reg = READ_GPIO(); reg = GPIO_SET_DATA(reg, L3CLOCK, 1); reg = GPIO_SET_DATA(reg, L3MODE, mode); reg = GPIO_SET_DATA(reg, L3DATA, 0); WRITE_GPIO(reg); if (mode == 1 ) { reg = READ_GPIO(); reg = GPIO_SET_DATA(reg, L3MODE, 1); WRITE_GPIO(reg); } DELAY(1); /* L3MODE setup time: min 190ns */ for(int i = 0; i<8; i++) { char bval = (value >> i) & 0x1; reg = READ_GPIO(); reg = GPIO_SET_DATA(reg, L3CLOCK, 0); reg = GPIO_SET_DATA(reg, L3DATA, bval); WRITE_GPIO(reg); DELAY(DELAY_TIME); reg = READ_GPIO(); reg = GPIO_SET_DATA(reg, L3CLOCK, 1); reg = GPIO_SET_DATA(reg, L3DATA, bval); WRITE_GPIO(reg); DELAY(DELAY_TIME); } reg = READ_GPIO(); reg = GPIO_SET_DATA(reg, L3MODE, 1); reg = GPIO_SET_DATA(reg, L3CLOCK, 1); reg = GPIO_SET_DATA(reg, L3DATA, 0); WRITE_GPIO(reg); #undef L3MODE #undef L3DATA #undef L3CLOCK #undef DELAY_TIME #undef READ_GPIO #undef WRITE_GPIO }