2023-11-23 19:01:59 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2023 Renesas Electronics Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT renesas_smartbond_mipi_dbi
|
|
|
|
|
|
|
|
#include <zephyr/drivers/mipi_dbi.h>
|
|
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
|
|
#include <zephyr/drivers/gpio.h>
|
|
|
|
#include <zephyr/irq.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <zephyr/device.h>
|
|
|
|
#include <zephyr/pm/device.h>
|
2024-05-22 00:25:47 +08:00
|
|
|
#include <zephyr/pm/policy.h>
|
2023-11-23 19:01:59 +08:00
|
|
|
#include <DA1469xAB.h>
|
|
|
|
#include <zephyr/drivers/clock_control.h>
|
|
|
|
#include <zephyr/drivers/clock_control/smartbond_clock_control.h>
|
|
|
|
#include <zephyr/drivers/display.h>
|
|
|
|
#include <zephyr/sys/util.h>
|
|
|
|
#include <zephyr/drivers/spi.h>
|
|
|
|
#include <da1469x_lcdc.h>
|
|
|
|
#include <da1469x_pd.h>
|
|
|
|
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(smartbond_mipi_dbi, CONFIG_MIPI_DBI_LOG_LEVEL);
|
|
|
|
|
|
|
|
#define SMARTBOND_IRQN DT_INST_IRQN(0)
|
|
|
|
#define SMARTBOND_IRQ_PRIO DT_INST_IRQ(0, priority)
|
|
|
|
|
|
|
|
#define PINCTRL_STATE_READ PINCTRL_STATE_PRIV_START
|
|
|
|
|
|
|
|
#define MIPI_DBI_SMARTBOND_IS_READ_SUPPORTED \
|
|
|
|
DT_INST_NODE_HAS_PROP(0, spi_dev)
|
|
|
|
|
|
|
|
#define LCDC_SMARTBOND_CLK_DIV(_freq) \
|
|
|
|
((32000000U % (_freq)) ? (96000000U / (_freq)) : (32000000U / (_freq)))
|
|
|
|
|
|
|
|
#define MIPI_DBI_SMARTBOND_IS_PLL_REQUIRED \
|
|
|
|
!!(32000000U % DT_PROP(DT_CHOSEN(zephyr_display), mipi_max_frequency))
|
|
|
|
|
|
|
|
#define MIPI_DBI_SMARTBOND_IS_TE_ENABLED \
|
|
|
|
DT_INST_PROP_OR(0, te_enable, 0)
|
|
|
|
|
|
|
|
#define MIPI_DBI_SMARTBOND_IS_DMA_PREFETCH_ENABLED \
|
|
|
|
DT_INST_ENUM_IDX_OR(0, dma_prefetch, 0)
|
|
|
|
|
|
|
|
#define MIPI_DBI_SMARTBOND_IS_RESET_AVAILABLE \
|
|
|
|
DT_INST_NODE_HAS_PROP(0, reset_gpios)
|
|
|
|
|
|
|
|
#define LCDC_LAYER0_OFFSETX_REG_SET_FIELD(_field, _var, _val) \
|
|
|
|
((_var)) = \
|
|
|
|
((_var) & ~(LCDC_LCDC_LAYER0_OFFSETX_REG_ ## _field ## _Msk)) | \
|
|
|
|
(((_var) << LCDC_LCDC_LAYER0_OFFSETX_REG_ ## _field ## _Pos) & \
|
|
|
|
LCDC_LCDC_LAYER0_OFFSETX_REG_ ## _field ## _Msk)
|
|
|
|
|
|
|
|
struct mipi_dbi_smartbond_data {
|
|
|
|
/* Provide mutual exclusion when a display operation is requested. */
|
|
|
|
struct k_sem device_sem;
|
|
|
|
/* Provide synchronization between task return and ISR firing */
|
|
|
|
struct k_sem sync_sem;
|
|
|
|
/* Flag indicating whether or not an underflow took place */
|
|
|
|
volatile bool underflow_flag;
|
|
|
|
/* Layer settings */
|
|
|
|
lcdc_smartbond_layer_cfg layer;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mipi_dbi_smartbond_config {
|
|
|
|
/* Reference to device instance's pinctrl configurations */
|
|
|
|
const struct pinctrl_dev_config *pcfg;
|
|
|
|
/* Reset GPIO */
|
|
|
|
const struct gpio_dt_spec reset;
|
|
|
|
/* Host controller's timing settings */
|
|
|
|
lcdc_smartbond_timing_cfg timing_cfg;
|
|
|
|
/* Background default color configuration */
|
|
|
|
lcdc_smartbond_bgcolor_cfg bgcolor_cfg;
|
|
|
|
};
|
|
|
|
|
2024-06-21 16:37:21 +08:00
|
|
|
/* Mark the device is progress and so it's not allowed to enter the sleep state. */
|
2024-05-22 00:25:47 +08:00
|
|
|
static inline void mipi_dbi_smartbond_pm_policy_state_lock_get(void)
|
2023-11-23 19:01:59 +08:00
|
|
|
{
|
2024-05-22 00:25:47 +08:00
|
|
|
/*
|
|
|
|
* Prevent the SoC from etering the normal sleep state as PDC does not support
|
|
|
|
* waking up the application core following LCDC events.
|
|
|
|
*/
|
|
|
|
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
|
2023-11-23 19:01:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark that device is inactive and so it's allowed to enter the sleep state */
|
2024-05-22 00:25:47 +08:00
|
|
|
static inline void mipi_dbi_smartbond_pm_policy_state_lock_put(void)
|
2023-11-23 19:01:59 +08:00
|
|
|
{
|
2024-05-22 00:25:47 +08:00
|
|
|
/* Allow the SoC to enter the nornmal sleep state once LCDC is inactive */
|
|
|
|
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
|
2023-11-23 19:01:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper function to trigger the LCDC fetching data from frame buffer to the connected display */
|
|
|
|
static void mipi_dbi_smartbond_send_single_frame(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct mipi_dbi_smartbond_data *data = dev->data;
|
|
|
|
|
|
|
|
#if MIPI_DBI_SMARTBOND_IS_TE_ENABLED
|
|
|
|
da1469x_lcdc_te_set_status(true, DT_INST_PROP_OR(0, te_polarity, false));
|
|
|
|
/*
|
|
|
|
* Wait for the TE signal to be asserted so display's refresh status can be synchronized
|
|
|
|
* with the current frame update.
|
|
|
|
*/
|
|
|
|
k_sem_take(&data->sync_sem, K_FOREVER);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
LCDC->LCDC_INTERRUPT_REG |= LCDC_LCDC_INTERRUPT_REG_LCDC_VSYNC_IRQ_EN_Msk;
|
|
|
|
|
|
|
|
/* Setting this bit will enable the host to start outputing pixel data */
|
|
|
|
LCDC->LCDC_MODE_REG |= LCDC_LCDC_MODE_REG_LCDC_SFRAME_UPD_Msk;
|
|
|
|
|
|
|
|
/* Wait for frame update to complete */
|
|
|
|
k_sem_take(&data->sync_sem, K_FOREVER);
|
|
|
|
|
|
|
|
if (data->underflow_flag) {
|
|
|
|
LOG_WRN("Underflow took place");
|
|
|
|
data->underflow_flag = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if MIPI_DBI_SMARTBOND_IS_RESET_AVAILABLE
|
2024-07-21 21:34:56 +08:00
|
|
|
static int mipi_dbi_smartbond_reset(const struct device *dev, k_timeout_t delay)
|
2023-11-23 19:01:59 +08:00
|
|
|
{
|
|
|
|
const struct mipi_dbi_smartbond_config *config = dev->config;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!gpio_is_ready_dt(&config->reset)) {
|
|
|
|
LOG_ERR("Reset signal not available");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = gpio_pin_set_dt(&config->reset, 1);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Cannot drive reset signal");
|
|
|
|
return ret;
|
|
|
|
}
|
2024-07-21 21:34:56 +08:00
|
|
|
k_sleep(delay);
|
2023-11-23 19:01:59 +08:00
|
|
|
|
|
|
|
return gpio_pin_set_dt(&config->reset, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Display pixel to output color format translation */
|
|
|
|
static inline uint8_t lcdc_smartbond_pixel_to_ocm(enum display_pixel_format pixfmt)
|
|
|
|
{
|
|
|
|
switch (pixfmt) {
|
|
|
|
case PIXEL_FORMAT_RGB_565:
|
|
|
|
return (uint8_t)LCDC_SMARTBOND_OCM_RGB565;
|
|
|
|
case PIXEL_FORMAT_RGB_888:
|
|
|
|
return (uint8_t)LCDC_SMARTBOND_OCM_RGB888;
|
|
|
|
case PIXEL_FORMAT_MONO10:
|
|
|
|
return (uint8_t)LCDC_SMARTBOND_L0_L1;
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported pixel format");
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint8_t lcdc_smartbond_line_mode_translation(uint8_t mode)
|
|
|
|
{
|
|
|
|
switch (mode) {
|
|
|
|
case MIPI_DBI_MODE_SPI_3WIRE:
|
|
|
|
return (uint8_t)LCDC_SMARTBOND_MODE_SPI3;
|
|
|
|
case MIPI_DBI_MODE_SPI_4WIRE:
|
|
|
|
return (uint8_t)LCDC_SMARTBOND_MODE_SPI4;
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported SPI mode");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint8_t lcdc_smartbond_pixel_to_lcm(enum display_pixel_format pixfmt)
|
|
|
|
{
|
|
|
|
switch (pixfmt) {
|
|
|
|
case PIXEL_FORMAT_RGB_565:
|
|
|
|
return (uint8_t)LCDC_SMARTBOND_L0_RGB565;
|
|
|
|
case PIXEL_FORMAT_ARGB_8888:
|
|
|
|
return (uint8_t)LCDC_SMARTBOND_L0_ARGB8888;
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported pixel format");
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lcdc_smartbond_mipi_dbi_translation(const struct mipi_dbi_config *dbi_config,
|
|
|
|
lcdc_smartbond_mipi_dbi_cfg *mipi_dbi_cfg,
|
|
|
|
enum display_pixel_format pixfmt)
|
|
|
|
{
|
|
|
|
mipi_dbi_cfg->cpha = dbi_config->config.operation & SPI_MODE_CPHA;
|
|
|
|
mipi_dbi_cfg->cpol = dbi_config->config.operation & SPI_MODE_CPOL;
|
|
|
|
mipi_dbi_cfg->cs_active_high = dbi_config->config.operation & SPI_CS_ACTIVE_HIGH;
|
|
|
|
mipi_dbi_cfg->line_mode = lcdc_smartbond_line_mode_translation(dbi_config->mode);
|
|
|
|
mipi_dbi_cfg->color_mode = lcdc_smartbond_pixel_to_ocm(pixfmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if MIPI_DBI_SMARTBOND_IS_READ_SUPPORTED
|
|
|
|
static int mipi_dbi_smartbond_command_read(const struct device *dev,
|
|
|
|
const struct mipi_dbi_config *dbi_config,
|
|
|
|
uint8_t *cmd, size_t num_cmds,
|
|
|
|
uint8_t *response, size_t len)
|
|
|
|
{
|
|
|
|
struct mipi_dbi_smartbond_data *data = dev->data;
|
|
|
|
const struct mipi_dbi_smartbond_config *config = dev->config;
|
|
|
|
int ret = 0;
|
|
|
|
lcdc_smartbond_mipi_dbi_cfg mipi_dbi_cfg;
|
|
|
|
|
|
|
|
k_sem_take(&data->device_sem, K_FOREVER);
|
|
|
|
|
2024-05-22 00:25:47 +08:00
|
|
|
mipi_dbi_smartbond_pm_policy_state_lock_get();
|
|
|
|
|
2023-11-23 19:01:59 +08:00
|
|
|
/*
|
|
|
|
* Add an arbitrary valid color format to satisfy subroutine. The MIPI DBI command/data
|
|
|
|
* engine should not be affected.
|
|
|
|
*/
|
|
|
|
lcdc_smartbond_mipi_dbi_translation(dbi_config, &mipi_dbi_cfg, PIXEL_FORMAT_RGB_565);
|
|
|
|
ret = da1469x_lcdc_mipi_dbi_interface_configure(&mipi_dbi_cfg);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto _mipi_dbi_read_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the cmd/data engine is busy since the #CS line will be overruled. */
|
|
|
|
if (da1469x_lcdc_is_busy()) {
|
|
|
|
LOG_WRN("MIPI DBI host is busy");
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto _mipi_dbi_read_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force CS line to low. Typically, command and data are bound in the same #CS assertion */
|
|
|
|
da1469x_lcdc_force_cs_line(true, mipi_dbi_cfg.cs_active_high);
|
|
|
|
|
|
|
|
da1469x_lcdc_send_cmd_data(true, cmd, num_cmds);
|
|
|
|
|
|
|
|
if (len) {
|
|
|
|
const struct device *spi_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, spi_dev));
|
|
|
|
|
|
|
|
struct spi_buf buffer = {
|
|
|
|
.buf = (void *)response,
|
|
|
|
.len = len,
|
|
|
|
};
|
|
|
|
struct spi_buf_set buf_set = {
|
|
|
|
.buffers = &buffer,
|
|
|
|
.count = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!device_is_ready(spi_dev)) {
|
|
|
|
LOG_ERR("SPI device is not ready");
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto _mipi_dbi_read_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Overwrite CLK and enable DI lines. CS is driven forcefully. */
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_READ);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Could not apply MIPI DBI pins' SPI read state (%d)", ret);
|
|
|
|
goto _mipi_dbi_read_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get response */
|
|
|
|
ret = spi_read(spi_dev, &dbi_config->config, &buf_set);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Could not read data from SPI");
|
|
|
|
goto _mipi_dbi_read_exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_mipi_dbi_read_exit:
|
|
|
|
|
|
|
|
/* Restore #CS line */
|
|
|
|
da1469x_lcdc_force_cs_line(false, mipi_dbi_cfg.cs_active_high);
|
|
|
|
|
|
|
|
/* Make sure default LCDC pins are applied upon exit */
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Could not apply MIPI DBI pins' default state (%d)", ret);
|
|
|
|
}
|
|
|
|
|
2024-05-22 00:25:47 +08:00
|
|
|
mipi_dbi_smartbond_pm_policy_state_lock_put();
|
|
|
|
|
2023-11-23 19:01:59 +08:00
|
|
|
k_sem_give(&data->device_sem);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int mipi_dbi_smartbond_command_write(const struct device *dev,
|
|
|
|
const struct mipi_dbi_config *dbi_config,
|
|
|
|
uint8_t cmd, const uint8_t *data_buf,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
struct mipi_dbi_smartbond_data *data = dev->data;
|
2024-06-10 20:35:21 +08:00
|
|
|
int ret = 0;
|
2023-11-23 19:01:59 +08:00
|
|
|
lcdc_smartbond_mipi_dbi_cfg mipi_dbi_cfg;
|
|
|
|
|
|
|
|
k_sem_take(&data->device_sem, K_FOREVER);
|
|
|
|
|
2024-05-22 00:25:47 +08:00
|
|
|
mipi_dbi_smartbond_pm_policy_state_lock_get();
|
2023-11-23 19:01:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an arbitrary valid color format to satisfy subroutine. The MIPI DBI command/data
|
|
|
|
* engine should not be affected.
|
|
|
|
*/
|
|
|
|
lcdc_smartbond_mipi_dbi_translation(dbi_config, &mipi_dbi_cfg, PIXEL_FORMAT_RGB_565);
|
|
|
|
ret = da1469x_lcdc_mipi_dbi_interface_configure(&mipi_dbi_cfg);
|
|
|
|
if (ret < 0) {
|
2024-06-10 20:35:21 +08:00
|
|
|
goto finish;
|
2023-11-23 19:01:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Command and accompanied data should be transmitted via the DBIB interface */
|
|
|
|
da1469x_lcdc_send_cmd_data(true, &cmd, 1);
|
|
|
|
|
|
|
|
if (len) {
|
|
|
|
/* Data should be transmitted via the DBIB interface */
|
|
|
|
da1469x_lcdc_send_cmd_data(false, data_buf, len);
|
|
|
|
}
|
|
|
|
|
2024-06-10 20:35:21 +08:00
|
|
|
finish:
|
2024-05-22 00:25:47 +08:00
|
|
|
mipi_dbi_smartbond_pm_policy_state_lock_put();
|
2023-11-23 19:01:59 +08:00
|
|
|
|
|
|
|
k_sem_give(&data->device_sem);
|
|
|
|
|
2024-06-10 20:35:21 +08:00
|
|
|
return ret;
|
2023-11-23 19:01:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mipi_dbi_smartbond_write_display(const struct device *dev,
|
|
|
|
const struct mipi_dbi_config *dbi_config,
|
|
|
|
const uint8_t *framebuf,
|
|
|
|
struct display_buffer_descriptor *desc,
|
|
|
|
enum display_pixel_format pixfmt)
|
|
|
|
{
|
|
|
|
struct mipi_dbi_smartbond_data *data = dev->data;
|
|
|
|
const struct mipi_dbi_smartbond_config *config = dev->config;
|
|
|
|
lcdc_smartbond_layer_cfg *layer = &data->layer;
|
|
|
|
int ret = 0;
|
|
|
|
lcdc_smartbond_mipi_dbi_cfg mipi_dbi_cfg;
|
|
|
|
uint8_t layer_color = lcdc_smartbond_pixel_to_lcm(pixfmt);
|
|
|
|
|
sys: util: use BITS_PER_BYTE macro instead of the magic number 8
Obviously, everyone knows that there are 8 bits per byte, so
there isn't a lot of magic happening, per se, but it's also
helpful to clearly denote where the magic number 8 is referring
to the number of bits in a byte.
Occasionally, 8 will refer to a field size or offset in a
structure, MMR, or word. Occasionally, the number 8 will refer
to the number of bytes in a 64-bit value (which should probably
be replaced with `sizeof(uint64_t)`).
For converting bits to bytes, or vice-versa, let's use
`BITS_PER_BYTE` for clarity (or other appropriate `BITS_PER_*`
macros).
Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com>
2024-11-03 23:08:19 +08:00
|
|
|
if (desc->width * desc->height * (DISPLAY_BITS_PER_PIXEL(pixfmt) / BITS_PER_BYTE) !=
|
|
|
|
desc->buf_size) {
|
2023-11-23 19:01:59 +08:00
|
|
|
LOG_ERR("Incorrect buffer size for given width and height");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_sem_take(&data->device_sem, K_FOREVER);
|
|
|
|
|
2024-05-22 00:25:47 +08:00
|
|
|
mipi_dbi_smartbond_pm_policy_state_lock_get();
|
2023-11-23 19:01:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mainly check if the frame generator is busy with a pending frame update (might happen
|
|
|
|
* when two frame updates take place one after the other and the display interface is
|
|
|
|
* quite slow). VSYNC interrupt line should be asserted when the last line is being
|
|
|
|
* outputed.
|
|
|
|
*/
|
|
|
|
if (da1469x_lcdc_is_busy()) {
|
|
|
|
LOG_WRN("MIPI DBI host is busy");
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto _mipi_dbi_write_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
lcdc_smartbond_mipi_dbi_translation(dbi_config, &mipi_dbi_cfg, pixfmt);
|
|
|
|
ret = da1469x_lcdc_mipi_dbi_interface_configure(&mipi_dbi_cfg);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto _mipi_dbi_write_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = da1469x_lcdc_timings_configure(desc->width, desc->height,
|
|
|
|
(lcdc_smartbond_timing_cfg *)&config->timing_cfg);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto _mipi_dbi_write_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
LCDC_SMARTBOND_LAYER_CONFIG(layer, framebuf, 0, 0, desc->width, desc->height,
|
|
|
|
layer_color,
|
|
|
|
da1469x_lcdc_stride_calculation(layer_color, desc->width));
|
|
|
|
ret = da1469x_lcdc_layer_configure(layer);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto _mipi_dbi_write_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Trigger single frame update via the LCDC-DMA engine */
|
|
|
|
mipi_dbi_smartbond_send_single_frame(dev);
|
|
|
|
|
|
|
|
_mipi_dbi_write_exit:
|
|
|
|
|
2024-05-22 00:25:47 +08:00
|
|
|
mipi_dbi_smartbond_pm_policy_state_lock_put();
|
2023-11-23 19:01:59 +08:00
|
|
|
|
|
|
|
k_sem_give(&data->device_sem);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mipi_dbi_smartbond_configure(const struct device *dev)
|
|
|
|
{
|
|
|
|
uint8_t clk_div =
|
|
|
|
LCDC_SMARTBOND_CLK_DIV(DT_PROP(DT_CHOSEN(zephyr_display), mipi_max_frequency));
|
|
|
|
const struct mipi_dbi_smartbond_config *config = dev->config;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First enable the controller so registers can be written. In serial interfaces
|
|
|
|
* clock divider is further divided by 2.
|
|
|
|
*/
|
|
|
|
da1469x_lcdc_set_status(true, MIPI_DBI_SMARTBOND_IS_PLL_REQUIRED,
|
|
|
|
(clk_div >= 2 ? clk_div / 2 : clk_div));
|
|
|
|
|
|
|
|
if (!da1469x_lcdc_check_id()) {
|
|
|
|
LOG_ERR("Mismatching LCDC ID");
|
|
|
|
da1469x_lcdc_set_status(false, 0, 0);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
da1469x_lcdc_te_set_status(false, DT_INST_PROP_OR(0, te_polarity, false));
|
|
|
|
|
|
|
|
da1469x_lcdc_bgcolor_configure((lcdc_smartbond_bgcolor_cfg *)&config->bgcolor_cfg);
|
|
|
|
|
|
|
|
LCDC_LAYER0_OFFSETX_REG_SET_FIELD(LCDC_L0_DMA_PREFETCH,
|
|
|
|
LCDC->LCDC_LAYER0_OFFSETX_REG, MIPI_DBI_SMARTBOND_IS_DMA_PREFETCH_ENABLED);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void smartbond_mipi_dbi_isr(const void *arg)
|
|
|
|
{
|
|
|
|
struct mipi_dbi_smartbond_data *data = ((const struct device *)arg)->data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Underflow sticky bit will remain high until cleared by writing
|
|
|
|
* any value to LCDC_INTERRUPT_REG.
|
|
|
|
*/
|
|
|
|
data->underflow_flag = LCDC_STATUS_REG_GET_FIELD(LCDC_STICKY_UNDERFLOW);
|
|
|
|
|
|
|
|
/* Default interrupt mode is level triggering so interrupt should be cleared */
|
|
|
|
da1469x_lcdc_te_set_status(false, DT_INST_PROP_OR(0, te_polarity, false));
|
|
|
|
|
|
|
|
k_sem_give(&data->sync_sem);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mipi_dbi_smartbond_resume(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct mipi_dbi_smartbond_config *config = dev->config;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Select default state */
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Could not apply LCDC pins' default state (%d)", ret);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if MIPI_DBI_SMARTBOND_IS_PLL_REQUIRED
|
|
|
|
const struct device *clock_dev = DEVICE_DT_GET(DT_NODELABEL(osc));
|
|
|
|
|
|
|
|
if (!device_is_ready(clock_dev)) {
|
|
|
|
LOG_WRN("Clock device is not available; PLL cannot be used");
|
|
|
|
} else {
|
|
|
|
ret = z_smartbond_select_sys_clk(SMARTBOND_CLK_PLL96M);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_WRN("Could not switch to PLL. Requested speed should not be achieved.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return mipi_dbi_smartbond_configure(dev);
|
|
|
|
}
|
|
|
|
|
2024-06-10 20:35:21 +08:00
|
|
|
#if defined(CONFIG_PM_DEVICE)
|
2023-11-23 19:01:59 +08:00
|
|
|
static int mipi_dbi_smartbond_suspend(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct mipi_dbi_smartbond_config *config = dev->config;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Select sleep state; it's OK if settings fails for any reason. */
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_WRN("Could not apply MIPI DBI pins' sleep state");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable host controller to minimize power consumption. */
|
|
|
|
da1469x_lcdc_set_status(false, false, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mipi_dbi_smartbond_pm_action(const struct device *dev, enum pm_device_action action)
|
|
|
|
{
|
2024-05-22 00:25:47 +08:00
|
|
|
int ret = 0;
|
2023-11-23 19:01:59 +08:00
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
2024-05-22 00:25:47 +08:00
|
|
|
/* A non-zero value should not affect sleep */
|
|
|
|
(void)mipi_dbi_smartbond_suspend(dev);
|
2024-05-01 17:52:29 +08:00
|
|
|
break;
|
2023-11-23 19:01:59 +08:00
|
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
|
|
/*
|
2024-05-01 17:52:29 +08:00
|
|
|
* The resume error code should not be taken into consideration
|
|
|
|
* by the PM subsystem.
|
2023-11-23 19:01:59 +08:00
|
|
|
*/
|
2024-05-01 17:52:29 +08:00
|
|
|
ret = mipi_dbi_smartbond_resume(dev);
|
|
|
|
break;
|
2023-11-23 19:01:59 +08:00
|
|
|
default:
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int mipi_dbi_smartbond_init(const struct device *dev)
|
|
|
|
{
|
2024-06-20 22:34:28 +08:00
|
|
|
__unused const struct mipi_dbi_smartbond_config *config = dev->config;
|
2023-11-23 19:01:59 +08:00
|
|
|
struct mipi_dbi_smartbond_data *data = dev->data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Device should be ready to be acquired */
|
|
|
|
k_sem_init(&data->device_sem, 1, 1);
|
|
|
|
/* Event should be signaled by LCDC ISR */
|
|
|
|
k_sem_init(&data->sync_sem, 0, 1);
|
|
|
|
|
|
|
|
#if MIPI_DBI_SMARTBOND_IS_RESET_AVAILABLE
|
|
|
|
if (gpio_is_ready_dt(&config->reset)) {
|
|
|
|
ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Could not configure reset line (%d)", ret);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
IRQ_CONNECT(SMARTBOND_IRQN, SMARTBOND_IRQ_PRIO, smartbond_mipi_dbi_isr,
|
|
|
|
DEVICE_DT_INST_GET(0), 0);
|
|
|
|
|
|
|
|
ret = mipi_dbi_smartbond_resume(dev);
|
|
|
|
|
2024-05-01 17:52:29 +08:00
|
|
|
return ret;
|
2023-11-23 19:01:59 +08:00
|
|
|
}
|
|
|
|
|
2024-07-21 20:50:37 +08:00
|
|
|
static const struct mipi_dbi_driver_api mipi_dbi_smartbond_driver_api = {
|
2023-11-23 19:01:59 +08:00
|
|
|
#if MIPI_DBI_SMARTBOND_IS_RESET_AVAILABLE
|
|
|
|
.reset = mipi_dbi_smartbond_reset,
|
|
|
|
#endif
|
|
|
|
.command_write = mipi_dbi_smartbond_command_write,
|
|
|
|
.write_display = mipi_dbi_smartbond_write_display,
|
|
|
|
#if MIPI_DBI_SMARTBOND_IS_READ_SUPPORTED
|
|
|
|
.command_read = mipi_dbi_smartbond_command_read,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#define SMARTBOND_MIPI_DBI_INIT(inst) \
|
|
|
|
PINCTRL_DT_INST_DEFINE(inst); \
|
|
|
|
\
|
|
|
|
static const struct mipi_dbi_smartbond_config mipi_dbi_smartbond_config_## inst = { \
|
|
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
|
|
|
|
.reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {}), \
|
|
|
|
.timing_cfg = { 0 }, \
|
|
|
|
.bgcolor_cfg = { 0xFF, 0xFF, 0xFF, 0 }, \
|
|
|
|
}; \
|
|
|
|
\
|
|
|
|
static struct mipi_dbi_smartbond_data mipi_dbi_smartbond_data_## inst; \
|
|
|
|
\
|
|
|
|
PM_DEVICE_DT_INST_DEFINE(inst, mipi_dbi_smartbond_pm_action); \
|
|
|
|
\
|
|
|
|
DEVICE_DT_INST_DEFINE(inst, mipi_dbi_smartbond_init, \
|
|
|
|
PM_DEVICE_DT_INST_GET(inst), \
|
|
|
|
&mipi_dbi_smartbond_data_## inst, \
|
|
|
|
&mipi_dbi_smartbond_config_## inst, \
|
|
|
|
POST_KERNEL, \
|
|
|
|
CONFIG_MIPI_DBI_INIT_PRIORITY, \
|
|
|
|
&mipi_dbi_smartbond_driver_api);
|
|
|
|
|
|
|
|
SMARTBOND_MIPI_DBI_INIT(0);
|