#include <sys/systm.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/note.h>
#include <sys/audio/audio_driver.h>
#include "audio_4231.h"
static ddi_dma_attr_t eb2_dma_attr = {
DMA_ATTR_V0,
0x0000000000000000LL,
0x00000000ffffffffLL,
0x0000000000ffffffLL,
0x0000000000000001LL,
0x00000074,
0x00000001,
0x000000000000ffffLL,
0x000000000000ffffLL,
0x00000001,
0x00000001,
0
};
static ddi_device_acc_attr_t codec_attr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_BE_ACC,
DDI_STRICTORDER_ACC
};
static ddi_device_acc_attr_t eb2_attr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC
};
static int eb2_map_regs(CS_state_t *);
static void eb2_unmap_regs(CS_state_t *);
static void eb2_reset(CS_state_t *);
static int eb2_start_engine(CS_engine_t *);
static void eb2_stop_engine(CS_engine_t *);
static void eb2_power(CS_state_t *, int);
static void eb2_reload(CS_engine_t *);
static uint32_t eb2_addr(CS_engine_t *);
cs4231_dma_ops_t cs4231_eb2dma_ops = {
"EB2 DMA controller",
&eb2_dma_attr,
eb2_map_regs,
eb2_unmap_regs,
eb2_reset,
eb2_start_engine,
eb2_stop_engine,
eb2_power,
eb2_reload,
eb2_addr,
};
static int
eb2_map_regs(CS_state_t *state)
{
dev_info_t *dip = state->cs_dip;
if (ddi_regs_map_setup(dip, 0, (caddr_t *)&state->cs_regs, 0,
sizeof (cs4231_pioregs_t), &codec_attr, &CODEC_HANDLE) !=
DDI_SUCCESS) {
audio_dev_warn(state->cs_adev, "failed mapping codec regs");
goto error;
}
if (ddi_regs_map_setup(dip, 1, (caddr_t *)&state->cs_eb2_regs.play, 0,
sizeof (cs4231_eb2regs_t), &eb2_attr, &EB2_PLAY_HNDL) !=
DDI_SUCCESS) {
audio_dev_warn(state->cs_adev, "failed mapping play regs");
goto error;
}
state->cs_engines[CS4231_PLAY]->ce_regsh = EB2_PLAY_HNDL;
state->cs_engines[CS4231_PLAY]->ce_eb2regs = state->cs_eb2_regs.play;
if (ddi_regs_map_setup(dip, 2, (caddr_t *)&state->cs_eb2_regs.record, 0,
sizeof (cs4231_eb2regs_t), &eb2_attr, &EB2_REC_HNDL) !=
DDI_SUCCESS) {
audio_dev_warn(state->cs_adev, "failed mapping rec regs");
goto error;
}
state->cs_engines[CS4231_REC]->ce_regsh = EB2_REC_HNDL;
state->cs_engines[CS4231_REC]->ce_eb2regs = state->cs_eb2_regs.record;
if (ddi_regs_map_setup(dip, 3, (caddr_t *)&state->cs_eb2_regs.auxio, 0,
sizeof (uint_t), &eb2_attr, &EB2_AUXIO_HNDL) != DDI_SUCCESS) {
audio_dev_warn(state->cs_adev, "failed mapping auxio reg");
goto error;
}
ddi_put32(EB2_PLAY_HNDL, &EB2_PLAY_CSR, EB2_PCLEAR_RESET_VALUE);
ddi_put32(EB2_REC_HNDL, &EB2_REC_CSR, EB2_RCLEAR_RESET_VALUE);
return (DDI_SUCCESS);
error:
eb2_unmap_regs(state);
return (DDI_FAILURE);
}
static void
eb2_unmap_regs(CS_state_t *state)
{
if (CODEC_HANDLE)
ddi_regs_map_free(&CODEC_HANDLE);
if (EB2_PLAY_HNDL)
ddi_regs_map_free(&EB2_PLAY_HNDL);
if (EB2_REC_HNDL)
ddi_regs_map_free(&EB2_REC_HNDL);
if (EB2_AUXIO_HNDL)
ddi_regs_map_free(&EB2_AUXIO_HNDL);
}
static void
eb2_reset(CS_state_t *state)
{
ddi_acc_handle_t phandle = EB2_PLAY_HNDL;
ddi_acc_handle_t rhandle = EB2_REC_HNDL;
uint_t reg;
int x;
ddi_put32(phandle, &EB2_PLAY_CSR, EB2_RESET);
reg = ddi_get32(phandle, &EB2_PLAY_CSR);
for (x = 0; (reg & EB2_FIFO_DRAIN) && x < CS4231_TIMEOUT; x++) {
drv_usecwait(1);
reg = ddi_get32(phandle, &EB2_PLAY_CSR);
}
ddi_put32(phandle, &EB2_PLAY_CSR, EB2_PCLEAR_RESET_VALUE);
ddi_put32(rhandle, &EB2_REC_CSR, EB2_RESET);
reg = ddi_get32(rhandle, &EB2_REC_CSR);
for (x = 0; (reg & EB2_FIFO_DRAIN) && x < CS4231_TIMEOUT; x++) {
drv_usecwait(1);
reg = ddi_get32(rhandle, &EB2_REC_CSR);
}
ddi_put32(rhandle, &EB2_REC_CSR, EB2_RCLEAR_RESET_VALUE);
}
static int
eb2_start_engine(CS_engine_t *eng)
{
CS_state_t *state = eng->ce_state;
ddi_acc_handle_t handle = eng->ce_regsh;
cs4231_eb2regs_t *regs = eng->ce_eb2regs;
uint_t csr;
int x;
uint32_t reset;
uint32_t enable;
if (eng->ce_num == CS4231_PLAY) {
reset = EB2_PCLEAR_RESET_VALUE;
enable = EB2_PLAY_ENABLE;
} else {
reset = EB2_RCLEAR_RESET_VALUE;
enable = EB2_REC_ENABLE;
}
ASSERT(mutex_owned(&state->cs_lock));
OR_SET_WORD(handle, ®s->eb2csr, EB2_RESET);
csr = ddi_get32(handle, ®s->eb2csr);
for (x = 0; (csr & EB2_FIFO_DRAIN) && x < CS4231_TIMEOUT; x++) {
drv_usecwait(1);
csr = ddi_get32(handle, ®s->eb2csr);
}
if (x >= CS4231_TIMEOUT) {
audio_dev_warn(state->cs_adev,
"timeout waiting for engine, not started!");
return (DDI_FAILURE);
}
AND_SET_WORD(handle, ®s->eb2csr, ~(EB2_RESET|EB2_EN_DMA));
OR_SET_WORD(handle, ®s->eb2csr, reset);
eb2_reload(eng);
OR_SET_WORD(handle, ®s->eb2csr, enable);
eb2_reload(eng);
return (DDI_SUCCESS);
}
static void
eb2_stop_engine(CS_engine_t *eng)
{
ddi_acc_handle_t handle = eng->ce_regsh;
cs4231_eb2regs_t *regs = eng->ce_eb2regs;
uint_t csr;
AND_SET_WORD(handle, ®s->eb2csr, ~(EB2_EN_DMA | EB2_INT_EN));
csr = ddi_get32(handle, ®s->eb2csr);
for (int x = 0; (csr & EB2_CYC_PENDING) && x < CS4231_TIMEOUT; x++) {
drv_usecwait(1);
csr = ddi_get32(handle, ®s->eb2csr);
}
OR_SET_WORD(handle, ®s->eb2csr, EB2_RESET | EB2_TC);
csr = ddi_get32(handle, ®s->eb2csr);
for (int x = 0; (csr & EB2_FIFO_DRAIN) && x < CS4231_TIMEOUT; x++) {
drv_usecwait(1);
csr = ddi_get32(handle, ®s->eb2csr);
}
AND_SET_WORD(handle, ®s->eb2csr, ~(EB2_RESET|EB2_EN_DMA));
}
static void
eb2_power(CS_state_t *state, int level)
{
ddi_acc_handle_t xhandle = EB2_AUXIO_HNDL;
if (level == CS4231_PWR_ON) {
AND_SET_WORD(xhandle, EB2_AUXIO_REG, ~EB2_AUXIO_COD_PDWN);
} else {
OR_SET_WORD(xhandle, EB2_AUXIO_REG, EB2_AUXIO_COD_PDWN);
}
}
static void
eb2_reload(CS_engine_t *eng)
{
ddi_acc_handle_t handle = eng->ce_regsh;
cs4231_eb2regs_t *regs = eng->ce_eb2regs;
if ((ddi_get32(handle, ®s->eb2csr) & EB2_NA_LOADED)) {
return;
}
ddi_put32(handle, ®s->eb2bcr, CS4231_FRAGSZ);
ddi_put32(handle, ®s->eb2acr,
eng->ce_paddr + (CS4231_FRAGSZ * eng->ce_curidx));
eng->ce_curidx++;
eng->ce_curidx %= CS4231_NFRAGS;
}
static uint32_t
eb2_addr(CS_engine_t *eng)
{
ddi_acc_handle_t handle = eng->ce_regsh;
cs4231_eb2regs_t *regs = eng->ce_eb2regs;
return (ddi_get32(handle, ®s->eb2acr));
}