diff --git a/bsp/nuvoton/libraries/m2354/StdDriver/inc/nu_trng.h b/bsp/nuvoton/libraries/m2354/StdDriver/inc/nu_trng.h new file mode 100644 index 0000000000000000000000000000000000000000..3766e882c984c1e710ebcee32ce5ebdb44c2d4cc --- /dev/null +++ b/bsp/nuvoton/libraries/m2354/StdDriver/inc/nu_trng.h @@ -0,0 +1,83 @@ +/**************************************************************************//** + * @file nu_trng.h + * @version V3.00 + * @brief TRNG driver header file + * + * @copyright SPDX-License-Identifier: Apache-2.0 + * @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved. + ******************************************************************************/ +#ifndef __NU_TRNG_H__ +#define __NU_TRNG_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @addtogroup Standard_Driver Standard Driver + @{ +*/ + +/** @addtogroup TRNG_Driver TRNG Driver + @{ +*/ + + +/** @addtogroup M460_TRNG_EXPORTED_MACROS TRNG Exported Macros + @{ +*/ + +/*----------------------------------------------------------------------------------------------*/ +/* Macros */ +/*----------------------------------------------------------------------------------------------*/ + +/** + * @brief Let TRNG engine know the currrent PCLK frequency. The CLKPSC is the peripheral + * clock frequency range for the selected value , the CLKPSC setting must be higher + * than or equal to the actual peripheral clock frequency (for correct random generation). + * @param clkpsc 0: PCLK is 80~100 MHz + * 1: PCLK is 60~80 MHz + * 2: PCLK is 50~60 MHz + * 3: PCLK is 40~50 MHz + * 4: PCLK is 30~40 MHz + * 5: PCLK is 25~30 MHz + * 6: PCLK is 20~25 MHz + * 7: PCLK is 15~20 MHz + * 8: PCLK is 12~15 MHz + * 9: PCLK is 9~12 MHz + * + * @return None + * \hideinitializer + */ +#define TRNG_SET_CLKP(clkpsc) do { TRNG->CTL = (TRNG->CTL&~TRNG_CTL_CLKP_Msk)|((clkpsc & 0xf)< + +#include "NuMicro.h" + + +/** @addtogroup Standard_Driver Standard Driver + @{ +*/ + +/** @addtogroup TRNG_Driver TRNG Driver + @{ +*/ + + +/** @addtogroup TRNG_EXPORTED_FUNCTIONS TRNG Exported Functions + @{ +*/ + + +/** + * @brief Initialize TRNG hardware. + * @return TRNG hardware enable success or failed. + * @retval 0 Success + * @retval -1 Time-out. TRNG hardware may not be enabled. + */ +int32_t TRNG_Open(void) +{ + uint32_t u32TimeOutCount = SystemCoreClock; /* 1 second time-out */ + + SYS->IPRST1 |= SYS_IPRST1_TRNGRST_Msk; + SYS->IPRST1 ^= SYS_IPRST1_TRNGRST_Msk; + + TRNG->CTL |= TRNG_CTL_TRNGEN_Msk; + + TRNG->ACT |= TRNG_ACT_ACT_Msk; + + /* Waiting for ready */ + while ((TRNG->CTL & TRNG_CTL_READY_Msk) == 0) + { + if (--u32TimeOutCount == 0) return -1; /* Time-out error */ + } + + return 0; +} + + +/** + * @brief Generate a 32-bits random number word. + * @param[out] u32RndNum The output 32-bits word random number. + * + * @return Success or time-out. + * @retval 0 Success + * @retval -1 Time-out. TRNG hardware may not be enabled. + */ +int32_t TRNG_GenWord(uint32_t *u32RndNum) +{ + uint32_t i, u32Reg, timeout; + + *u32RndNum = 0; + u32Reg = TRNG->CTL; + + for (i = 0; i < 4; i++) + { + TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg; + + /* TRNG should generate one byte per 125*8 us */ + for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--) + { + if (TRNG->CTL & TRNG_CTL_DVIF_Msk) + break; + } + + if (timeout == 0) + return -1; + + *u32RndNum |= ((TRNG->DATA & 0xff) << i * 8); + + } + return 0; +} + +/** + * @brief Generate a big number in binary format. + * @param[out] u8BigNum The output big number. + * @param[in] i32Len Request bit length of the output big number. It must be multiple of 8. + * + * @return Success or time-out. + * @retval 0 Success + * @retval -1 Time-out. TRNG hardware may not be enabled. + */ +int32_t TRNG_GenBignum(uint8_t u8BigNum[], int32_t i32Len) +{ + uint32_t i, u32Reg, timeout; + + u32Reg = TRNG->CTL; + + for (i = 0; i < i32Len / 8; i++) + { + TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg; + + /* TRNG should generate one byte per 125*8 us */ + for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--) + { + if (TRNG->CTL & TRNG_CTL_DVIF_Msk) + break; + } + + if (timeout == 0) + return -1; + + u8BigNum[i] = (TRNG->DATA & 0xff); + } + return 0; +} + +/** + * @brief Generate a big number in hex format. + * @param[out] cBigNumHex The output hex format big number. + * @param[in] i32Len Request bit length of the output big number. It must be multiple of 8. + * + * @return Success or time-out. + * @retval 0 Success + * @retval -1 Time-out. TRNG hardware may not be enabled. + */ +int32_t TRNG_GenBignumHex(char cBigNumHex[], int32_t i32Len) +{ + uint32_t i, idx, u32Reg, timeout; + uint32_t data; + + u32Reg = TRNG->CTL; + idx = 0; + for (i = 0; i < i32Len / 8; i++) + { + TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg; + + /* TRNG should generate one byte per 125*8 us */ + for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--) + { + if (TRNG->CTL & TRNG_CTL_DVIF_Msk) + break; + } + + if (timeout == 0) + return -1; + + data = (TRNG->DATA & 0xff); + + if (data >= 0xA0) + cBigNumHex[idx++] = ((data >> 4) & 0xf) - 10 + 'A'; + else + cBigNumHex[idx++] = ((data >> 4) & 0xf) + '0'; + + data &= 0xf; + if (data >= 0xA) + cBigNumHex[idx++] = data - 10 + 'A'; + else + cBigNumHex[idx++] = data + '0'; + } + cBigNumHex[idx] = 0; + return 0; +} + + +/*@}*/ /* end of group TRNG_EXPORTED_FUNCTIONS */ + +/*@}*/ /* end of group TRNG_Driver */ + +/*@}*/ /* end of group Standard_Driver */ diff --git a/bsp/nuvoton/libraries/m2354/rtt_port/drv_sdio.c b/bsp/nuvoton/libraries/m2354/rtt_port/drv_sdio.c new file mode 100644 index 0000000000000000000000000000000000000000..3e982bea4f21556d1183dfe98c6516806a189eda --- /dev/null +++ b/bsp/nuvoton/libraries/m2354/rtt_port/drv_sdio.c @@ -0,0 +1,796 @@ +/**************************************************************************//** +* +* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2022-4-21 Wayne First version +* +******************************************************************************/ + +#include "rtconfig.h" + +#if defined(BSP_USING_SDH) + +#include +#include +#include + +#include "NuMicro.h" + +#define LOG_TAG "drv.sdh" +#undef DBG_ENABLE +#define DBG_SECTION_NAME LOG_TAG +#define DBG_LEVEL LOG_LVL_ASSERT +#define DBG_COLOR +#include + +#define SDH_ALIGN_LEN 4 +#define SDH_BUFF_SIZE 512 +#define SDH_BLOCK_SIZE 512 + +enum +{ + SDH_START = -1, +#if defined(BSP_USING_SDH0) + SDH0_IDX, +#endif +#if defined(BSP_USING_SDH1) + SDH1_IDX, +#endif + SDH_CNT +}; + +struct nu_sdh +{ + struct rt_mmcsd_host *host; + char *name; + SDH_T *base; + IRQn_Type irqn; + uint32_t rstidx; + uint32_t modid; + + uint8_t *cachebuf; + uint32_t u32CmdResp0; + uint32_t u32CmdResp1; + uint32_t u32CurClk; + rt_tick_t LastNotice; +}; +typedef struct nu_sdh *nu_sdh_t; + +/* Private variables ------------------------------------------------------------*/ +static struct nu_sdh nu_sdh_arr [] = +{ +#if defined(BSP_USING_SDH0) + { + .name = "sdh0", + .base = SDH0, + .irqn = SDH0_IRQn, + .rstidx = SDH0_RST, + .modid = SDH0_MODULE, + .cachebuf = RT_NULL, + }, +#endif +#if defined(BSP_USING_SDH1) + { + .name = "sdh1", + .base = SDH1, + .irqn = SDH1_IRQn, + .rstidx = SDH1_RST, + .modid = SDH1_MODULE, + .cachebuf = RT_NULL, + }, +#endif +}; /* struct nu_sdh nu_sdh_arr [] */ + +#define SDH_SetClock SDH_Set_clock + +static int SDH_SetBusWidth(SDH_T *sdh, uint32_t bw) +{ + if (bw == 4) + { + sdh->CTL |= SDH_CTL_DBW_Msk; + } + else if (bw == 1) + { + sdh->CTL &= ~SDH_CTL_DBW_Msk; + } + else + { + goto exit_SDH_SetBusWidth; + } + + return 0; + +exit_SDH_SetBusWidth: + + return -1; + +} + +static int SDH_GetBusStatus(SDH_T *sdh, uint32_t mask) +{ + int cnt = 0x100000; + while (cnt-- > 0) + { + sdh->CTL |= SDH_CTL_CLK8OEN_Msk; + while (sdh->CTL & SDH_CTL_CLK8OEN_Msk) { } + + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) + break; + } + return (cnt == 0) ? -1 : 0 ; +} + +static int SDH_GetCD(SDH_T *sdh) +{ + int i32CD = 0; + if ((sdh->GCTL & SDH_GCTL_SDEN_Msk) == SDH_GCTL_SDEN_Msk) + { + if ((sdh->INTEN & SDH_INTEN_CDSRC_Msk) == SDH_INTEN_CDSRC_Msk) /* Card detect pin from GPIO */ + { + i32CD = (sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) ? 0 : 1; + } + else /* Card detect pin from DAT3 mode */ + { + __IO uint32_t i; + + sdh->CTL |= SDH_CTL_CLKKEEP_Msk; + for (i = 0ul; i < 5000ul; i++) { } + + i32CD = ((sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) == SDH_INTSTS_CDSTS_Msk) ? 1 : 0; + sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk; + } + } + return i32CD; +} + +static void SDH_Enable(SDH_T *sdh) +{ + /* Reset sdh and its DMA engine at first. */ + sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk; + while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { } + sdh->DMACTL = SDH_DMACTL_DMAEN_Msk; + sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag + + sdh->GCTL = SDH_GCTL_GCTLRST_Msk; + while ((sdh->GCTL & SDH_GCTL_GCTLRST_Msk) == SDH_GCTL_GCTLRST_Msk) { }// clear all interrupt flag + sdh->GINTSTS = SDH_GINTSTS_DTAIF_Msk; + sdh->GCTL = SDH_GCTL_SDEN_Msk; + + sdh->CTL |= SDH_CTL_CTLRST_Msk; + while ((sdh->CTL & SDH_CTL_CTLRST_Msk) == SDH_CTL_CTLRST_Msk) { } + + sdh->INTSTS = 0xFFFFFFFF; // clear all interrupt flag + + sdh->INTEN |= SDH_INTEN_CDSRC_Msk; + sdh->INTEN |= SDH_INTEN_CDIEN_Msk; +} + +/** + * @brief This function get command responding. + * @param sdh SDH instance + * @param cmd rt_mmcsd_cmd + * @retval none + */ +static void nu_sdh_sendcmd_done(SDH_T *sdh, struct rt_mmcsd_cmd *cmd) +{ + if (resp_type(cmd) == RESP_R2) + { + uint8_t *c = (uint8_t *)&sdh->FB[0]; + int i, j, tmp[5]; + + for (i = 0, j = 0; j < 5; i += 4, j++) + { + tmp[j] = (*(c + i) << 24) | (*(c + i + 1) << 16) | (*(c + i + 2) << 8) | (*(c + i + 3)); + } + for (i = 0; i < 4; i++) + { + cmd->resp[i] = ((tmp[i] & 0x00ffffff) << 8) | + ((tmp[i + 1] & 0xff000000) >> 24); + } + } + else + { + cmd->resp[0] = (sdh->RESP0 << 8) | (sdh->RESP1 & 0xff); + cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0; + } +} + +/** + * @brief This function wait data-sending/receiving. + * @param sdh SDH instance + * @param data rt_mmcsd_data + * @retval error code + */ +static int nu_sdh_xfer_data(SDH_T *sdh, struct rt_mmcsd_data *data) +{ + while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk)) { } + + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk); + + if (data->flags & DATA_DIR_WRITE) + { + sdh->CTL |= SDH_CTL_CLKKEEP_Msk; + + while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) { } + + sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk; + } + + return 0; +} + +/** + * @brief This function send command and wait its response. + * @param host rt_mmcsd_host + * @param cmd rt_mmcsd_cmd + * @param data rt_mmcsd_data + * @retval error code + */ +static int nu_sdh_sendcmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd, struct rt_mmcsd_data *data) +{ + int ret; + nu_sdh_t NuSdh = (nu_sdh_t)host->private_data; + SDH_T *sdh = NuSdh->base; + + volatile uint32_t ctl = 0, tout = 0; + + switch (host->io_cfg.bus_width) + { + case MMCSD_BUS_WIDTH_1: + ctl &= ~SDH_CTL_DBW_Msk; + break; + + case MMCSD_BUS_WIDTH_4: + ctl |= SDH_CTL_DBW_Msk; + break; + + case MMCSD_BUS_WIDTH_8: + default: + return -1; + } + + /* Reset sdh and its DMA engine at first. */ + sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk; + while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { } + sdh->DMACTL = SDH_DMACTL_DMAEN_Msk; + sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag + + if (resp_type(cmd) != RESP_NONE) + { + if (resp_type(cmd) == RESP_R2) + { + ctl |= SDH_CTL_R2EN_Msk; + } + else + { + ctl |= SDH_CTL_RIEN_Msk; + } + tout = 0xFFFF; + } + + /* Set SDNWR and BLK_CNT to 1 */ + ctl |= ((9 << SDH_CTL_SDNWR_Pos) | (1 << SDH_CTL_BLKCNT_Pos)); + ctl |= ((cmd->cmd_code << SDH_CTL_CMDCODE_Pos) | SDH_CTL_COEN_Msk); + + /* Set Transfer mode regarding to data flag */ + if (data != RT_NULL) + { + sdh->BLEN = data->blksize - 1; + + if (data->blksize <= 0x200) + { + if (data->blks < 256) + { + ctl = (ctl & ~SDH_CTL_BLKCNT_Msk) | (data->blks << SDH_CTL_BLKCNT_Pos); + } + else + { + LOG_E("SD Max block transfer is 255!!"); + } + } + + if (data->flags & DATA_DIR_READ) + { + tout = 0xFFFFFF; + ctl |= SDH_CTL_DIEN_Msk; // Data-in + sdh->DMASA = (uint32_t)data->buf; // Read from dest + } + else if (data->flags & DATA_DIR_WRITE) + { + ctl |= SDH_CTL_DOEN_Msk; // Data-out + sdh->DMASA = (uint32_t)data->buf; // Write to dest + } + } + else if (resp_type(cmd) == RESP_R1B) + { + } + + /* Clear response-timeout flag first for safty and reset new timeout value. */ + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk); + sdh->TOUT = tout; + + /* Set argument and start a transaction. */ + sdh->CMDARG = cmd->arg; + sdh->CTL = ctl; + + /* Wait a command done. */ + while ((sdh->CTL & (SDH_CTL_COEN_Msk)) == SDH_CTL_COEN_Msk) { } + + if (resp_type(cmd) != RESP_NONE) + { + if (resp_type(cmd) == RESP_R2) + { + /* Wait to receive a response R2 from SD card and store the response data into DMC's Flash buffer (exclude CRC7). */ + while (sdh->CTL & SDH_CTL_R2EN_Msk) + { + /* When get a Response timeout, break the polling. */ + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk)) + { + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + } + else + { + /* Wait to receive a response from SD card. */ + while ((sdh->CTL & SDH_CTL_RIEN_Msk)) + { + /* When get a Response timeout, break the polling. */ + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk)) + { + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + + /* TOFIX: ISSUE: Sometimes, SDH's RIEN is cleared automatically by controller after host sending CMD5 to SD card. */ + /* Workaround: To check previous cmd's response with CMD's. */ + if (cmd->cmd_code == 5) + { + if ((NuSdh->u32CmdResp0 == sdh->RESP0) && (NuSdh->u32CmdResp1 == sdh->RESP1)) + { + LOG_E("False CMD5-RESP issue occured.\n"); + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + NuSdh->u32CmdResp0 = sdh->RESP0; + NuSdh->u32CmdResp1 = sdh->RESP1; + } + + /* Get response from FB or register */ + nu_sdh_sendcmd_done(sdh, cmd); + } + + if (data != RT_NULL) + { + /* Wait data processing done */ + nu_sdh_xfer_data(sdh, data); + + ret = SDH_GetBusStatus(sdh, 0); + if (ret) + { + LOG_E("ERROR: Busy %d\n", ret); + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + + /* Handle CRC flag */ + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk)) // Fault + { + uint32_t u32INTSTS = sdh->INTSTS; + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk); + ret = __LINE__; + + if ((resp_type(cmd) != RESP_R3) && (u32INTSTS & SDH_INTSTS_CRC7_Msk) == 0) //CRC7, Ignore R3 + { + LOG_E("CRC7 error! (resp_type=%d)", resp_type(cmd)); + goto exit_nu_sdh_sendcmd; + } + + if ((u32INTSTS & SDH_INTSTS_CRC16_Msk) == 0) //CRC16 + { + LOG_E("CRC16 error! (resp_type=%d)", resp_type(cmd)); + goto exit_nu_sdh_sendcmd; + } + } + + return 0; + + +exit_nu_sdh_sendcmd: + + LOG_D("[%s %d] cmdid=%d error line=%d", __func__, __LINE__, cmd->cmd_code, ret); + + cmd->resp[0] = cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0; + sdh->TOUT = 0; + SDH_Enable(sdh); + + return -ret; +} + +/** + * @brief This function send request. + * @param host rt_mmcsd_host + * @param req request + * @retval None + */ +static void nu_sdh_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) +{ + nu_sdh_t NuSdh; + SDH_T *sdh; + + RT_ASSERT(host); + RT_ASSERT(req); + + NuSdh = (nu_sdh_t)host->private_data; + sdh = NuSdh->base; + + if (!SDH_GetCD(sdh)) // card is not present + { + LOG_E("Card is not present"); + req->cmd->err = -RT_EIO; + goto exit_nu_sdh_request; + } + + if (req->cmd != RT_NULL) + { + struct rt_mmcsd_cmd *cmd = req->cmd; + struct rt_mmcsd_data *data = req->data; + + LOG_D("[%s%s%s%s%s]REQ: CMD:%d ARG:0x%08x RESP_TYPE:%d rw:%c addr:%08x, blks:%d, blksize:%d datalen:%d", + (host->card == RT_NULL) ? "Unknown" : "", + (host->card) && (host->card->card_type == CARD_TYPE_MMC) ? "MMC" : "", + (host->card) && (host->card->card_type == CARD_TYPE_SD) ? "SD" : "", + (host->card) && (host->card->card_type == CARD_TYPE_SDIO) ? "SDIO" : "", + (host->card) && (host->card->card_type == CARD_TYPE_SDIO_COMBO) ? "SDIO_COMBO" : "", + cmd->cmd_code, + cmd->arg, + resp_type(cmd), + data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-', + data ? data->buf : 0, + data ? data->blks : 0, + data ? data->blksize : 0, + data ? data->blks * data->blksize : 0); + + if (data != RT_NULL) + { + rt_uint32_t size; + rt_int32_t IsNonaligned = 0; + rt_uint32_t *org_data_buf = data->buf; + + size = data->blksize * data->blks; + + RT_ASSERT(org_data_buf); + + IsNonaligned = (((rt_uint32_t)data->buf & (SDH_ALIGN_LEN - 1)) > 0) ? 1 : 0; + if (IsNonaligned) + { + /* Allocate memory temp buffer on demand. */ + RT_ASSERT(size <= SDH_BUFF_SIZE); + + if (NuSdh->cachebuf == RT_NULL) + { + NuSdh->cachebuf = rt_malloc_align(SDH_BUFF_SIZE, SDH_ALIGN_LEN); + RT_ASSERT(NuSdh->cachebuf); + } + + data->buf = (rt_uint32_t *)NuSdh->cachebuf; + if (data->flags & DATA_DIR_WRITE) + { + LOG_D("Un-aligned, prepare into cache buf(%d)", size); + rt_memcpy(data->buf, org_data_buf, size); + } + } + + cmd->err = nu_sdh_sendcmd(host, cmd, data); + + if (!cmd->err && IsNonaligned) + { + if (data->flags & DATA_DIR_READ) + { + LOG_D("Un-aligned, restore from cache buf(%d)", size); + rt_memcpy(org_data_buf, data->buf, size); + } + } + data->buf = org_data_buf; + + LOG_HEX("data.dest", 16, (void *)data->buf, size); + } + else + { + cmd->err = nu_sdh_sendcmd(host, cmd, NULL); + } + + if (resp_type(cmd) != RESP_NONE) + LOG_HEX("cmd->resp", 16, (void *)&cmd->resp[0], 16); + } + + if (req->stop != RT_NULL) + { + struct rt_mmcsd_cmd *stop = req->stop; + + stop->err = nu_sdh_sendcmd(host, stop, NULL); + + if (resp_type(stop) != RESP_NONE) + LOG_HEX("stop->resp", 16, (void *)&stop->resp[0], 16); + + } + +exit_nu_sdh_request: + + mmcsd_req_complete(host); +} + +/** + * @brief This function config. + * @param host rt_mmcsd_host + * @param io_cfg rt_mmcsd_io_cfg + * @retval None + */ +static void nu_sdh_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg) +{ + nu_sdh_t NuSdh; + rt_uint32_t clk; + SDH_T *sdh; + + RT_ASSERT(host); + RT_ASSERT(io_cfg); + + NuSdh = (nu_sdh_t)host->private_data; + sdh = NuSdh->base; + clk = io_cfg->clock; + + LOG_D("[%s]clk:%d width(%d):%s%s%s power:%s%s%s", + NuSdh->name, + clk, + io_cfg->bus_width, + (io_cfg->bus_width) == MMCSD_BUS_WIDTH_8 ? "8" : "", + (io_cfg->bus_width) == MMCSD_BUS_WIDTH_4 ? "4" : "", + (io_cfg->bus_width) == MMCSD_BUS_WIDTH_1 ? "1" : "", + io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "", + io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "", + io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : ""); + + /* Clock */ + if (clk > host->freq_max) + clk = host->freq_max; + + if (clk < host->freq_min) + clk = host->freq_min; + + LOG_D("[%s] ExceptedFreq: %d kHz", NuSdh->name, clk / 1000); + if (NuSdh->u32CurClk != (clk / 1000)) + { + SDH_SetClock(sdh, clk / 1000); + NuSdh->u32CurClk = (clk / 1000); + } + + switch (io_cfg->power_mode) + { + case MMCSD_POWER_UP: + if (clk <= 400000) + { + /* power ON 74 clock */ + sdh->CTL |= SDH_CTL_CLK74OEN_Msk; + + while ((sdh->CTL & SDH_CTL_CLK74OEN_Msk) == SDH_CTL_CLK74OEN_Msk) + { + } + } + break; + + case MMCSD_POWER_ON: + break; + + case MMCSD_POWER_OFF: + break; + + default: + break; + } + + /* Bus width */ + switch ((io_cfg->bus_width)) + { + case MMCSD_BUS_WIDTH_1: + SDH_SetBusWidth(sdh, 1); + break; + + case MMCSD_BUS_WIDTH_4: + SDH_SetBusWidth(sdh, 4); + break; + + case MMCSD_BUS_WIDTH_8: + default: + break; + } + +} + +/** + * @brief This function detect sdcard. + * @param host rt_mmcsd_host + * @retval card detection status + */ +static rt_int32_t nu_sdh_card_detect(struct rt_mmcsd_host *host) +{ + nu_sdh_t NuSdh; + RT_ASSERT(host); + + NuSdh = (nu_sdh_t)host->private_data; + SDH_T *sdh = NuSdh->base; + + LOG_D("try to detect device"); + return SDH_GetCD(sdh); +} + +static void nu_sdh_isr(nu_sdh_t NuSdh) +{ + SDH_T *sdh = NuSdh->base; + uint32_t isr = sdh->INTSTS; + + /* card detected */ + if (isr & SDH_INTSTS_CDIF_Msk) + { + rt_tick_t cur_tick = rt_tick_get(); + rt_tick_t diff_tick; + + /* ready to change */ + if (cur_tick >= NuSdh->LastNotice) + diff_tick = (cur_tick - NuSdh->LastNotice); + else + diff_tick = ((rt_tick_t) -1) - NuSdh->LastNotice + cur_tick; + + if (!NuSdh->LastNotice || (diff_tick > (RT_TICK_PER_SECOND / 5))) // Debounce 200ms + { + NuSdh->LastNotice = cur_tick; + mmcsd_change(NuSdh->host); + } + + /* Clear CDIF interrupt flag */ + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CDIF_Msk); + } +} + +#if defined(BSP_USING_SDH0) +void SDH0_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + nu_sdh_isr(&nu_sdh_arr[SDH0_IDX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif + +#if defined(BSP_USING_SDH1) +void SDH1_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + nu_sdh_isr(&nu_sdh_arr[SDH1_IDX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif + + +/** + * @brief This function update sdh interrupt. + * @param host rt_mmcsd_host + * @param enable + * @retval None + */ +void nu_sdh_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable) +{ + nu_sdh_t NuSdh = (nu_sdh_t)host->private_data; + SDH_T *sdh = NuSdh->base; + + if (enable) + { + LOG_D("Enable %s irq", NuSdh->name); + SDH_ENABLE_INT(sdh, SDH_INTSTS_CDIF_Msk); + } + else + { + LOG_D("Disable %s irq", NuSdh->name); + SDH_DISABLE_INT(sdh, SDH_INTSTS_CDIF_Msk); + } +} + +static const struct rt_mmcsd_host_ops ops = +{ + nu_sdh_request, + nu_sdh_iocfg, + nu_sdh_card_detect, + nu_sdh_irq_update, +}; + +/** + * @brief This function create mmcsd host. + * @param sdh nu_sdh_t + * @retval nuvton + */ +void nu_sdh_host_init(nu_sdh_t sdh) +{ + struct rt_mmcsd_host *host = mmcsd_alloc_host(); + RT_ASSERT(host); + + /* set host default attributes */ + host->ops = &ops; + host->freq_min = 300 * 1000; + host->freq_max = 48 * 1000 * 1000; + host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34; + + host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED; + + host->max_seg_size = SDH_BUFF_SIZE; + host->max_dma_segs = 1; + host->max_blk_size = SDH_BLOCK_SIZE; + host->max_blk_count = (SDH_BUFF_SIZE / SDH_BLOCK_SIZE); + + /* link up host and sdio */ + host->private_data = sdh; + sdh->host = host; + + /* Enable DMA engine at first. */ + SDH_Enable(sdh->base); + + /* Install ISR. */ + NVIC_EnableIRQ(sdh->irqn); + + /* ready to change */ + //mmcsd_change(host); +} + +static int rt_hw_sdh_init(void) +{ + int i; + + for (i = (SDH_START + 1); i < SDH_CNT; i++) + { + CLK_EnableModuleClock(nu_sdh_arr[i].modid); + SYS_ResetModule(nu_sdh_arr[i].rstidx); + + nu_sdh_host_init(&nu_sdh_arr[i]); + } + + return 0; +} +INIT_DEVICE_EXPORT(rt_hw_sdh_init); + +void nu_sd_attach(void) +{ + int i; + /* ready to change */ + for (i = (SDH_START + 1); i < SDH_CNT; i++) + { + if (nu_sdh_arr[i].host) + mmcsd_change(nu_sdh_arr[i].host); + } +} +MSH_CMD_EXPORT(nu_sd_attach, attach card); + +void nu_sd_regdump(void) +{ + int i; + for (i = (SDH_START + 1); i < SDH_CNT; i++) + { + if (nu_sdh_arr[i].host) + LOG_HEX("sdh_reg", 16, (void *)nu_sdh_arr[i].base, sizeof(SDH_T)); + } +} +MSH_CMD_EXPORT(nu_sd_regdump, dump sdh registers); + +#endif diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_sdio.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_sdio.c new file mode 100644 index 0000000000000000000000000000000000000000..3e982bea4f21556d1183dfe98c6516806a189eda --- /dev/null +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_sdio.c @@ -0,0 +1,796 @@ +/**************************************************************************//** +* +* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2022-4-21 Wayne First version +* +******************************************************************************/ + +#include "rtconfig.h" + +#if defined(BSP_USING_SDH) + +#include +#include +#include + +#include "NuMicro.h" + +#define LOG_TAG "drv.sdh" +#undef DBG_ENABLE +#define DBG_SECTION_NAME LOG_TAG +#define DBG_LEVEL LOG_LVL_ASSERT +#define DBG_COLOR +#include + +#define SDH_ALIGN_LEN 4 +#define SDH_BUFF_SIZE 512 +#define SDH_BLOCK_SIZE 512 + +enum +{ + SDH_START = -1, +#if defined(BSP_USING_SDH0) + SDH0_IDX, +#endif +#if defined(BSP_USING_SDH1) + SDH1_IDX, +#endif + SDH_CNT +}; + +struct nu_sdh +{ + struct rt_mmcsd_host *host; + char *name; + SDH_T *base; + IRQn_Type irqn; + uint32_t rstidx; + uint32_t modid; + + uint8_t *cachebuf; + uint32_t u32CmdResp0; + uint32_t u32CmdResp1; + uint32_t u32CurClk; + rt_tick_t LastNotice; +}; +typedef struct nu_sdh *nu_sdh_t; + +/* Private variables ------------------------------------------------------------*/ +static struct nu_sdh nu_sdh_arr [] = +{ +#if defined(BSP_USING_SDH0) + { + .name = "sdh0", + .base = SDH0, + .irqn = SDH0_IRQn, + .rstidx = SDH0_RST, + .modid = SDH0_MODULE, + .cachebuf = RT_NULL, + }, +#endif +#if defined(BSP_USING_SDH1) + { + .name = "sdh1", + .base = SDH1, + .irqn = SDH1_IRQn, + .rstidx = SDH1_RST, + .modid = SDH1_MODULE, + .cachebuf = RT_NULL, + }, +#endif +}; /* struct nu_sdh nu_sdh_arr [] */ + +#define SDH_SetClock SDH_Set_clock + +static int SDH_SetBusWidth(SDH_T *sdh, uint32_t bw) +{ + if (bw == 4) + { + sdh->CTL |= SDH_CTL_DBW_Msk; + } + else if (bw == 1) + { + sdh->CTL &= ~SDH_CTL_DBW_Msk; + } + else + { + goto exit_SDH_SetBusWidth; + } + + return 0; + +exit_SDH_SetBusWidth: + + return -1; + +} + +static int SDH_GetBusStatus(SDH_T *sdh, uint32_t mask) +{ + int cnt = 0x100000; + while (cnt-- > 0) + { + sdh->CTL |= SDH_CTL_CLK8OEN_Msk; + while (sdh->CTL & SDH_CTL_CLK8OEN_Msk) { } + + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) + break; + } + return (cnt == 0) ? -1 : 0 ; +} + +static int SDH_GetCD(SDH_T *sdh) +{ + int i32CD = 0; + if ((sdh->GCTL & SDH_GCTL_SDEN_Msk) == SDH_GCTL_SDEN_Msk) + { + if ((sdh->INTEN & SDH_INTEN_CDSRC_Msk) == SDH_INTEN_CDSRC_Msk) /* Card detect pin from GPIO */ + { + i32CD = (sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) ? 0 : 1; + } + else /* Card detect pin from DAT3 mode */ + { + __IO uint32_t i; + + sdh->CTL |= SDH_CTL_CLKKEEP_Msk; + for (i = 0ul; i < 5000ul; i++) { } + + i32CD = ((sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) == SDH_INTSTS_CDSTS_Msk) ? 1 : 0; + sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk; + } + } + return i32CD; +} + +static void SDH_Enable(SDH_T *sdh) +{ + /* Reset sdh and its DMA engine at first. */ + sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk; + while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { } + sdh->DMACTL = SDH_DMACTL_DMAEN_Msk; + sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag + + sdh->GCTL = SDH_GCTL_GCTLRST_Msk; + while ((sdh->GCTL & SDH_GCTL_GCTLRST_Msk) == SDH_GCTL_GCTLRST_Msk) { }// clear all interrupt flag + sdh->GINTSTS = SDH_GINTSTS_DTAIF_Msk; + sdh->GCTL = SDH_GCTL_SDEN_Msk; + + sdh->CTL |= SDH_CTL_CTLRST_Msk; + while ((sdh->CTL & SDH_CTL_CTLRST_Msk) == SDH_CTL_CTLRST_Msk) { } + + sdh->INTSTS = 0xFFFFFFFF; // clear all interrupt flag + + sdh->INTEN |= SDH_INTEN_CDSRC_Msk; + sdh->INTEN |= SDH_INTEN_CDIEN_Msk; +} + +/** + * @brief This function get command responding. + * @param sdh SDH instance + * @param cmd rt_mmcsd_cmd + * @retval none + */ +static void nu_sdh_sendcmd_done(SDH_T *sdh, struct rt_mmcsd_cmd *cmd) +{ + if (resp_type(cmd) == RESP_R2) + { + uint8_t *c = (uint8_t *)&sdh->FB[0]; + int i, j, tmp[5]; + + for (i = 0, j = 0; j < 5; i += 4, j++) + { + tmp[j] = (*(c + i) << 24) | (*(c + i + 1) << 16) | (*(c + i + 2) << 8) | (*(c + i + 3)); + } + for (i = 0; i < 4; i++) + { + cmd->resp[i] = ((tmp[i] & 0x00ffffff) << 8) | + ((tmp[i + 1] & 0xff000000) >> 24); + } + } + else + { + cmd->resp[0] = (sdh->RESP0 << 8) | (sdh->RESP1 & 0xff); + cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0; + } +} + +/** + * @brief This function wait data-sending/receiving. + * @param sdh SDH instance + * @param data rt_mmcsd_data + * @retval error code + */ +static int nu_sdh_xfer_data(SDH_T *sdh, struct rt_mmcsd_data *data) +{ + while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk)) { } + + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk); + + if (data->flags & DATA_DIR_WRITE) + { + sdh->CTL |= SDH_CTL_CLKKEEP_Msk; + + while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) { } + + sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk; + } + + return 0; +} + +/** + * @brief This function send command and wait its response. + * @param host rt_mmcsd_host + * @param cmd rt_mmcsd_cmd + * @param data rt_mmcsd_data + * @retval error code + */ +static int nu_sdh_sendcmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd, struct rt_mmcsd_data *data) +{ + int ret; + nu_sdh_t NuSdh = (nu_sdh_t)host->private_data; + SDH_T *sdh = NuSdh->base; + + volatile uint32_t ctl = 0, tout = 0; + + switch (host->io_cfg.bus_width) + { + case MMCSD_BUS_WIDTH_1: + ctl &= ~SDH_CTL_DBW_Msk; + break; + + case MMCSD_BUS_WIDTH_4: + ctl |= SDH_CTL_DBW_Msk; + break; + + case MMCSD_BUS_WIDTH_8: + default: + return -1; + } + + /* Reset sdh and its DMA engine at first. */ + sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk; + while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { } + sdh->DMACTL = SDH_DMACTL_DMAEN_Msk; + sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag + + if (resp_type(cmd) != RESP_NONE) + { + if (resp_type(cmd) == RESP_R2) + { + ctl |= SDH_CTL_R2EN_Msk; + } + else + { + ctl |= SDH_CTL_RIEN_Msk; + } + tout = 0xFFFF; + } + + /* Set SDNWR and BLK_CNT to 1 */ + ctl |= ((9 << SDH_CTL_SDNWR_Pos) | (1 << SDH_CTL_BLKCNT_Pos)); + ctl |= ((cmd->cmd_code << SDH_CTL_CMDCODE_Pos) | SDH_CTL_COEN_Msk); + + /* Set Transfer mode regarding to data flag */ + if (data != RT_NULL) + { + sdh->BLEN = data->blksize - 1; + + if (data->blksize <= 0x200) + { + if (data->blks < 256) + { + ctl = (ctl & ~SDH_CTL_BLKCNT_Msk) | (data->blks << SDH_CTL_BLKCNT_Pos); + } + else + { + LOG_E("SD Max block transfer is 255!!"); + } + } + + if (data->flags & DATA_DIR_READ) + { + tout = 0xFFFFFF; + ctl |= SDH_CTL_DIEN_Msk; // Data-in + sdh->DMASA = (uint32_t)data->buf; // Read from dest + } + else if (data->flags & DATA_DIR_WRITE) + { + ctl |= SDH_CTL_DOEN_Msk; // Data-out + sdh->DMASA = (uint32_t)data->buf; // Write to dest + } + } + else if (resp_type(cmd) == RESP_R1B) + { + } + + /* Clear response-timeout flag first for safty and reset new timeout value. */ + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk); + sdh->TOUT = tout; + + /* Set argument and start a transaction. */ + sdh->CMDARG = cmd->arg; + sdh->CTL = ctl; + + /* Wait a command done. */ + while ((sdh->CTL & (SDH_CTL_COEN_Msk)) == SDH_CTL_COEN_Msk) { } + + if (resp_type(cmd) != RESP_NONE) + { + if (resp_type(cmd) == RESP_R2) + { + /* Wait to receive a response R2 from SD card and store the response data into DMC's Flash buffer (exclude CRC7). */ + while (sdh->CTL & SDH_CTL_R2EN_Msk) + { + /* When get a Response timeout, break the polling. */ + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk)) + { + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + } + else + { + /* Wait to receive a response from SD card. */ + while ((sdh->CTL & SDH_CTL_RIEN_Msk)) + { + /* When get a Response timeout, break the polling. */ + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk)) + { + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + + /* TOFIX: ISSUE: Sometimes, SDH's RIEN is cleared automatically by controller after host sending CMD5 to SD card. */ + /* Workaround: To check previous cmd's response with CMD's. */ + if (cmd->cmd_code == 5) + { + if ((NuSdh->u32CmdResp0 == sdh->RESP0) && (NuSdh->u32CmdResp1 == sdh->RESP1)) + { + LOG_E("False CMD5-RESP issue occured.\n"); + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + NuSdh->u32CmdResp0 = sdh->RESP0; + NuSdh->u32CmdResp1 = sdh->RESP1; + } + + /* Get response from FB or register */ + nu_sdh_sendcmd_done(sdh, cmd); + } + + if (data != RT_NULL) + { + /* Wait data processing done */ + nu_sdh_xfer_data(sdh, data); + + ret = SDH_GetBusStatus(sdh, 0); + if (ret) + { + LOG_E("ERROR: Busy %d\n", ret); + ret = __LINE__; + goto exit_nu_sdh_sendcmd; + } + } + + /* Handle CRC flag */ + if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk)) // Fault + { + uint32_t u32INTSTS = sdh->INTSTS; + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk); + ret = __LINE__; + + if ((resp_type(cmd) != RESP_R3) && (u32INTSTS & SDH_INTSTS_CRC7_Msk) == 0) //CRC7, Ignore R3 + { + LOG_E("CRC7 error! (resp_type=%d)", resp_type(cmd)); + goto exit_nu_sdh_sendcmd; + } + + if ((u32INTSTS & SDH_INTSTS_CRC16_Msk) == 0) //CRC16 + { + LOG_E("CRC16 error! (resp_type=%d)", resp_type(cmd)); + goto exit_nu_sdh_sendcmd; + } + } + + return 0; + + +exit_nu_sdh_sendcmd: + + LOG_D("[%s %d] cmdid=%d error line=%d", __func__, __LINE__, cmd->cmd_code, ret); + + cmd->resp[0] = cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0; + sdh->TOUT = 0; + SDH_Enable(sdh); + + return -ret; +} + +/** + * @brief This function send request. + * @param host rt_mmcsd_host + * @param req request + * @retval None + */ +static void nu_sdh_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) +{ + nu_sdh_t NuSdh; + SDH_T *sdh; + + RT_ASSERT(host); + RT_ASSERT(req); + + NuSdh = (nu_sdh_t)host->private_data; + sdh = NuSdh->base; + + if (!SDH_GetCD(sdh)) // card is not present + { + LOG_E("Card is not present"); + req->cmd->err = -RT_EIO; + goto exit_nu_sdh_request; + } + + if (req->cmd != RT_NULL) + { + struct rt_mmcsd_cmd *cmd = req->cmd; + struct rt_mmcsd_data *data = req->data; + + LOG_D("[%s%s%s%s%s]REQ: CMD:%d ARG:0x%08x RESP_TYPE:%d rw:%c addr:%08x, blks:%d, blksize:%d datalen:%d", + (host->card == RT_NULL) ? "Unknown" : "", + (host->card) && (host->card->card_type == CARD_TYPE_MMC) ? "MMC" : "", + (host->card) && (host->card->card_type == CARD_TYPE_SD) ? "SD" : "", + (host->card) && (host->card->card_type == CARD_TYPE_SDIO) ? "SDIO" : "", + (host->card) && (host->card->card_type == CARD_TYPE_SDIO_COMBO) ? "SDIO_COMBO" : "", + cmd->cmd_code, + cmd->arg, + resp_type(cmd), + data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-', + data ? data->buf : 0, + data ? data->blks : 0, + data ? data->blksize : 0, + data ? data->blks * data->blksize : 0); + + if (data != RT_NULL) + { + rt_uint32_t size; + rt_int32_t IsNonaligned = 0; + rt_uint32_t *org_data_buf = data->buf; + + size = data->blksize * data->blks; + + RT_ASSERT(org_data_buf); + + IsNonaligned = (((rt_uint32_t)data->buf & (SDH_ALIGN_LEN - 1)) > 0) ? 1 : 0; + if (IsNonaligned) + { + /* Allocate memory temp buffer on demand. */ + RT_ASSERT(size <= SDH_BUFF_SIZE); + + if (NuSdh->cachebuf == RT_NULL) + { + NuSdh->cachebuf = rt_malloc_align(SDH_BUFF_SIZE, SDH_ALIGN_LEN); + RT_ASSERT(NuSdh->cachebuf); + } + + data->buf = (rt_uint32_t *)NuSdh->cachebuf; + if (data->flags & DATA_DIR_WRITE) + { + LOG_D("Un-aligned, prepare into cache buf(%d)", size); + rt_memcpy(data->buf, org_data_buf, size); + } + } + + cmd->err = nu_sdh_sendcmd(host, cmd, data); + + if (!cmd->err && IsNonaligned) + { + if (data->flags & DATA_DIR_READ) + { + LOG_D("Un-aligned, restore from cache buf(%d)", size); + rt_memcpy(org_data_buf, data->buf, size); + } + } + data->buf = org_data_buf; + + LOG_HEX("data.dest", 16, (void *)data->buf, size); + } + else + { + cmd->err = nu_sdh_sendcmd(host, cmd, NULL); + } + + if (resp_type(cmd) != RESP_NONE) + LOG_HEX("cmd->resp", 16, (void *)&cmd->resp[0], 16); + } + + if (req->stop != RT_NULL) + { + struct rt_mmcsd_cmd *stop = req->stop; + + stop->err = nu_sdh_sendcmd(host, stop, NULL); + + if (resp_type(stop) != RESP_NONE) + LOG_HEX("stop->resp", 16, (void *)&stop->resp[0], 16); + + } + +exit_nu_sdh_request: + + mmcsd_req_complete(host); +} + +/** + * @brief This function config. + * @param host rt_mmcsd_host + * @param io_cfg rt_mmcsd_io_cfg + * @retval None + */ +static void nu_sdh_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg) +{ + nu_sdh_t NuSdh; + rt_uint32_t clk; + SDH_T *sdh; + + RT_ASSERT(host); + RT_ASSERT(io_cfg); + + NuSdh = (nu_sdh_t)host->private_data; + sdh = NuSdh->base; + clk = io_cfg->clock; + + LOG_D("[%s]clk:%d width(%d):%s%s%s power:%s%s%s", + NuSdh->name, + clk, + io_cfg->bus_width, + (io_cfg->bus_width) == MMCSD_BUS_WIDTH_8 ? "8" : "", + (io_cfg->bus_width) == MMCSD_BUS_WIDTH_4 ? "4" : "", + (io_cfg->bus_width) == MMCSD_BUS_WIDTH_1 ? "1" : "", + io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "", + io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "", + io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : ""); + + /* Clock */ + if (clk > host->freq_max) + clk = host->freq_max; + + if (clk < host->freq_min) + clk = host->freq_min; + + LOG_D("[%s] ExceptedFreq: %d kHz", NuSdh->name, clk / 1000); + if (NuSdh->u32CurClk != (clk / 1000)) + { + SDH_SetClock(sdh, clk / 1000); + NuSdh->u32CurClk = (clk / 1000); + } + + switch (io_cfg->power_mode) + { + case MMCSD_POWER_UP: + if (clk <= 400000) + { + /* power ON 74 clock */ + sdh->CTL |= SDH_CTL_CLK74OEN_Msk; + + while ((sdh->CTL & SDH_CTL_CLK74OEN_Msk) == SDH_CTL_CLK74OEN_Msk) + { + } + } + break; + + case MMCSD_POWER_ON: + break; + + case MMCSD_POWER_OFF: + break; + + default: + break; + } + + /* Bus width */ + switch ((io_cfg->bus_width)) + { + case MMCSD_BUS_WIDTH_1: + SDH_SetBusWidth(sdh, 1); + break; + + case MMCSD_BUS_WIDTH_4: + SDH_SetBusWidth(sdh, 4); + break; + + case MMCSD_BUS_WIDTH_8: + default: + break; + } + +} + +/** + * @brief This function detect sdcard. + * @param host rt_mmcsd_host + * @retval card detection status + */ +static rt_int32_t nu_sdh_card_detect(struct rt_mmcsd_host *host) +{ + nu_sdh_t NuSdh; + RT_ASSERT(host); + + NuSdh = (nu_sdh_t)host->private_data; + SDH_T *sdh = NuSdh->base; + + LOG_D("try to detect device"); + return SDH_GetCD(sdh); +} + +static void nu_sdh_isr(nu_sdh_t NuSdh) +{ + SDH_T *sdh = NuSdh->base; + uint32_t isr = sdh->INTSTS; + + /* card detected */ + if (isr & SDH_INTSTS_CDIF_Msk) + { + rt_tick_t cur_tick = rt_tick_get(); + rt_tick_t diff_tick; + + /* ready to change */ + if (cur_tick >= NuSdh->LastNotice) + diff_tick = (cur_tick - NuSdh->LastNotice); + else + diff_tick = ((rt_tick_t) -1) - NuSdh->LastNotice + cur_tick; + + if (!NuSdh->LastNotice || (diff_tick > (RT_TICK_PER_SECOND / 5))) // Debounce 200ms + { + NuSdh->LastNotice = cur_tick; + mmcsd_change(NuSdh->host); + } + + /* Clear CDIF interrupt flag */ + SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CDIF_Msk); + } +} + +#if defined(BSP_USING_SDH0) +void SDH0_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + nu_sdh_isr(&nu_sdh_arr[SDH0_IDX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif + +#if defined(BSP_USING_SDH1) +void SDH1_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + nu_sdh_isr(&nu_sdh_arr[SDH1_IDX]); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif + + +/** + * @brief This function update sdh interrupt. + * @param host rt_mmcsd_host + * @param enable + * @retval None + */ +void nu_sdh_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable) +{ + nu_sdh_t NuSdh = (nu_sdh_t)host->private_data; + SDH_T *sdh = NuSdh->base; + + if (enable) + { + LOG_D("Enable %s irq", NuSdh->name); + SDH_ENABLE_INT(sdh, SDH_INTSTS_CDIF_Msk); + } + else + { + LOG_D("Disable %s irq", NuSdh->name); + SDH_DISABLE_INT(sdh, SDH_INTSTS_CDIF_Msk); + } +} + +static const struct rt_mmcsd_host_ops ops = +{ + nu_sdh_request, + nu_sdh_iocfg, + nu_sdh_card_detect, + nu_sdh_irq_update, +}; + +/** + * @brief This function create mmcsd host. + * @param sdh nu_sdh_t + * @retval nuvton + */ +void nu_sdh_host_init(nu_sdh_t sdh) +{ + struct rt_mmcsd_host *host = mmcsd_alloc_host(); + RT_ASSERT(host); + + /* set host default attributes */ + host->ops = &ops; + host->freq_min = 300 * 1000; + host->freq_max = 48 * 1000 * 1000; + host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34; + + host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED; + + host->max_seg_size = SDH_BUFF_SIZE; + host->max_dma_segs = 1; + host->max_blk_size = SDH_BLOCK_SIZE; + host->max_blk_count = (SDH_BUFF_SIZE / SDH_BLOCK_SIZE); + + /* link up host and sdio */ + host->private_data = sdh; + sdh->host = host; + + /* Enable DMA engine at first. */ + SDH_Enable(sdh->base); + + /* Install ISR. */ + NVIC_EnableIRQ(sdh->irqn); + + /* ready to change */ + //mmcsd_change(host); +} + +static int rt_hw_sdh_init(void) +{ + int i; + + for (i = (SDH_START + 1); i < SDH_CNT; i++) + { + CLK_EnableModuleClock(nu_sdh_arr[i].modid); + SYS_ResetModule(nu_sdh_arr[i].rstidx); + + nu_sdh_host_init(&nu_sdh_arr[i]); + } + + return 0; +} +INIT_DEVICE_EXPORT(rt_hw_sdh_init); + +void nu_sd_attach(void) +{ + int i; + /* ready to change */ + for (i = (SDH_START + 1); i < SDH_CNT; i++) + { + if (nu_sdh_arr[i].host) + mmcsd_change(nu_sdh_arr[i].host); + } +} +MSH_CMD_EXPORT(nu_sd_attach, attach card); + +void nu_sd_regdump(void) +{ + int i; + for (i = (SDH_START + 1); i < SDH_CNT; i++) + { + if (nu_sdh_arr[i].host) + LOG_HEX("sdh_reg", 16, (void *)nu_sdh_arr[i].base, sizeof(SDH_T)); + } +} +MSH_CMD_EXPORT(nu_sd_regdump, dump sdh registers); + +#endif diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/SConscript b/bsp/nuvoton/libraries/nu_packages/FSA506/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..9534778e6f9d3a8fca11b474382d3b5a9ff380f3 --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/FSA506/SConscript @@ -0,0 +1,19 @@ +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +group = [] + +src = Split(""" +lcd_fsa506.c +""") +CPPPATH = [cwd] + +if GetDepend('NU_PKG_USING_FSA506_EBI'): + src += Glob('fsa506_ebi.c') + +if GetDepend('NU_PKG_USING_FSA506'): + group = DefineGroup('nu_pkgs_fsa506', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') + diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/fsa506_ebi.c b/bsp/nuvoton/libraries/nu_packages/FSA506/fsa506_ebi.c new file mode 100644 index 0000000000000000000000000000000000000000..de851408a9a6f9680a413bc2b931796d7691cf7f --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/FSA506/fsa506_ebi.c @@ -0,0 +1,124 @@ +/**************************************************************************//** +* +* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2022-2-23 Wayne First version +* +******************************************************************************/ + +#include + +#if defined(NU_PKG_USING_FSA506_EBI) + +#include +#include "drv_pdma.h" + +#define FSA506_ADDR_CMD 0x0 +#define FSA506_ADDR_DATA 0x0 + +#if defined(FSA506_EBI_16BIT) + #define fsa506_reg_write(RegAddr) (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_CMD))) = (RegAddr)) + #define fsa506_read_data() (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_DATA)))) + #define fsa506_write_data(Data) (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))) = (Data)) +#else + #define fsa506_reg_write(RegAddr) (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_CMD))) = (RegAddr)) + #define fsa506_read_data() (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_DATA)))) + #define fsa506_write_data(Data) (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))) = (Data)) +#endif + +static rt_uint32_t s_u32AccessBase = 0; + +void fsa506_send_cmd(rt_uint8_t cmd) +{ + CLR_RS; + fsa506_reg_write(cmd); + SET_RS; +} + +void fsa506_send_cmd_parameter(rt_uint8_t data) +{ + fsa506_write_data(data); +} + +void fsa506_send_cmd_done(void) +{ + CLR_RS; + fsa506_reg_write(0x80); + SET_RS; +} + +void fsa506_write_reg(rt_uint8_t cmd, rt_uint8_t data) +{ + fsa506_send_cmd(cmd); + fsa506_send_cmd_parameter(data); + fsa506_send_cmd_done(); +} + +void fsa506_send_pixel_data(rt_uint16_t color) +{ +#if 1 + // for LV_COLOR_16_SWAP + //BGR, B is high byte + fsa506_write_data(color & 0xffu); + fsa506_write_data((color >> 8) & 0xffu); +#else + //RGB, R is high byte + fsa506_write_data((color >> 8) & 0xffu); + fsa506_write_data(color & 0xffu); +#endif +} + +void fsa506_send_pixels(rt_uint16_t *pixels, int len) +{ + int count = len / sizeof(rt_uint16_t); + if (count < 512) + { + // CPU feed + int i = 0; + while (i < count) + { + fsa506_send_pixel_data(pixels[i]); + i++; + } + } + else + { + // PDMA-M2M feed + // Must enable LV_COLOR_16_SWAP definition in LVGL. + nu_pdma_mempush((void *)(s_u32AccessBase + (FSA506_ADDR_DATA)), (void *)pixels, 8, len); + } +} + +void fsa506_set_column(uint16_t StartCol, uint16_t EndCol) +{ + fsa506_write_reg(0x0, (StartCol >> 8) & 0xFF); + fsa506_write_reg(0x1, StartCol & 0xFF); + fsa506_write_reg(0x2, (EndCol >> 8) & 0xFF); + fsa506_write_reg(0x3, EndCol & 0xFF); +} + +void fsa506_set_page(uint16_t StartPage, uint16_t EndPage) +{ + fsa506_write_reg(0x4, (StartPage >> 8) & 0xFF); + fsa506_write_reg(0x5, StartPage & 0xFF); + fsa506_write_reg(0x6, (EndPage >> 8) & 0xFF); + fsa506_write_reg(0x7, EndPage & 0xFF); +} + +void fsa506_lcd_get_pixel(char *color, int x, int y) +{ + //Not supported. + return; +} + +rt_err_t rt_hw_lcd_fsa506_ebi_init(rt_uint32_t fsa506_base) +{ + s_u32AccessBase = fsa506_base; + return RT_EOK; +} + +#endif /* if defined(NU_PKG_USING_FSA506_EBI) */ diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.c b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.c new file mode 100644 index 0000000000000000000000000000000000000000..d172d7da6ad17dab0c5dc2544835766e30701dce --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.c @@ -0,0 +1,392 @@ +/**************************************************************************//** +* +* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2022-2-23 Wayne First version +* +******************************************************************************/ + +#include + +#if defined(NU_PKG_USING_FSA506) + +#include +#include + +#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER) + #if !defined(NU_PKG_FSA506_LINE_BUFFER_NUMBER) + #define NU_PKG_FSA506_LINE_BUFFER_NUMBER YSIZE_PHYS + #endif +#endif + +#define fsa506_delay_ms(ms) rt_thread_mdelay(ms) +static void fsa506_fillscreen(rt_uint16_t color); + +static struct rt_device_graphic_info g_FSA506Info = +{ + .bits_per_pixel = 16, + .pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565, + .framebuffer = RT_NULL, + .width = XSIZE_PHYS, + .pitch = XSIZE_PHYS * 2, + .height = YSIZE_PHYS +}; + +static rt_err_t fsa506_pin_init(void) +{ + rt_pin_mode(BOARD_USING_FSA506_PIN_DC, PIN_MODE_OUTPUT); + rt_pin_mode(BOARD_USING_FSA506_PIN_RESET, PIN_MODE_OUTPUT); + rt_pin_mode(BOARD_USING_FSA506_PIN_BACKLIGHT, PIN_MODE_OUTPUT); + rt_pin_mode(BOARD_USING_FSA506_PIN_DISPLAY, PIN_MODE_OUTPUT); + + CLR_RS; + CLR_RST; + SET_BACKLIGHT_OFF; + SET_DISP_OFF; + + return RT_EOK; +} + +static rt_err_t fsa506_lcd_init(rt_device_t dev) +{ + /* Hardware reset */ + CLR_RST; + fsa506_delay_ms(100); // Delay 100ms + + SET_RST; + fsa506_delay_ms(100); // Delay 100ms + + fsa506_write_reg(0x40, 0x12); // Underspece + fsa506_write_reg(0x41, 0x05); // Underspece + fsa506_write_reg(0x42, 0x06); // Underspece + + /* Set the panel X size */ + fsa506_write_reg(0x08, (uint8_t)(XSIZE_PHYS >> 8)); //Set the panel X size H[1.0] + fsa506_write_reg(0x09, (uint8_t)(XSIZE_PHYS)); //Set the panel X size L[7:0] + + /* Memory write start address */ + fsa506_write_reg(0x0a, 0x00); //[17:16] bits of memory write start address + fsa506_write_reg(0x0b, 0x00); //[15:8] bits of memory write start address + fsa506_write_reg(0x0c, 0x00); //[7:0] bits of memory write start address + + /* Clock & format */ + fsa506_write_reg(0x10, 0x0D); //[0-1] : 20MHz, [2]: Parallel panel, [3]: Normal operation + fsa506_write_reg(0x11, 0x05); //[3-5]: RGB, [0-2]BGR + + /* For TFT output timing adjust */ + fsa506_write_reg(0x12, 0x00); //Hsync start position H-Byte + fsa506_write_reg(0x13, 0x00); //Hsync start position L-Byte + fsa506_write_reg(0x14, (uint8_t)(41 >> 8)); //Hsync pulse width H-Byte + fsa506_write_reg(0x15, (uint8_t)(41)); //Hsync pulse width L-Byte + + fsa506_write_reg(0x16, (uint8_t)(43 >> 8)); //DE pulse start position H-Byte + fsa506_write_reg(0x17, (uint8_t)(43)); //DE pulse start position L-Byte + fsa506_write_reg(0x18, (uint8_t)(XSIZE_PHYS >> 8)); //DE pulse width H-Byte + fsa506_write_reg(0x19, (uint8_t)(XSIZE_PHYS)); //DE pulse width L-Byte + fsa506_write_reg(0x1a, (uint8_t)(525 >> 8)); //Hsync total clocks H-Byte + fsa506_write_reg(0x1b, (uint8_t)(525)); //Hsync total clocks H-Byte + fsa506_write_reg(0x1c, 0x00); //Vsync start position H-Byte + fsa506_write_reg(0x1d, 0x00); //Vsync start position L-Byte + fsa506_write_reg(0x1e, (uint8_t)(10 >> 8)); //Vsync pulse width H-Byte + fsa506_write_reg(0x1f, (uint8_t)(10)); //Vsync pulse width L-Byte + fsa506_write_reg(0x20, (uint8_t)(12 >> 8)); //Vertical DE pulse start position H-Byte + fsa506_write_reg(0x21, (uint8_t)(12)); //Vertical DE pulse start position L-Byte + fsa506_write_reg(0x22, (uint8_t)(YSIZE_PHYS >> 8)); //Vertical Active width H-Byte + fsa506_write_reg(0x23, (uint8_t)(YSIZE_PHYS)); //Vertical Active width H-Byte + fsa506_write_reg(0x24, (uint8_t)(286 >> 8)); //Vertical total width H-Byte + fsa506_write_reg(0x25, (uint8_t)(286)); //Vertical total width L-Byte + + fsa506_write_reg(0x26, 0x00); //Memory read start address + fsa506_write_reg(0x27, 0x00); //Memory read start address + fsa506_write_reg(0x28, 0x00); //Memory read start address + fsa506_write_reg(0x29, 0x01); //[0] Load output timing related setting (H sync., V sync. and DE) to take effect + + //[7:4] Reserved + //[3] Output pin X_DCON level control + //[2] Output clock inversion 0: Normal 1: Inverse + //[1:0] Image rotate + // 00: 0? 01: 90? 10: 270?11: 180? + fsa506_write_reg(0x2d, (1 << 2) | 0x08); + + /* Set the Horizontal offset */ + fsa506_write_reg(0x30, 0x00); //_H byte H-Offset[3:0] + fsa506_write_reg(0x31, 0x00); //_L byte H-Offset[7:0] + fsa506_write_reg(0x32, 0x00); //_H byte V-Offset[3:0] + fsa506_write_reg(0x33, 0x00); //_L byte V-Offset[7:0] + fsa506_write_reg(0x34, (uint8_t)(XSIZE_PHYS >> 8)); //H byte H-def[3:0] + fsa506_write_reg(0x35, (uint8_t)(XSIZE_PHYS)); //_L byte H-def[7:0] + fsa506_write_reg(0x36, (uint8_t)((2 * YSIZE_PHYS) >> 8)); //[3:0] MSB of image vertical physical resolution in memory + fsa506_write_reg(0x37, (uint8_t)(2 * YSIZE_PHYS)); //[7:0] LSB of image vertical physical resolution in memory + + fsa506_fillscreen(0); + + SET_DISP_ON; + + SET_BACKLIGHT_ON; + + return RT_EOK; +} + +#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER) +static void fsa506_fillrect(uint16_t *pixels, struct rt_device_rect_info *pRectInfo) +{ + fsa506_set_column(pRectInfo->x, pRectInfo->x + pRectInfo->width - 1); + fsa506_set_page(pRectInfo->y, pRectInfo->y + pRectInfo->height - 1); + + fsa506_send_cmd(0xC1); + fsa506_send_pixels(pixels, pRectInfo->height * pRectInfo->width * 2); + fsa506_send_cmd_done(); +} +#endif + +static void fsa506_fillscreen(rt_uint16_t color) +{ +#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER) + struct rt_device_rect_info rectinfo; + int filled_line_num = 0; + + while (filled_line_num < YSIZE_PHYS) + { + int pixel_count; + rectinfo.x = 0; + rectinfo.y = filled_line_num; + rectinfo.width = XSIZE_PHYS; + rectinfo.height = (NU_PKG_FSA506_LINE_BUFFER_NUMBER < YSIZE_PHYS) ? NU_PKG_FSA506_LINE_BUFFER_NUMBER : YSIZE_PHYS; + + pixel_count = XSIZE_PHYS * NU_PKG_FSA506_LINE_BUFFER_NUMBER; + rt_uint16_t *pu16ShadowBuf = (rt_uint16_t *)g_FSA506Info.framebuffer; + + while (pixel_count > 0) + { + *pu16ShadowBuf++ = color; + pixel_count--; + } + fsa506_fillrect((uint16_t *)g_FSA506Info.framebuffer, &rectinfo); + filled_line_num += NU_PKG_FSA506_LINE_BUFFER_NUMBER; + } +#else + fsa506_set_column(0, (XSIZE_PHYS - 1)); + fsa506_set_page(0, (YSIZE_PHYS - 1)); + + fsa506_send_cmd(0xC1); + for (int i = 0; i < (XSIZE_PHYS * YSIZE_PHYS); i++) + fsa506_send_pixel_data(color); + fsa506_send_cmd_done(); +#endif +} + +static void fsa506_lcd_set_pixel(const char *color, int x, int y) +{ + fsa506_set_column(x, x); + fsa506_set_page(y, y); + + fsa506_send_cmd(0xC1); + fsa506_send_pixel_data(*(uint16_t *)color); + fsa506_send_cmd_done(); +} + +static void fsa506_lcd_draw_hline(const char *pixel, int x1, int x2, int y) +{ + fsa506_set_column(x1, x2); + fsa506_set_page(y, y); + + fsa506_send_cmd(0xC1); + for (; x1 < x2; x1++) + fsa506_send_pixel_data(*(uint16_t *)pixel); + fsa506_send_cmd_done(); +} + +static void fsa506_lcd_draw_vline(const char *pixel, int x, int y1, int y2) +{ + fsa506_set_column(x, x); + fsa506_set_page(y1, y2); + + fsa506_send_cmd(0xC1); + for (; y1 < y2; y1++) + fsa506_send_pixel_data(*(uint16_t *)pixel); + fsa506_send_cmd_done(); +} + +static void fsa506_lcd_blit_line(const char *pixels, int x, int y, rt_size_t size) +{ + rt_uint16_t *ptr = (rt_uint16_t *)pixels; + + fsa506_set_column(x, x + size); + fsa506_set_page(y, y); + + fsa506_send_cmd(0xC1); + while (size--) + fsa506_send_pixel_data(*ptr++); + fsa506_send_cmd_done(); +} + +static rt_err_t fsa506_lcd_open(rt_device_t dev, rt_uint16_t oflag) +{ + return RT_EOK; +} + +static rt_err_t fsa506_lcd_close(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_err_t fsa506_lcd_control(rt_device_t dev, int cmd, void *args) +{ + switch (cmd) + { + case RTGRAPHIC_CTRL_GET_INFO: + { + struct rt_device_graphic_info *info; + + info = (struct rt_device_graphic_info *) args; + RT_ASSERT(info != RT_NULL); + rt_memcpy(args, (void *)&g_FSA506Info, sizeof(struct rt_device_graphic_info)); + } + break; + + case RTGRAPHIC_CTRL_RECT_UPDATE: + { +#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER) + struct rt_device_rect_info *psRectInfo = (struct rt_device_rect_info *)args; + rt_uint16_t *pixels = (rt_uint16_t *)g_FSA506Info.framebuffer; + RT_ASSERT(args); + + fsa506_fillrect(pixels, psRectInfo); +#else + /* nothong to be done */ +#endif + } + break; + + default: + break; + } + + return RT_EOK; +} + +static struct rt_device lcd_device; +static struct rt_device_graphic_ops fsa506_ops = +{ + fsa506_lcd_set_pixel, + fsa506_lcd_get_pixel, + fsa506_lcd_draw_hline, + fsa506_lcd_draw_vline, + fsa506_lcd_blit_line +}; + +int rt_hw_lcd_fsa506_init(void) +{ + fsa506_pin_init(); + + /* register lcd device */ + lcd_device.type = RT_Device_Class_Graphic; + lcd_device.init = fsa506_lcd_init; + lcd_device.open = fsa506_lcd_open; + lcd_device.close = fsa506_lcd_close; + lcd_device.control = fsa506_lcd_control; + lcd_device.read = RT_NULL; + lcd_device.write = RT_NULL; + + lcd_device.user_data = &fsa506_ops; + +#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER) + g_FSA506Info.framebuffer = rt_malloc_align((g_FSA506Info.pitch * NU_PKG_FSA506_LINE_BUFFER_NUMBER) + 32, 32); + RT_ASSERT(g_FSA506Info.framebuffer != RT_NULL); + g_FSA506Info.smem_len = g_FSA506Info.pitch * NU_PKG_FSA506_LINE_BUFFER_NUMBER; +#endif + + /* register graphic device driver */ + rt_device_register(&lcd_device, "lcd", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); + + return 0; +} + +#ifdef RT_USING_FINSH +#define LINE_LEN 32 +static void lcd_test(int argc, char *argv[]) +{ + uint16_t pixels[LINE_LEN]; + uint16_t color; + int x, y, i; + x = y = 100; + + fsa506_lcd_init(NULL); + + color = 0x0; //Black, RGB + rt_kprintf("Brush 0x%X on screen.\n", color); + fsa506_fillscreen(color); + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + + color = 0xffff; //White, RGB + rt_kprintf("Brush 0x%X on screen.\n", color); + fsa506_fillscreen(color); + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + + color = 0x1f; //Blue, RGB + rt_kprintf("Brush 0x%X on screen.\n", color); + fsa506_fillscreen(color); + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + + color = 0x07e0; //Green, RGB + rt_kprintf("Brush 0x%X on screen.\n", color); + fsa506_fillscreen(color); + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + + color = 0xf800; //Red, RGB + rt_kprintf("Brush 0x%X on screen.\n", color); + fsa506_fillscreen(color); + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + + color = 0xffff; //White, RGB + rt_kprintf("lcd draw hline, pixel: 0x%X, x1: %d, x2: %d, y: %d\n", color, x, x + 20, y); + fsa506_lcd_draw_hline((const char *)&color, x, x + 20, y); + + color = 0xffff; //White, RGB + rt_kprintf("lcd draw vline, pixel: 0x%X, x: %d, y: %d\n", color, y, y + 20); + fsa506_lcd_draw_vline((const char *)&color, x, y, y + 20); + + for (i = 0; i < LINE_LEN; i++) + pixels[i] = 20 + i * 5; + + x = y = 50; + rt_kprintf("lcd blit line, start: x: %d, y: %d\n", x, y); + fsa506_lcd_blit_line((const char *)&pixels[0], x, y, LINE_LEN); + + x = y = 200; + color = 0x07E0; //Green, RGB + rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + fsa506_lcd_set_pixel((const char *)&color, x, y); + color = 0x0; + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + + x = y = 200; + color = 0x1f; //Blue, RGB + rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + fsa506_lcd_set_pixel((const char *)&color, x, y); + color = 0x0; + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + + x = y = 200; + color = 0xf800; //Red, RGB + rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); + fsa506_lcd_set_pixel((const char *)&color, x, y); + color = 0x0; + fsa506_lcd_get_pixel((char *)&color, x, y); + rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y); +} +MSH_CMD_EXPORT(lcd_test, test lcd display); +#endif + +#endif /* if defined(NU_PKG_USING_FSA506) */ diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.h b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.h new file mode 100644 index 0000000000000000000000000000000000000000..42bd11780179f5a12be49513a16ae98014d2f7f2 --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.h @@ -0,0 +1,57 @@ +/**************************************************************************//** +* +* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2022-2-23 Wayne First version +* +******************************************************************************/ + +#ifndef __LCD_FSA506_H__ +#define __LCD_FSA506_H__ + +#include +#include + +#define SET_RS rt_pin_write(BOARD_USING_FSA506_PIN_DC, 1) +#define CLR_RS rt_pin_write(BOARD_USING_FSA506_PIN_DC, 0) + +#define SET_RST rt_pin_write(BOARD_USING_FSA506_PIN_RESET, 1) +#define CLR_RST rt_pin_write(BOARD_USING_FSA506_PIN_RESET, 0) + +#define SET_BACKLIGHT_ON rt_pin_write(BOARD_USING_FSA506_PIN_BACKLIGHT, 1) +#define SET_BACKLIGHT_OFF rt_pin_write(BOARD_USING_FSA506_PIN_BACKLIGHT, 0) + +#define SET_DISP_ON rt_pin_write(BOARD_USING_FSA506_PIN_DISPLAY, 1) +#define SET_DISP_OFF rt_pin_write(BOARD_USING_FSA506_PIN_DISPLAY, 0) + +// +// Physical display size +// +//#if defined(NU_PKG_FSA506_HORIZONTAL) +#define XSIZE_PHYS 480 +#define YSIZE_PHYS 272 +//#else +// #define XSIZE_PHYS 272 +// #define YSIZE_PHYS 480 +//#endif + +int rt_hw_lcd_fsa506_init(void); +void fsa506_send_cmd(rt_uint8_t cmd); +void fsa506_send_cmd_parameter(rt_uint8_t data); +void fsa506_send_cmd_done(void); +void fsa506_write_reg(rt_uint8_t cmd, rt_uint8_t data); +void fsa506_set_column(rt_uint16_t StartCol, rt_uint16_t EndCol); +void fsa506_set_page(rt_uint16_t StartPage, rt_uint16_t EndPage); +void fsa506_send_pixel_data(rt_uint16_t color); +void fsa506_lcd_get_pixel(char *color, int x, int y); +void fsa506_send_pixels(rt_uint16_t *pixels, int len); + +#if defined(NU_PKG_USING_FSA506_EBI) + rt_err_t rt_hw_lcd_fsa506_ebi_init(rt_uint32_t ebi_base); +#endif + +#endif /* __LCD_FSA506_H__ */ diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.c b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.c new file mode 100644 index 0000000000000000000000000000000000000000..f600f1bbd621dd0063f9536a12b497397b2f5c5b --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-04-11 Wayne First version + */ + +#include +#include + +#include + +#define DBG_TAG "ft5446" +#define DBG_LVL DBG_INFO +#include + +#include "ft5446.h" + +static struct rt_i2c_client ft5446_client; +static void ft5446_touch_up(void *buf, rt_int8_t id); +static rt_err_t ft5446_write_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t value) +{ + struct rt_i2c_msg msgs; + rt_uint8_t buf[2]; + + buf[0] = reg; + buf[1] = value; + + msgs.addr = dev->client_addr; + msgs.flags = RT_I2C_WR; + msgs.buf = buf; + msgs.len = sizeof(buf); + + if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1) + { + return RT_EOK; + } + else + { + return -RT_ERROR; + } +} + +static rt_err_t ft5446_read_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t len) +{ + struct rt_i2c_msg msgs[2]; + + msgs[0].addr = dev->client_addr; + msgs[0].flags = RT_I2C_WR; + msgs[0].buf = ® + msgs[0].len = FT_REGITER_LEN; + + msgs[1].addr = dev->client_addr; + msgs[1].flags = RT_I2C_RD; + msgs[1].buf = data; + msgs[1].len = len; + + if (rt_i2c_transfer(dev->bus, msgs, 2) == 2) + { + return RT_EOK; + } + else + { + return -RT_ERROR; + } +} + +static rt_int16_t pre_x[FT_MAX_TOUCH]; +static rt_int16_t pre_y[FT_MAX_TOUCH]; +static rt_int16_t pre_w[FT_MAX_TOUCH]; +static rt_uint8_t s_tp_dowm[FT_MAX_TOUCH]; + +static void ft5446_touch_up(void *buf, rt_int8_t id) +{ + struct rt_touch_data *read_data = (struct rt_touch_data *)buf; + + if (s_tp_dowm[id] == 1) + { + s_tp_dowm[id] = 0; + read_data[id].event = RT_TOUCH_EVENT_UP; + } + else + { + read_data[id].event = RT_TOUCH_EVENT_NONE; + } + + read_data[id].timestamp = rt_touch_get_ts(); + read_data[id].width = pre_w[id]; + read_data[id].x_coordinate = pre_x[id]; + read_data[id].y_coordinate = pre_y[id]; + read_data[id].track_id = id; + + pre_x[id] = -1; /* last point is none */ + pre_y[id] = -1; + pre_w[id] = -1; + + //LOG_I("%s (%d)\n", __func__, id); +} + +static void ft5446_touch_down(void *buf, rt_int8_t id, rt_int16_t x, rt_int16_t y, rt_int16_t w) +{ + struct rt_touch_data *read_data = (struct rt_touch_data *)buf; + + if (s_tp_dowm[id] == 1) + { + read_data[id].event = RT_TOUCH_EVENT_MOVE; + + } + else + { + read_data[id].event = RT_TOUCH_EVENT_DOWN; + s_tp_dowm[id] = 1; + } + + read_data[id].timestamp = rt_touch_get_ts(); + read_data[id].width = w; + read_data[id].x_coordinate = x; + read_data[id].y_coordinate = y; + read_data[id].track_id = id; + + pre_x[id] = x; /* save last point */ + pre_y[id] = y; + pre_w[id] = w; + + //LOG_I("%s (%d %d %d %d)\n", __func__, id, x, y, w ); +} + +static int8_t pre_id[FT_MAX_TOUCH]; +static S_FT_REGMAP sFtRegMap; +static rt_uint8_t pre_touch = 0; + +static rt_size_t ft5446_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num) +{ + int i; + + rt_err_t error = 0; + rt_int32_t touch_event, touchid; + + RT_ASSERT(touch); + RT_ASSERT(buf); + RT_ASSERT(read_num != 0); + RT_ASSERT(read_num <= FT_MAX_TOUCH); + + error = ft5446_read_reg(&ft5446_client, 0, (rt_uint8_t *)&sFtRegMap, sizeof(sFtRegMap)); + if (error) + { + LOG_E("get touch data failed, err:%d\n", error); + goto exit_read_point; + } + + if (sFtRegMap.u8TDStatus > FT_MAX_TOUCH) + { + LOG_E("FW report max point:%d > panel info. max:%d\n", sFtRegMap.u8TDStatus, FT_MAX_TOUCH); + goto exit_read_point; + } + + if (pre_touch > sFtRegMap.u8TDStatus) /* point up */ + { + for (i = 0; i < FT_MAX_TOUCH; i++) + { + rt_uint8_t j; + for (j = 0; j < sFtRegMap.u8TDStatus; j++) /* this time touch num */ + { + touchid = sFtRegMap.m_sTP[j].u8TouchID; + if (touchid >= 0x0f) + continue; + + if (pre_id[i] == touchid) /* this id is not free */ + break; + } + + if ((j == sFtRegMap.u8TDStatus) && (pre_id[i] != -1)) /* free this node */ + { + // LOG_I("free %d tid=%d\n", i, pre_id[i]); + ft5446_touch_up(buf, pre_id[i]); + pre_id[i] = -1; + } + } + } + + for (i = 0; i < sFtRegMap.u8TDStatus; i++) + { + touch_event = sFtRegMap.m_sTP[i].u8EvtFlag; + touchid = sFtRegMap.m_sTP[i].u8TouchID; + + //LOG_I("(%d/%d) %d %d\n", i, sFtRegMap.u8TDStatus, touchid, touch_event ); + + if (touchid >= 0x0f) + continue; + + pre_id[i] = touchid; + + if ((touch_event == FT_EVENTFLAG_PRESS_DOWN) || (touch_event == FT_EVENTFLAG_CONTACT)) + { + rt_uint16_t x, y, w; + + x = ((uint16_t)sFtRegMap.m_sTP[i].u8X_11_8 << 8) | sFtRegMap.m_sTP[i].u8X_7_0; + y = ((uint16_t)sFtRegMap.m_sTP[i].u8Y_11_8 << 8) | sFtRegMap.m_sTP[i].u8Y_7_0; + w = sFtRegMap.m_sTP[i].m_u8Weight; + + //LOG_I("[%d] (%d %d %d %d)\n", touch_event, touchid, x, y, w ); + + if (x >= touch->info.range_x || y >= touch->info.range_y) + { + LOG_E("invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n", + 0, x, touch->info.range_x, + 0, y, touch->info.range_y); + continue; + } + + ft5446_touch_down(buf, touchid, x, y, w); + } + else + { + // Up + ft5446_touch_up(buf, touchid); + } + + } // for (i = 0; i < sFtRegMap.u8TDStatus; i++) + + pre_touch = sFtRegMap.u8TDStatus; + + return read_num; + +exit_read_point: + + pre_touch = 0; + + return 0; +} + +static rt_err_t ft5446_control(struct rt_touch_device *touch, int cmd, void *arg) +{ + switch (cmd) + { + case RT_TOUCH_CTRL_GET_INFO: + { + struct rt_touch_info *info = (struct rt_touch_info *)arg; + RT_ASSERT(arg); + + rt_memcpy(info, &touch->info, sizeof(struct rt_touch_info)); + break; + } + case RT_TOUCH_CTRL_GET_ID: + break; + case RT_TOUCH_CTRL_SET_X_RANGE: + break; + case RT_TOUCH_CTRL_SET_Y_RANGE: + break; + case RT_TOUCH_CTRL_SET_X_TO_Y: + break; + case RT_TOUCH_CTRL_SET_MODE: + break; + default: + break; + } + return RT_EOK; +} + +static struct rt_touch_ops ft5446_touch_ops = +{ + .touch_readpoint = ft5446_read_point, + .touch_control = ft5446_control, +}; + +static void ft5446_init(struct rt_i2c_client *dev) +{ + ft5446_write_reg(dev, 0x0, 0); +} + +int rt_hw_ft5446_init(const char *name, struct rt_touch_config *cfg) +{ + struct rt_touch_device *touch_device = RT_NULL; + rt_uint32_t bus_speed = 400000; + + touch_device = (struct rt_touch_device *)rt_malloc(sizeof(struct rt_touch_device)); + if (touch_device == RT_NULL) + { + LOG_E("touch device malloc fail"); + return -RT_ERROR; + } + rt_memset((void *)touch_device, 0, sizeof(struct rt_touch_device)); + + /* hw init*/ + rt_pin_mode(*(rt_uint8_t *)cfg->user_data, PIN_MODE_OUTPUT); + + rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_LOW); + rt_thread_delay(5); + rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_HIGH); + rt_thread_delay(200); + + rt_pin_mode(cfg->irq_pin.pin, cfg->irq_pin.mode); + + ft5446_client.bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name); + + if (ft5446_client.bus == RT_NULL) + { + LOG_E("Can't find %s device", cfg->dev_name); + return -RT_ERROR; + } + + if (rt_device_open((rt_device_t)ft5446_client.bus, RT_DEVICE_FLAG_RDWR) != RT_EOK) + { + LOG_E("open %s device failed", cfg->dev_name); + return -RT_ERROR; + } + + if (rt_device_control((rt_device_t)ft5446_client.bus, RT_I2C_DEV_CTRL_CLK, &bus_speed) != RT_EOK) + { + LOG_E("control %s device failed", cfg->dev_name); + return -RT_ERROR; + } + + ft5446_client.client_addr = FT5446_ADDRESS; + + ft5446_init(&ft5446_client); + + rt_memset(&pre_x[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&pre_y[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&pre_w[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&s_tp_dowm[0], 0, FT_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&pre_id[0], 0xff, FT_MAX_TOUCH * sizeof(int8_t)); + + /* register touch device */ + touch_device->info.type = RT_TOUCH_TYPE_CAPACITANCE; + touch_device->info.vendor = RT_TOUCH_VENDOR_FT; + touch_device->info.range_x = BSP_LCD_WIDTH; + touch_device->info.range_y = BSP_LCD_HEIGHT; + touch_device->info.point_num = FT_MAX_TOUCH; + + rt_memcpy(&touch_device->config, cfg, sizeof(struct rt_touch_config)); + touch_device->ops = &ft5446_touch_ops; + + rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL); + + LOG_I("touch device ft5446 init success"); + + return RT_EOK; +} diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.h b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.h new file mode 100644 index 0000000000000000000000000000000000000000..e662ad5156b81572df15156d01ca8c3d9ea4e332 --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-04-11 Wayne the first version + */ + +#ifndef __FT5446_H__ +#define __FT5446_H__ + +#include "touch.h" + +#define FT_REGITER_LEN 1 +#define FT_MAX_TOUCH 5 +#define FT5446_ADDRESS 0x38 + +#pragma anon_unions + +typedef struct +{ + //03H + union + { + uint8_t m_u8XH; + struct + { + uint8_t u8X_11_8: 4; + uint8_t : 2; + uint8_t u8EvtFlag: 2; +#define FT_EVENTFLAG_PRESS_DOWN 0x0 +#define FT_EVENTFLAG_LIFT_UP 0x1 +#define FT_EVENTFLAG_CONTACT 0x2 +#define FT_EVENTFLAG_NONE 0x3 + }; + }; + + //04H + union + { + uint8_t m_u8XL; + struct + { + uint8_t u8X_7_0; + }; + }; + + //05H + union + { + uint8_t m_u8YH; + struct + { + uint8_t u8Y_11_8: 4; + uint8_t u8TouchID: 4; /* Touch ID of Touch Point, this value is 0x0F when the ID is invalid */ + }; + }; + + //06H + union + { + uint8_t m_u8YL; + struct + { + uint8_t u8Y_7_0; + }; + }; + + //07H + uint8_t m_u8Weight; /* Touch pressure value */ + + //08H + union + { + uint8_t m_u8Misc; + struct + { + uint8_t : 4; + uint8_t u8TouchArea: 4; /* Touch area value */ + }; + }; + +} S_FT_TP; + +#pragma pack(push) +#pragma pack(4) + +typedef struct +{ + union + { + uint8_t m_u8ModeSwitch; +#define FT_DEVICE_MODE_WORKING 0x0 +#define FT_DEVICE_MODE_TEST 0x4 + + struct + { + uint8_t : 4; + uint8_t u8DevMode: 3; + uint8_t : 1; + }; + }; + + uint8_t m_u8Guesture; +#define FT_GESTURE_ID_MOVE_UP 0x10 +#define FT_GESTURE_ID_MOVE_RIGHT 0x14 +#define FT_GESTURE_ID_MOVE_DOWN 0x18 +#define FT_GESTURE_ID_MOVE_LEFT 0x1C +#define FT_GESTURE_ID_MOVE_IN 0x48 +#define FT_GESTURE_ID_MOVE_OUT 0x49 +#define FT_GESTURE_ID_MOVE_NONE 0x00 + + union + { + uint8_t m_u8Status; + + struct + { + uint8_t u8TDStatus: 4; + uint8_t : 4; + }; + }; + + S_FT_TP m_sTP[FT_MAX_TOUCH]; + +} S_FT_REGMAP; +#pragma pack(pop) + +int rt_hw_ft5446_init(const char *name, struct rt_touch_config *cfg); + +#endif /* __FT5446_H__ */ diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.c b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.c new file mode 100644 index 0000000000000000000000000000000000000000..3b516dc7a4a4c284c68e9962b621c31b6f459a9b --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-04-11 Wayne First version + */ + +#include +#include + +#include + +#define DBG_TAG "st1663i" +#define DBG_LVL DBG_INFO +#include + +#include "st1663i.h" + +static struct rt_i2c_client st1663i_client; +static void st1663i_touch_up(void *buf, rt_int8_t id); +static rt_err_t st1663i_write_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t value) +{ + struct rt_i2c_msg msgs; + rt_uint8_t buf[2]; + + buf[0] = reg; + buf[1] = value; + + msgs.addr = dev->client_addr; + msgs.flags = RT_I2C_WR; + msgs.buf = buf; + msgs.len = sizeof(buf); + + if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1) + { + return RT_EOK; + } + else + { + return -RT_ERROR; + } +} + +static rt_err_t st1663i_read_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t len) +{ + struct rt_i2c_msg msgs[2]; + + msgs[0].addr = dev->client_addr; + msgs[0].flags = RT_I2C_WR; + msgs[0].buf = ® + msgs[0].len = ST_REGITER_LEN; + + msgs[1].addr = dev->client_addr; + msgs[1].flags = RT_I2C_RD; + msgs[1].buf = data; + msgs[1].len = len; + + if (rt_i2c_transfer(dev->bus, msgs, 2) == 2) + { + return RT_EOK; + } + else + { + return -RT_ERROR; + } +} + +static rt_int16_t pre_x[ST_MAX_TOUCH]; +static rt_int16_t pre_y[ST_MAX_TOUCH]; +static rt_int16_t pre_w[ST_MAX_TOUCH]; +static rt_uint8_t s_tp_dowm[ST_MAX_TOUCH]; + +static void st1663i_touch_up(void *buf, rt_int8_t id) +{ + struct rt_touch_data *read_data = (struct rt_touch_data *)buf; + + if (s_tp_dowm[id] == 1) + { + s_tp_dowm[id] = 0; + read_data[id].event = RT_TOUCH_EVENT_UP; + } + else + { + read_data[id].event = RT_TOUCH_EVENT_NONE; + } + + read_data[id].timestamp = rt_touch_get_ts(); + read_data[id].width = pre_w[id]; + read_data[id].x_coordinate = pre_x[id]; + read_data[id].y_coordinate = pre_y[id]; + read_data[id].track_id = id; + + pre_x[id] = -1; /* last point is none */ + pre_y[id] = -1; + pre_w[id] = -1; + + //LOG_I("%s (%d)\n", __func__, id); +} + +static void st1663i_touch_down(void *buf, rt_int8_t id, rt_int16_t x, rt_int16_t y, rt_int16_t w) +{ + struct rt_touch_data *read_data = (struct rt_touch_data *)buf; + + if (s_tp_dowm[id] == 1) + { + read_data[id].event = RT_TOUCH_EVENT_MOVE; + + } + else + { + read_data[id].event = RT_TOUCH_EVENT_DOWN; + s_tp_dowm[id] = 1; + } + + read_data[id].timestamp = rt_touch_get_ts(); + read_data[id].width = w; + read_data[id].x_coordinate = x; + read_data[id].y_coordinate = y; + read_data[id].track_id = id; + + pre_x[id] = x; /* save last point */ + pre_y[id] = y; + pre_w[id] = w; + + //LOG_I("%s (%d %d %d %d)\n", __func__, id, x, y, w ); +} + +static int8_t pre_id[ST_MAX_TOUCH]; +static S_ST_REGMAP sStRegMap; +static rt_uint8_t pre_touch = 0; + +static rt_size_t st1663i_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num) +{ + int i; + + rt_err_t error = 0; + rt_int32_t touch_event, touchid; + + RT_ASSERT(touch); + RT_ASSERT(buf); + RT_ASSERT(read_num != 0); + RT_ASSERT(read_num <= ST_MAX_TOUCH); + + error = st1663i_read_reg(&st1663i_client, 0x10, (rt_uint8_t *)&sStRegMap, sizeof(sStRegMap)); + if (error) + { + LOG_E("get touch data failed, err:%d\n", error); + goto exit_read_point; + } + + if (sStRegMap.u8Fingers > ST_MAX_TOUCH) + { + LOG_E("FW report max point:%d > panel info. max:%d\n", sStRegMap.u8Fingers, ST_MAX_TOUCH); + goto exit_read_point; + } + + if (pre_touch > sStRegMap.u8Fingers) /* point up */ + { + for (i = 0; i < ST_MAX_TOUCH; i++) + { + rt_uint8_t j; + for (j = 0; j < sStRegMap.u8Fingers; j++) /* this time touch num */ + { + touchid = i; + + if (pre_id[i] == touchid) /* this id is not free */ + break; + } + + if ((j == sStRegMap.u8Fingers) && (pre_id[i] != -1)) /* free this node */ + { + // LOG_I("free %d tid=%d\n", i, pre_id[i]); + st1663i_touch_up(buf, pre_id[i]); + pre_id[i] = -1; + } + } + } + + for (i = 0; i < sStRegMap.u8Fingers; i++) + { + touch_event = sStRegMap.m_sTP[i].u8Valid; + touchid = i; + + //LOG_I("(%d/%d) %d %d\n", i, sStRegMap.u8Fingers, touchid, touch_event); + + pre_id[i] = touchid; + + if (touch_event) + { + rt_uint16_t x, y, w; + + x = ((uint16_t)sStRegMap.m_sTP[i].u8X0_H << 8) | sStRegMap.m_sTP[i].m_u8X0_L; + y = ((uint16_t)sStRegMap.m_sTP[i].u8Y0_H << 8) | sStRegMap.m_sTP[i].m_u8Y0_L; + w = sStRegMap.m_sTP[i].m_u8Z; + + //LOG_I("[%d] (%d %d %d %d)\n", touch_event, touchid, x, y, w); + + if (x >= touch->info.range_x || y >= touch->info.range_y) + { + LOG_E("invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n", + 0, x, touch->info.range_x, + 0, y, touch->info.range_y); + continue; + } + + st1663i_touch_down(buf, touchid, x, y, w); + } + else + { + // Up + st1663i_touch_up(buf, touchid); + } + + } // for (i = 0; i < sStRegMap.u8TDStatus; i++) + + pre_touch = sStRegMap.u8Fingers; + + return read_num; + +exit_read_point: + + pre_touch = 0; + + return 0; +} + +static rt_err_t st1663i_control(struct rt_touch_device *touch, int cmd, void *arg) +{ + switch (cmd) + { + case RT_TOUCH_CTRL_GET_INFO: + { + struct rt_touch_info *info = (struct rt_touch_info *)arg; + RT_ASSERT(arg); + + rt_memcpy(info, &touch->info, sizeof(struct rt_touch_info)); + break; + } + case RT_TOUCH_CTRL_GET_ID: + break; + case RT_TOUCH_CTRL_SET_X_RANGE: + break; + case RT_TOUCH_CTRL_SET_Y_RANGE: + break; + case RT_TOUCH_CTRL_SET_X_TO_Y: + break; + case RT_TOUCH_CTRL_SET_MODE: + break; + default: + break; + } + return RT_EOK; +} + +static struct rt_touch_ops st1663i_touch_ops = +{ + .touch_readpoint = st1663i_read_point, + .touch_control = st1663i_control, +}; + +static void st1663i_init(struct rt_i2c_client *dev) +{ + st1663i_write_reg(dev, 0x0, 0); +} + +int rt_hw_st1663i_init(const char *name, struct rt_touch_config *cfg) +{ + struct rt_touch_device *touch_device = RT_NULL; + rt_uint32_t bus_speed = 400000; + + touch_device = (struct rt_touch_device *)rt_malloc(sizeof(struct rt_touch_device)); + if (touch_device == RT_NULL) + { + LOG_E("touch device malloc fail"); + return -RT_ERROR; + } + rt_memset((void *)touch_device, 0, sizeof(struct rt_touch_device)); + + /* hw init*/ + rt_pin_mode(*(rt_uint8_t *)cfg->user_data, PIN_MODE_OUTPUT); + + rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_LOW); + rt_thread_delay(5); + rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_HIGH); + rt_thread_delay(200); + + rt_pin_mode(cfg->irq_pin.pin, cfg->irq_pin.mode); + + st1663i_client.bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name); + + if (st1663i_client.bus == RT_NULL) + { + LOG_E("Can't find %s device", cfg->dev_name); + return -RT_ERROR; + } + + if (rt_device_open((rt_device_t)st1663i_client.bus, RT_DEVICE_FLAG_RDWR) != RT_EOK) + { + LOG_E("open %s device failed", cfg->dev_name); + return -RT_ERROR; + } + + if (rt_device_control((rt_device_t)st1663i_client.bus, RT_I2C_DEV_CTRL_CLK, &bus_speed) != RT_EOK) + { + LOG_E("control %s device failed", cfg->dev_name); + return -RT_ERROR; + } + + st1663i_client.client_addr = ST1663I_ADDRESS; + + st1663i_init(&st1663i_client); + + rt_memset(&pre_x[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&pre_y[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&pre_w[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&s_tp_dowm[0], 0, ST_MAX_TOUCH * sizeof(int16_t)); + rt_memset(&pre_id[0], 0xff, ST_MAX_TOUCH * sizeof(int8_t)); + + /* register touch device */ + touch_device->info.type = RT_TOUCH_TYPE_CAPACITANCE; + touch_device->info.vendor = RT_TOUCH_VENDOR_UNKNOWN; + touch_device->info.range_x = BSP_LCD_WIDTH; + touch_device->info.range_y = BSP_LCD_HEIGHT; + touch_device->info.point_num = ST_MAX_TOUCH; + + rt_memcpy(&touch_device->config, cfg, sizeof(struct rt_touch_config)); + touch_device->ops = &st1663i_touch_ops; + + rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL); + + LOG_I("touch device st1663i init success"); + + return RT_EOK; +} diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.h b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.h new file mode 100644 index 0000000000000000000000000000000000000000..5bfbd406019db88e790889b4812a39571edac540 --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-04-11 Wayne the first version + */ + +#ifndef __ST1663I_H__ +#define __ST1663I_H__ + +#include "touch.h" + +#define ST_REGITER_LEN 1 +#define ST_MAX_TOUCH 5 +#define ST1663I_ADDRESS 0x55 + +#pragma anon_unions + +typedef struct +{ + //012H*n+0 (n=0, 1, ...,4) + union + { + uint8_t m_u8XY0_H; + struct + { + uint8_t u8Y0_H: 3; + uint8_t : 1; + uint8_t u8X0_H: 3; + uint8_t u8Valid: 1; + }; + }; + + //012H*n+1 (n=0, 1, ...,4) + uint8_t m_u8X0_L; + + //012H*n+2 (n=0, 1, ...,4) + uint8_t m_u8Y0_L; + + //012H*n+3 (n=0, 1, ...,4) + uint8_t m_u8Z; + +} S_ST_TP; + + +#pragma pack(push) +#pragma pack(4) + +typedef struct +{ + union + { + uint8_t m_u8TouchInfo; + struct + { + uint8_t u8Fingers: 4; + uint8_t : 4; + }; + }; + + uint8_t m_u8Keys; + + S_ST_TP m_sTP[ST_MAX_TOUCH]; + +} S_ST_REGMAP; +#pragma pack(pop) + +int rt_hw_st1663i_init(const char *name, struct rt_touch_config *cfg); + +#endif /* __ST1663I_H__ */ diff --git a/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.c b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.c new file mode 100644 index 0000000000000000000000000000000000000000..e0dca96f554b94308b97a4fa5f5b075c71148a7a --- /dev/null +++ b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-12-17 Wayne The first version + */ +/** + * @file lv_gpu_n9h30_ge2d.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include + +#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0) + +#include "lv_gpu_n9h30_ge2d.h" +#include "nu_2d.h" +#include "mmu.h" + +/********************* + * DEFINES + *********************/ + +#if LV_COLOR_16_SWAP + #error "Can't use GE2D with LV_COLOR_16_SWAP 1" +#endif + +#if !((LV_COLOR_DEPTH == 16) || (LV_COLOR_DEPTH == 32)) + /*Can't use GPU with other formats*/ + #error "Can't use GPU with other formats" +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_draw_n9h30_ge2d_blend_fill(lv_color_t *dest_buf, lv_coord_t dest_stride, const lv_area_t *fill_area, + lv_color_t color); + + +static void lv_draw_n9h30_ge2d_blend_map(lv_color_t *dest_buf, const lv_area_t *dest_area, lv_coord_t dest_stride, + const lv_color_t *src_buf, lv_coord_t src_stride, lv_opa_t opa); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Turn on the peripheral and set output color mode, this only needs to be done once + */ +void lv_draw_n9h30_ge2d_ctx_init(lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx) +{ + lv_draw_sw_init_ctx(drv, draw_ctx); + + lv_draw_n9h30_ge2d_ctx_t *ge2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx; + + ge2d_draw_ctx->blend = lv_draw_n9h30_ge2d_blend; + ge2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_n9h30_ge2d_wait_cb; +} + +void lv_draw_n9h30_ge2d_ctx_deinit(lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx) +{ + LV_UNUSED(drv); + LV_UNUSED(draw_ctx); +} + +void lv_draw_n9h30_ge2d_blend(lv_draw_ctx_t *draw_ctx, const lv_draw_sw_blend_dsc_t *dsc) +{ + lv_area_t blend_area; + int32_t blend_area_w; + bool done = false; + + if (!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return; + + blend_area_w = lv_area_get_width(&blend_area); + + if ((lv_area_get_size(&blend_area) > 7200) && + (((blend_area_w * (LV_COLOR_DEPTH / 2)) & 0x3) == 0) && + (dsc->mask_buf == NULL) && (dsc->blend_mode == LV_BLEND_MODE_NORMAL)) + { + lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); + + lv_color_t *dest_buf = draw_ctx->buf; + dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1); + + const lv_color_t *src_buf = dsc->src_buf; + if (src_buf) + { + lv_coord_t src_stride; + src_stride = lv_area_get_width(dsc->blend_area); + src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1); + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + lv_draw_n9h30_ge2d_blend_map(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa); + done = true; + } + else if (dsc->opa >= LV_OPA_MAX) + { + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + lv_draw_n9h30_ge2d_blend_fill(dest_buf, dest_stride, &blend_area, dsc->color); + done = true; + } + } + + if (!done) lv_draw_sw_blend_basic(draw_ctx, dsc); +} + +static void lv_draw_n9h30_ge2d_blend_fill(lv_color_t *dest_buf, lv_coord_t dest_stride, const lv_area_t *fill_area, + lv_color_t color) +{ + int32_t fill_area_w = lv_area_get_width(fill_area); + int32_t fill_area_h = lv_area_get_height(fill_area); + + lv_color_t *start_buf = dest_buf - (fill_area->y1 * dest_stride) - fill_area->x1; + + //rt_kprintf("[blend_fill %d %08x] %dx%d %d %d\n", lv_area_get_size(fill_area), dest_buf, fill_area_w, fill_area_h, fill_area->x1, fill_area->y1 ); + + if (IS_CACHEABLE_VRAM(dest_buf)) + mmu_clean_invalidated_dcache((uint32_t)dest_buf, sizeof(lv_color_t) * (dest_stride * fill_area_h + fill_area_w)); + + /*Hardware filling*/ + // Enter GE2D -> + ge2dInit(sizeof(lv_color_t) * 8, dest_stride, fill_area->y2, (void *)start_buf); + + ge2dClip_SetClip(fill_area->x1, fill_area->y1, fill_area->x2, fill_area->y2); + + if (sizeof(lv_color_t) == 4) + ge2dFill_Solid(fill_area->x1, fill_area->y1, fill_area_w, fill_area_h, color.full); + else if (sizeof(lv_color_t) == 2) + ge2dFill_Solid_RGB565(fill_area->x1, fill_area->y1, fill_area_w, fill_area_h, color.full); + + ge2dClip_SetClip(-1, 0, 0, 0); + // -> Leave GE2D +} + +static void lv_draw_n9h30_ge2d_blend_map(lv_color_t *dest_buf, const lv_area_t *dest_area, lv_coord_t dest_stride, + const lv_color_t *src_buf, lv_coord_t src_stride, lv_opa_t opa) +{ + /*Simple copy*/ + int32_t dest_x = dest_area->x1; + int32_t dest_y = dest_area->y1; + int32_t dest_w = lv_area_get_width(dest_area); + int32_t dest_h = lv_area_get_height(dest_area); + + const lv_color_t *dest_start_buf = dest_buf - (dest_area->y1 * dest_stride) - dest_area->x1; + + //rt_kprintf("[blend_map %d %08x -> %08x] (x:%d y:%d, %dx%d) \n", lv_area_get_size(dest_area), src_buf, dest_buf, dest_x, dest_y, dest_w, dest_h, src_stride, dest_stride); + + // Enter GE2D -> + ge2dInit(sizeof(lv_color_t) * 8, dest_stride, dest_area->y2, (void *)dest_start_buf); + + if (opa >= LV_OPA_MAX) + { + ge2dBitblt_SetAlphaMode(0, 0, 0); + ge2dBitblt_SetDrawMode(0, 0, 0); + } + else + { + ge2dBitblt_SetAlphaMode(1, opa, opa); + } + + // flush + mmu_clean_dcache((uint32_t)src_buf, sizeof(lv_color_t) * (src_stride * dest_h + dest_w)); + + ge2dSpriteBlt_Screen(dest_x, dest_y, dest_w, dest_h, (void *)src_buf); + // -> Leave GE2D +} + +void lv_gpu_n9h30_ge2d_wait_cb(lv_draw_ctx_t *draw_ctx) +{ + lv_draw_sw_wait_for_finish(draw_ctx); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif // #if (LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)) diff --git a/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.h b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.h new file mode 100644 index 0000000000000000000000000000000000000000..0852168ebde4dbb6d233f4d77583415d669771ee --- /dev/null +++ b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-29 Wayne The first version + */ +#ifndef LV_GPU_N9H_GE2D_H +#define LV_GPU_N9H_GE2D_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../misc/lv_color.h" +#include "../../hal/lv_hal_disp.h" +#include "../sw/lv_draw_sw.h" + +#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0) + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +struct _lv_disp_drv_t; +typedef lv_draw_sw_ctx_t lv_draw_n9h30_ge2d_ctx_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Turn on the peripheral and set output color mode, this only needs to be done once + */ +void lv_draw_n9h30_ge2d_init(void); + +void lv_draw_n9h30_ge2d_ctx_init(struct _lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx); + +void lv_draw_n9h30_ge2d_ctx_deinit(struct _lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx); + +void lv_draw_n9h30_ge2d_blend(lv_draw_ctx_t *draw_ctx, const lv_draw_sw_blend_dsc_t *dsc); + +void lv_gpu_n9h30_ge2d_wait_cb(lv_draw_ctx_t *draw_ctx); + +/********************** + * MACROS + **********************/ + +#endif /*#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_N9H_GE2D_H*/ +