123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- /*
- * File : drv_pl041.c
- * This file is part of RT-Thread RTOS
- * COPYRIGHT (C) 2017, RT-Thread Development Team
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Change Logs:
- * Date Author Notes
- * 2018-05-25 RT-Thread the first version
- */
- #include <rtthread.h>
- #include <rthw.h>
- #include "drv_pl041.h"
- #include "drv_ac97.h"
- #include "realview.h"
- #define DBG_ENABLE
- #define DBG_SECTION_NAME "[PL041]"
- // #define DBG_LEVEL DBG_LOG
- // #define DBG_LEVEL DBG_INFO
- #define DBG_LEVEL DBG_WARNING
- // #define DBG_LEVEL DBG_ERROR
- #define DBG_COLOR
- #include <rtdbg.h>
- #define FRAME_PERIOD_US (50)
- #define PL041_CHANNLE_NUM (4)
- #define PL041_READ(_a) (*(volatile rt_uint32_t *)(_a))
- #define PL041_WRITE(_a, _v) (*(volatile rt_uint32_t *)(_a) = (_v))
- struct pl041_irq_def
- {
- pl041_irq_fun_t fun;
- void *user_data;
- };
- static struct pl041_irq_def irq_tbl[PL041_CHANNLE_NUM];
- static void aaci_pl041_delay(rt_uint32_t us)
- {
- volatile int i;
- for (i = us * 10; i != 0; i--);
- }
- static void aaci_ac97_select_codec(void)
- {
- rt_uint32_t v, maincr;
- maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
- AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
- v = PL041_READ(&PL041->slfr);
- if (v & AACI_SLFR_2RXV)
- {
- PL041_READ(&PL041->sl2rx);
- }
- if (v & AACI_SLFR_1RXV)
- {
- PL041_READ(&PL041->sl1rx);
- }
- if (maincr != PL041_READ(&PL041->maincr))
- {
- PL041_WRITE(&PL041->maincr, maincr);
- aaci_pl041_delay(1);
- }
- }
- void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val)
- {
- rt_uint32_t v, timeout;
- aaci_ac97_select_codec();
- PL041_WRITE(&PL041->sl2tx, val << 4);
- PL041_WRITE(&PL041->sl1tx, reg << 12);
- aaci_pl041_delay(FRAME_PERIOD_US);
- timeout = FRAME_PERIOD_US * 8;
- do
- {
- aaci_pl041_delay(1);
- v = PL041_READ(&PL041->slfr);
- }
- while ((v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB)) && --timeout);
- if (v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB))
- {
- dbg_log(DBG_ERROR, "timeout waiting for write to complete\n");
- }
- }
- rt_uint16_t aaci_ac97_read(rt_uint16_t reg)
- {
- rt_uint32_t v, timeout, retries = 10;
- aaci_ac97_select_codec();
- PL041_WRITE(&PL041->sl1tx, (reg << 12) | (1 << 19));
- aaci_pl041_delay(FRAME_PERIOD_US);
- timeout = FRAME_PERIOD_US * 8;
- do
- {
- aaci_pl041_delay(1);
- v = PL041_READ(&PL041->slfr);
- }
- while ((v & AACI_SLFR_1TXB) && --timeout);
- if (v & AACI_SLFR_1TXB)
- {
- dbg_log(DBG_ERROR, "timeout on slot 1 TX busy\n");
- v = ~0x0;
- return v;
- }
- aaci_pl041_delay(FRAME_PERIOD_US);
- timeout = FRAME_PERIOD_US * 8;
- do
- {
- aaci_pl041_delay(1);
- v = PL041_READ(&PL041->slfr) & (AACI_SLFR_1RXV | AACI_SLFR_2RXV);
- }
- while ((v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV)) && --timeout);
- if (v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV))
- {
- dbg_log(DBG_ERROR, "timeout on RX valid\n");
- v = ~0x0;
- return v;
- }
- do
- {
- v = PL041_READ(&PL041->sl1rx) >> 12;
- if (v == reg)
- {
- v = PL041_READ(&PL041->sl2rx) >> 4;
- break;
- }
- else if (--retries)
- {
- dbg_log(DBG_ERROR, "ac97 read back fail. retry\n");
- continue;
- }
- else
- {
- dbg_log(DBG_ERROR, "wrong ac97 register read back (%x != %x)\n", v, reg);
- v = ~0x0;
- }
- }
- while (retries);
- return v;
- }
- int aaci_pl041_channle_disable(int channle)
- {
- rt_uint32_t v;
- void *p_rx, *p_tx;
- p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
- p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
- v = PL041_READ(p_rx);
- v &= ~AACI_CR_EN;
- PL041_WRITE(p_rx, v);
- v = PL041_READ(p_tx);
- v &= ~AACI_CR_EN;
- PL041_WRITE(p_tx, v);
- return 0;
- }
- int aaci_pl041_channle_enable(int channle)
- {
- rt_uint32_t v;
- void *p_rx, *p_tx;
- p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
- p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
- v = PL041_READ(p_rx);
- v |= AACI_CR_EN;
- PL041_WRITE(p_rx, v);
- v = PL041_READ(p_tx);
- v |= AACI_CR_EN;
- PL041_WRITE(p_tx, v);
- return 0;
- }
- int aaci_pl041_channle_read(int channle, rt_uint16_t *buff, int count)
- {
- void *p_data, *p_status;
- int i = 0;
- p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
- p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
- for (i = 0; (!(PL041_READ(p_status) & AACI_SR_RXFE)) && (i < count); i++)
- {
- buff[i] = (rt_uint16_t)PL041_READ(p_data);
- }
- return i;
- }
- int aaci_pl041_channle_write(int channle, rt_uint16_t *buff, int count)
- {
- void *p_data, *p_status;
- int i = 0;
- p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
- p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
- for (i = 0; (!(PL041_READ(p_status) & AACI_SR_TXFF)) && (i < count); i++)
- {
- PL041_WRITE(p_data, buff[i]);
- }
- return i;
- }
- int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cgf)
- {
- rt_uint32_t v;
- void *p_rx, *p_tx;
- p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
- p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
- v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->itype;
- PL041_WRITE(p_rx, v);
- v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->otype;
- PL041_WRITE(p_tx, v);
- ac97_set_vol(cgf->vol);
- ac97_set_rate(cgf->rate);
- return 0;
- }
- void aaci_pl041_irq_enable(int channle, rt_uint32_t vector)
- {
- rt_uint32_t v;
- void *p_irq;
- vector &= vector & 0x7f;
- p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
- v = PL041_READ(p_irq);
- v |= vector;
- PL041_WRITE(p_irq, v);
- }
- void aaci_pl041_irq_disable(int channle, rt_uint32_t vector)
- {
- rt_uint32_t v;
- void *p_irq;
- vector &= vector & 0x7f;
- p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
- v = PL041_READ(p_irq);
- v &= ~vector;
- PL041_WRITE(p_irq, v);
- }
- rt_err_t aaci_pl041_irq_register(int channle, pl041_irq_fun_t fun, void *user_data)
- {
- if (channle < 0 || channle >= PL041_CHANNLE_NUM)
- {
- dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
- return -RT_ERROR;
- }
- irq_tbl[channle].fun = fun;
- irq_tbl[channle].user_data = user_data;
- return RT_EOK;
- }
- rt_err_t aaci_pl041_irq_unregister(int channle)
- {
- if (channle < 0 || channle >= PL041_CHANNLE_NUM)
- {
- dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
- return -RT_ERROR;
- }
- irq_tbl[channle].fun = RT_NULL;
- irq_tbl[channle].user_data = RT_NULL;
- return RT_EOK;
- }
- static void aaci_pl041_irq_handle(int irqno, void *param)
- {
- rt_uint32_t mask, channle, m;
- struct pl041_irq_def *_irq = param;
- void *p_status;
- mask = PL041_READ(&PL041->allints);
- PL041_WRITE(PL041->intclr, mask);
- for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
- {
- mask = mask >> 7;
- m = mask & 0x7f;
- if (m & AACI_ISR_ORINTR)
- {
- dbg_log(DBG_WARNING, "RX overrun on chan %d\n", channle);
- }
- if (m & AACI_ISR_RXTOINTR)
- {
- dbg_log(DBG_WARNING, "RX timeout on chan %d\n", channle);
- }
- if (mask & AACI_ISR_URINTR)
- {
- dbg_log(DBG_WARNING, "TX underrun on chan %d\n", channle);
- }
- p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
- if (_irq[channle].fun != RT_NULL)
- {
- _irq[channle].fun(PL041_READ(p_status), _irq[channle].user_data);
- }
- }
- }
- rt_err_t aaci_pl041_init(void)
- {
- rt_uint32_t i, maincr;
- maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
- AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
- for (i = 0; i < 4; i++)
- {
- void *base = (void *)((rt_uint32_t)(&PL041->rxcr1) + i * 0x14);
- PL041_WRITE(base + AACI_IE, 0);
- PL041_WRITE(base + AACI_TXCR, 0);
- PL041_WRITE(base + AACI_RXCR, 0);
- }
- PL041_WRITE(&PL041->intclr, 0x1fff);
- PL041_WRITE(&PL041->maincr, maincr);
- PL041_WRITE(&PL041->reset, 0);
- aaci_pl041_delay(2);
- PL041_WRITE(&PL041->reset, RESET_NRST);
- rt_hw_interrupt_install(43, aaci_pl041_irq_handle, &irq_tbl, "aaci_pl041");
- rt_hw_interrupt_umask(43);
- return 0;
- }
- #if 0
- #define PL041_DUMP(_v) rt_kprintf("%32s:addr:0x%08x data:0x%08x\n", #_v, &(_v), (_v))
- int _aaci_pl041_reg_dump(int argc, char **argv)
- {
- PL041_DUMP(PL041->rxcr1);
- PL041_DUMP(PL041->txcr1);
- PL041_DUMP(PL041->sr1);
- PL041_DUMP(PL041->isr1);
- PL041_DUMP(PL041->iie1);
- PL041_DUMP(PL041->rxcr2);
- PL041_DUMP(PL041->txcr2);
- PL041_DUMP(PL041->sr2);
- PL041_DUMP(PL041->isr2);
- PL041_DUMP(PL041->iie2);
- PL041_DUMP(PL041->rxcr3);
- PL041_DUMP(PL041->txcr3);
- PL041_DUMP(PL041->sr3);
- PL041_DUMP(PL041->isr3);
- PL041_DUMP(PL041->iie3);
- PL041_DUMP(PL041->rxcr4);
- PL041_DUMP(PL041->txcr4);
- PL041_DUMP(PL041->sr4);
- PL041_DUMP(PL041->isr4);
- PL041_DUMP(PL041->iie4);
- PL041_DUMP(PL041->sl1rx);
- PL041_DUMP(PL041->sl1tx);
- PL041_DUMP(PL041->sl2rx);
- PL041_DUMP(PL041->sl2tx);
- PL041_DUMP(PL041->sl12rx);
- PL041_DUMP(PL041->sl12tx);
- PL041_DUMP(PL041->slfr);
- PL041_DUMP(PL041->slistat);
- PL041_DUMP(PL041->slien);
- PL041_DUMP(PL041->intclr);
- PL041_DUMP(PL041->maincr);
- PL041_DUMP(PL041->reset);
- PL041_DUMP(PL041->sync);
- PL041_DUMP(PL041->allints);
- PL041_DUMP(PL041->mainfr);
- PL041_DUMP(PL041->dr1[0]);
- PL041_DUMP(PL041->dr2[0]);
- PL041_DUMP(PL041->dr3[0]);
- PL041_DUMP(PL041->dr4[0]);
- return 0;
- }
- FINSH_FUNCTION_EXPORT_ALIAS(_aaci_pl041_reg_dump, __cmd_pl041_dump, aaci pl041 dump reg.);
- #endif
|