/* * Copyright (c) 2014 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - 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. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /** @addtogroup hdaudio * @{ */ /** @file High Definition Audio stream */ #include #include #include #include #include #include #include #include #include "hdactl.h" #include "hdaudio.h" #include "regif.h" #include "spec/bdl.h" #include "stream.h" static int hda_stream_buffers_alloc(hda_stream_t *stream) { void *bdl; void *buffer; uintptr_t buffer_phys; size_t i; // size_t j, k; int rc; stream->nbuffers = 4; stream->bufsize = 16384; /* * BDL must be aligned to 128 bytes. If 64OK is not set, * it must be within the 32-bit address space. */ bdl = AS_AREA_ANY; rc = dmamem_map_anonymous(stream->nbuffers * sizeof(hda_buffer_desc_t), stream->hda->ctl->ok64bit ? 0 : DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &stream->bdl_phys, &bdl); if (rc != EOK) goto error; stream->bdl = bdl; /* Allocate arrays of buffer pointers */ stream->buf = calloc(stream->nbuffers, sizeof(void *)); if (stream->buf == NULL) goto error; stream->buf_phys = calloc(stream->nbuffers, sizeof(uintptr_t)); if (stream->buf_phys == NULL) goto error; /* Allocate buffers */ /* for (i = 0; i < stream->nbuffers; i++) { buffer = AS_AREA_ANY; rc = dmamem_map_anonymous(stream->bufsize, stream->hda->ctl->ok64bit ? 0 : DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &buffer_phys, &buffer); if (rc != EOK) goto error; ddf_msg(LVL_NOTE, "Stream buf phys=0x%llx virt=%p", (unsigned long long)buffer_phys, buffer); stream->buf[i] = buffer; stream->buf_phys[i] = buffer_phys; k = 0; for (j = 0; j < stream->bufsize / 2; j++) { int16_t *bp = stream->buf[i]; bp[j] = (k > 128) ? -100 : 100; ++k; if (k >= 256) k = 0; } } */ // async_usleep(1000*1000); /* audio_pcm_iface requires a single contiguous buffer */ buffer = AS_AREA_ANY; rc = dmamem_map_anonymous(stream->bufsize * stream->nbuffers, stream->hda->ctl->ok64bit ? 0 : DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &buffer_phys, &buffer); if (rc != EOK) { ddf_msg(LVL_NOTE, "dmamem_map_anon -> %d", rc); goto error; } for (i = 0; i < stream->nbuffers; i++) { stream->buf[i] = buffer + i * stream->bufsize; stream->buf_phys[i] = buffer_phys + i * stream->bufsize; ddf_msg(LVL_NOTE, "Stream buf phys=0x%llx virt=%p", (long long unsigned)(uintptr_t)stream->buf[i], (void *)stream->buf_phys[i]); /* k = 0; for (j = 0; j < stream->bufsize / 2; j++) { int16_t *bp = stream->buf[i]; bp[j] = (k > 128) ? -10000 : 10000; ++k; if (k >= 256) k = 0; } */ } /* Fill in BDL */ for (i = 0; i < stream->nbuffers; i++) { stream->bdl[i].address = host2uint64_t_le(stream->buf_phys[i]); stream->bdl[i].length = host2uint32_t_le(stream->bufsize); stream->bdl[i].flags = BIT_V(uint32_t, bdf_ioc); } return EOK; error: return ENOMEM; } static void hda_stream_desc_configure(hda_stream_t *stream) { hda_sdesc_regs_t *sdregs; uint8_t ctl1; uint8_t ctl3; ctl3 = (stream->sid << 4); ctl1 = 0x4; sdregs = &stream->hda->regs->sdesc[stream->sdid]; hda_reg8_write(&sdregs->ctl3, ctl3); hda_reg8_write(&sdregs->ctl1, ctl1); hda_reg32_write(&sdregs->cbl, stream->nbuffers * stream->bufsize); hda_reg16_write(&sdregs->lvi, stream->nbuffers - 1); hda_reg16_write(&sdregs->fmt, stream->fmt); hda_reg32_write(&sdregs->bdpl, LOWER32(stream->bdl_phys)); hda_reg32_write(&sdregs->bdpu, UPPER32(stream->bdl_phys)); } static void hda_stream_set_run(hda_stream_t *stream, bool run) { uint32_t ctl; hda_sdesc_regs_t *sdregs; sdregs = &stream->hda->regs->sdesc[stream->sdid]; ctl = hda_reg8_read(&sdregs->ctl1); if (run) ctl = ctl | BIT_V(uint8_t, sdctl1_run); else ctl = ctl & ~BIT_V(uint8_t, sdctl1_run); hda_reg8_write(&sdregs->ctl1, ctl); } static void hda_stream_reset_noinit(hda_stream_t *stream) { uint32_t ctl; hda_sdesc_regs_t *sdregs; sdregs = &stream->hda->regs->sdesc[stream->sdid]; ctl = hda_reg8_read(&sdregs->ctl1); ctl = ctl | BIT_V(uint8_t, sdctl1_srst); hda_reg8_write(&sdregs->ctl1, ctl); async_usleep(100 * 1000); ctl = hda_reg8_read(&sdregs->ctl1); ctl = ctl & ~BIT_V(uint8_t, sdctl1_srst); hda_reg8_write(&sdregs->ctl1, ctl); async_usleep(100 * 1000); } hda_stream_t *hda_stream_create(hda_t *hda, hda_stream_dir_t dir, uint32_t fmt) { hda_stream_t *stream; int rc; stream = calloc(1, sizeof(hda_stream_t)); if (stream == NULL) return NULL; stream->hda = hda; stream->dir = dir; stream->sid = 1; /* XXX Allocate this */ stream->sdid = hda->ctl->iss; /* XXX Allocate - First output SDESC */ stream->fmt = fmt; ddf_msg(LVL_NOTE, "snum=%d sdidx=%d", stream->sid, stream->sdid); ddf_msg(LVL_NOTE, "Allocate buffers"); rc = hda_stream_buffers_alloc(stream); if (rc != EOK) goto error; ddf_msg(LVL_NOTE, "Configure stream descriptor"); hda_stream_desc_configure(stream); return stream; error: return NULL; } void hda_stream_destroy(hda_stream_t *stream) { ddf_msg(LVL_NOTE, "hda_stream_destroy()"); hda_stream_reset_noinit(stream); free(stream); } void hda_stream_start(hda_stream_t *stream) { ddf_msg(LVL_NOTE, "hda_stream_start()"); hda_stream_set_run(stream, true); } void hda_stream_stop(hda_stream_t *stream) { ddf_msg(LVL_NOTE, "hda_stream_stop()"); hda_stream_set_run(stream, false); } void hda_stream_reset(hda_stream_t *stream) { ddf_msg(LVL_NOTE, "hda_stream_reset()"); hda_stream_reset_noinit(stream); hda_stream_desc_configure(stream); } /** @} */