drivers: dma: introduce SMARTDMA dma driver
Introduce SMARTDMA dma driver. The SMARTDMA is a peripheral present on some NXP SOCs, which implements a programmable DMA engine. The DMA engine does not use channels, but rather provides a series of API functions implemented by the firmware provided with MCUX SDK. These API functions can be selected by the dma_config slot parameter. A custom API is also provided to allow the user to install an alternate firmware into the SMARTDMA. Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
9bcd8e9b3e
commit
8d2f4633f2
|
@ -32,3 +32,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_MCHP_XEC dma_mchp_xec.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_DMA_XMC4XXX dma_xmc4xxx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_DMA_RPI_PICO dma_rpi_pico.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MCUX_PXP dma_mcux_pxp.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_DMA_MCUX_SMARTDMA dma_mcux_smartdma.c)
|
||||
|
|
|
@ -61,4 +61,7 @@ source "drivers/dma/Kconfig.rpi_pico"
|
|||
source "drivers/dma/Kconfig.intel_lpss"
|
||||
|
||||
source "drivers/dma/Kconfig.mcux_pxp"
|
||||
|
||||
source "drivers/dma/Kconfig.mcux_smartdma"
|
||||
|
||||
endif # DMA
|
||||
|
|
9
drivers/dma/Kconfig.mcux_smartdma
Normal file
9
drivers/dma/Kconfig.mcux_smartdma
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright 2023 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config DMA_MCUX_SMARTDMA
|
||||
bool "MCUX SmartDMA Driver"
|
||||
default y
|
||||
depends on DT_HAS_NXP_SMARTDMA_ENABLED
|
||||
help
|
||||
MCUX SmartDMA driver.
|
252
drivers/dma/dma_mcux_smartdma.c
Normal file
252
drivers/dma/dma_mcux_smartdma.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/dma.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/pm/policy.h>
|
||||
#include <zephyr/drivers/dma/dma_mcux_smartdma.h>
|
||||
|
||||
#include <soc.h>
|
||||
#include <fsl_smartdma.h>
|
||||
#include <fsl_inputmux.h>
|
||||
#include <fsl_power.h>
|
||||
|
||||
#define DT_DRV_COMPAT nxp_smartdma
|
||||
|
||||
LOG_MODULE_REGISTER(dma_mcux_smartdma, CONFIG_DMA_LOG_LEVEL);
|
||||
|
||||
/* SMARTDMA peripheral registers, taken from MCUX driver implementation*/
|
||||
struct smartdma_periph {
|
||||
volatile uint32_t BOOT;
|
||||
volatile uint32_t CTRL;
|
||||
volatile uint32_t PC;
|
||||
volatile uint32_t SP;
|
||||
volatile uint32_t BREAK_ADDR;
|
||||
volatile uint32_t BREAK_VECT;
|
||||
volatile uint32_t EMER_VECT;
|
||||
volatile uint32_t EMER_SEL;
|
||||
volatile uint32_t ARM2SMARTDMA;
|
||||
volatile uint32_t SMARTDMA2ARM;
|
||||
volatile uint32_t PENDTRAP;
|
||||
};
|
||||
|
||||
struct dma_mcux_smartdma_config {
|
||||
struct smartdma_periph *base;
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
|
||||
void (**smartdma_progs)(void);
|
||||
};
|
||||
|
||||
struct dma_mcux_smartdma_data {
|
||||
uint32_t smartdma_stack[32]; /* Stack for SMARTDMA */
|
||||
/* Installed DMA callback and user data */
|
||||
dma_callback_t callback;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/* Seems to be written to smartDMA control register when it is configured */
|
||||
#define SMARTDMA_MAGIC 0xC0DE0000U
|
||||
/* These bits are set when the SMARTDMA boots, cleared to reset it */
|
||||
#define SMARTDMA_BOOT 0x11
|
||||
|
||||
static inline bool dma_mcux_smartdma_prog_is_mipi(uint32_t prog)
|
||||
{
|
||||
return ((prog == kSMARTDMA_MIPI_RGB565_DMA) ||
|
||||
(prog == kSMARTDMA_MIPI_RGB888_DMA) ||
|
||||
(prog == kSMARTDMA_MIPI_RGB565_R180_DMA) ||
|
||||
(prog == kSMARTDMA_MIPI_RGB888_R180_DMA));
|
||||
}
|
||||
|
||||
/* Configure a channel */
|
||||
static int dma_mcux_smartdma_configure(const struct device *dev,
|
||||
uint32_t channel, struct dma_config *config)
|
||||
{
|
||||
const struct dma_mcux_smartdma_config *dev_config = dev->config;
|
||||
struct dma_mcux_smartdma_data *data = dev->data;
|
||||
uint32_t prog_idx;
|
||||
bool swap_pixels = false;
|
||||
|
||||
/* SMARTDMA does not have channels */
|
||||
ARG_UNUSED(channel);
|
||||
|
||||
data->callback = config->dma_callback;
|
||||
data->user_data = config->user_data;
|
||||
|
||||
/* Reset smartDMA */
|
||||
SMARTDMA_Reset();
|
||||
|
||||
/*
|
||||
* The dma_slot parameter is used to determine which SMARTDMA program
|
||||
* to run. First, convert the Zephyr define to a HAL enum.
|
||||
*/
|
||||
switch (config->dma_slot) {
|
||||
case DMA_SMARTDMA_MIPI_RGB565_DMA:
|
||||
prog_idx = kSMARTDMA_MIPI_RGB565_DMA;
|
||||
break;
|
||||
case DMA_SMARTDMA_MIPI_RGB888_DMA:
|
||||
prog_idx = kSMARTDMA_MIPI_RGB888_DMA;
|
||||
break;
|
||||
case DMA_SMARTDMA_MIPI_RGB565_180:
|
||||
prog_idx = kSMARTDMA_MIPI_RGB565_R180_DMA;
|
||||
break;
|
||||
case DMA_SMARTDMA_MIPI_RGB888_180:
|
||||
prog_idx = kSMARTDMA_MIPI_RGB888_R180_DMA;
|
||||
break;
|
||||
case DMA_SMARTDMA_MIPI_RGB565_DMA_SWAP:
|
||||
swap_pixels = true;
|
||||
prog_idx = kSMARTDMA_MIPI_RGB565_DMA;
|
||||
break;
|
||||
case DMA_SMARTDMA_MIPI_RGB888_DMA_SWAP:
|
||||
swap_pixels = true;
|
||||
prog_idx = kSMARTDMA_MIPI_RGB888_DMA;
|
||||
break;
|
||||
case DMA_SMARTDMA_MIPI_RGB565_180_SWAP:
|
||||
swap_pixels = true;
|
||||
prog_idx = kSMARTDMA_MIPI_RGB565_R180_DMA;
|
||||
break;
|
||||
case DMA_SMARTDMA_MIPI_RGB888_180_SWAP:
|
||||
swap_pixels = true;
|
||||
prog_idx = kSMARTDMA_MIPI_RGB888_R180_DMA;
|
||||
break;
|
||||
default:
|
||||
prog_idx = config->dma_slot;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dma_mcux_smartdma_prog_is_mipi(prog_idx)) {
|
||||
smartdma_dsi_param_t param = {.disablePixelByteSwap = (swap_pixels == false)};
|
||||
|
||||
if (config->block_count != 1) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
/* Setup SMARTDMA */
|
||||
param.p_buffer = (uint8_t *)config->head_block->source_address;
|
||||
param.buffersize = config->head_block->block_size;
|
||||
param.smartdma_stack = data->smartdma_stack;
|
||||
/* Save configuration to SMARTDMA */
|
||||
dev_config->base->ARM2SMARTDMA = (uint32_t)(¶m);
|
||||
} else {
|
||||
/* For other cases, we simply pass the entire DMA config
|
||||
* struct to the SMARTDMA. The user's application could either
|
||||
* populate this structure with data, or choose to write
|
||||
* different configuration data to the SMARTDMA in their
|
||||
* application
|
||||
*/
|
||||
dev_config->base->ARM2SMARTDMA = ((uint32_t)config);
|
||||
}
|
||||
/* Save program */
|
||||
dev_config->base->BOOT = (uint32_t)dev_config->smartdma_progs[prog_idx];
|
||||
LOG_DBG("Boot address set to 0x%X", dev_config->base->BOOT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dma_mcux_smartdma_start(const struct device *dev, uint32_t channel)
|
||||
{
|
||||
const struct dma_mcux_smartdma_config *config = dev->config;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* Block PM transition until DMA completes */
|
||||
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
|
||||
#endif
|
||||
/* Kick off SMARTDMA */
|
||||
config->base->CTRL = SMARTDMA_MAGIC | SMARTDMA_BOOT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dma_mcux_smartdma_stop(const struct device *dev, uint32_t channel)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
ARG_UNUSED(channel);
|
||||
/* Stop DMA */
|
||||
SMARTDMA_Reset();
|
||||
#ifdef CONFIG_PM
|
||||
/* Release PM lock */
|
||||
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dma_mcux_smartdma_init(const struct device *dev)
|
||||
{
|
||||
const struct dma_mcux_smartdma_config *config = dev->config;
|
||||
/*
|
||||
* Initialize the SMARTDMA with firmware. The default firmware
|
||||
* from MCUX SDK is a display firmware, which has functions
|
||||
* implemented above in the dma configuration function. The
|
||||
* user can install another firmware using `dma_smartdma_install_fw`
|
||||
*/
|
||||
SMARTDMA_Init((uint32_t)config->smartdma_progs,
|
||||
s_smartdmaDisplayFirmware,
|
||||
SMARTDMA_DISPLAY_FIRMWARE_SIZE);
|
||||
config->irq_config_func(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma_mcux_smartdma_irq(const struct device *dev)
|
||||
{
|
||||
const struct dma_mcux_smartdma_data *data = dev->data;
|
||||
|
||||
if (data->callback) {
|
||||
data->callback(dev, data->user_data, 0, 0);
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
/* Release PM lock */
|
||||
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief install SMARTDMA firmware
|
||||
*
|
||||
* Install a custom firmware for the smartDMA. This function allows the user
|
||||
* to install a custom firmware into the smartDMA, which implements
|
||||
* different API functions than the standard MCUX SDK firmware.
|
||||
* @param dev: smartDMA device
|
||||
* @param firmware: address of buffer containing smartDMA firmware
|
||||
* @param len: length of firmware buffer
|
||||
*/
|
||||
void dma_smartdma_install_fw(const struct device *dev, uint8_t *firmware,
|
||||
uint32_t len)
|
||||
{
|
||||
const struct dma_mcux_smartdma_config *config = dev->config;
|
||||
|
||||
SMARTDMA_InstallFirmware((uint32_t)config->smartdma_progs, firmware, len);
|
||||
}
|
||||
|
||||
static const struct dma_driver_api dma_mcux_smartdma_api = {
|
||||
.config = dma_mcux_smartdma_configure,
|
||||
.start = dma_mcux_smartdma_start,
|
||||
.stop = dma_mcux_smartdma_stop,
|
||||
};
|
||||
|
||||
|
||||
#define SMARTDMA_INIT(n) \
|
||||
static void dma_mcux_smartdma_config_func_##n(const struct device *dev) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
|
||||
dma_mcux_smartdma_irq, \
|
||||
DEVICE_DT_INST_GET(n), 0); \
|
||||
irq_enable(DT_INST_IRQN(n)); \
|
||||
} \
|
||||
\
|
||||
static const struct dma_mcux_smartdma_config smartdma_##n##_config = { \
|
||||
.base = (struct smartdma_periph *)DT_INST_REG_ADDR(n), \
|
||||
.smartdma_progs = (void (**)(void))DT_INST_PROP(n, program_mem),\
|
||||
.irq_config_func = dma_mcux_smartdma_config_func_##n, \
|
||||
}; \
|
||||
static struct dma_mcux_smartdma_data smartdma_##n##_data; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, \
|
||||
&dma_mcux_smartdma_init, \
|
||||
NULL, \
|
||||
&smartdma_##n##_data, &smartdma_##n##_config, \
|
||||
POST_KERNEL, CONFIG_DMA_INIT_PRIORITY, \
|
||||
&dma_mcux_smartdma_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SMARTDMA_INIT)
|
28
dts/bindings/dma/nxp,smartdma.yaml
Normal file
28
dts/bindings/dma/nxp,smartdma.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright 2023 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: NXP SmartDMA controller
|
||||
|
||||
compatible: "nxp,smartdma"
|
||||
|
||||
include: dma-controller.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
program-mem:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Program memory to load SMARTDMA routines into. Must be set to a RAM
|
||||
region that the SMARTDMA can access on the chip.
|
||||
|
||||
# SmartDMA does not support channels, so no DMA cells should be provided.
|
||||
# the driver or application using SMARTDMA can set the DMA program to run
|
||||
# using the `dma_slot` parameter.
|
||||
"#dma-cells":
|
||||
const: 0
|
53
include/zephyr/drivers/dma/dma_mcux_smartdma.h
Normal file
53
include/zephyr/drivers/dma/dma_mcux_smartdma.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_DMA_MCUX_SMARTDMA_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_DMA_MCUX_SMARTDMA_H_
|
||||
|
||||
/* Write RGB565 data to MIPI DSI via DMA. */
|
||||
#define DMA_SMARTDMA_MIPI_RGB565_DMA 0
|
||||
/* Write RGB888 data to MIPI DSI via DMA */
|
||||
#define DMA_SMARTDMA_MIPI_RGB888_DMA 1
|
||||
/* Write RGB565 data to MIPI DSI via DMA. Rotate output data by 180 degrees */
|
||||
#define DMA_SMARTDMA_MIPI_RGB565_180 2
|
||||
/* Write RGB888 data to MIPI DSI via DMA. Rotate output data by 180 degrees */
|
||||
#define DMA_SMARTDMA_MIPI_RGB888_180 3
|
||||
|
||||
/* Write RGB565 data to MIPI DSI via DMA. Swap data endianness, so that
|
||||
* little endian RGB565 data will be written big endian style.
|
||||
*/
|
||||
#define DMA_SMARTDMA_MIPI_RGB565_DMA_SWAP 4
|
||||
/* Write RGB888 data to MIPI DSI via DMA. Swap data endianness, so that
|
||||
* little endian RGB888 data will be written big endian style.
|
||||
*/
|
||||
#define DMA_SMARTDMA_MIPI_RGB888_DMA_SWAP 5
|
||||
/* Write RGB565 data to MIPI DSI via DMA. Rotate output data by 180 degrees,
|
||||
* and swap data endianness
|
||||
*/
|
||||
#define DMA_SMARTDMA_MIPI_RGB565_180_SWAP 6
|
||||
/* Write RGB888 data to MIPI DSI via DMA. Rotate output data by 180 degrees,
|
||||
* and swap data endianness
|
||||
*/
|
||||
#define DMA_SMARTDMA_MIPI_RGB888_180_SWAP 7
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief install SMARTDMA firmware
|
||||
*
|
||||
* Install a custom firmware for the smartDMA. This function allows the user
|
||||
* to install a custom firmware into the smartDMA, which implements
|
||||
* different API functions than the standard MCUX SDK firmware.
|
||||
* @param dev: smartDMA device
|
||||
* @param firmware: address of buffer containing smartDMA firmware
|
||||
* @param len: length of firmware buffer
|
||||
*/
|
||||
void dma_smartdma_install_fw(const struct device *dev, uint8_t *firmware,
|
||||
uint32_t len);
|
||||
|
||||
#define GD32_DMA_FEATURES_FIFO_THRESHOLD(threshold) (threshold & 0x3)
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_DMA_MCUX_SMARTDMA_H_ */
|
Loading…
Reference in a new issue