Skip to content

Commit 8d74553

Browse files
miggazElqueznashif
authored andcommitted
drivers: mipi_dbi: add driver for st,stm32-fmc
Add support for using the stm32 fmc to interact with a display controller, using Intel 8080 protocol with a 16 bits parallel bus. Signed-off-by: Miguel Gazquez <[email protected]>
1 parent 40e9061 commit 8d74553

File tree

5 files changed

+228
-0
lines changed

5 files changed

+228
-0
lines changed

boards/st/stm32l562e_dk/Kconfig.defconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,9 @@ config BT_HCI_VS
2525

2626
endif # BT
2727

28+
if MIPI_DBI_STM32_FMC
29+
config MIPI_DBI_STM32_FMC_MEM_BARRIER
30+
default n
31+
endif # MIPI_DBI_STM32_FMC
32+
2833
endif # BOARD_STM32L562E_DK

drivers/mipi_dbi/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ zephyr_sources_ifdef(CONFIG_MIPI_DBI_SPI mipi_dbi_spi.c)
66
zephyr_sources_ifdef(CONFIG_MIPI_DBI_SMARTBOND mipi_dbi_smartbond.c)
77
zephyr_sources_ifdef(CONFIG_MIPI_DBI_NXP_LCDIC mipi_dbi_nxp_lcdic.c)
88
zephyr_sources_ifdef(CONFIG_MIPI_DBI_NXP_FLEXIO_LCDIF mipi_dbi_nxp_flexio_lcdif.c)
9+
zephyr_sources_ifdef(CONFIG_MIPI_DBI_STM32_FMC mipi_dbi_stm32_fmc.c)
910
# Data bus width is used by the SDK driver and processes it as a compile time option
1011
if(CONFIG_MIPI_DBI_NXP_FLEXIO_LCDIF)
1112
dt_chosen(flexio0_lcd PROPERTY "zephyr,display")

drivers/mipi_dbi/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ source "drivers/mipi_dbi/Kconfig.spi"
2525
source "drivers/mipi_dbi/Kconfig.smartbond"
2626
source "drivers/mipi_dbi/Kconfig.nxp_lcdic"
2727
source "drivers/mipi_dbi/Kconfig.nxp_flexio_lcdif"
28+
source "drivers/mipi_dbi/Kconfig.stm32_fmc"
2829

2930
endif

drivers/mipi_dbi/Kconfig.stm32_fmc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2024 Bootlin
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config MIPI_DBI_STM32_FMC
5+
bool "MIPI DBI driver for STM32 FMC"
6+
default y
7+
depends on DT_HAS_ST_STM32_FMC_MIPI_DBI_ENABLED
8+
select MEMC
9+
help
10+
Enable support for MIPI DBI driver for controller based on the stm32 FMC.
11+
12+
if MIPI_DBI_STM32_FMC
13+
14+
config MIPI_DBI_STM32_FMC_MEM_BARRIER
15+
bool "Adds memory barrier after every address and data register access"
16+
default y
17+
18+
endif # MIPI_DBI_STM32_FMC

drivers/mipi_dbi/mipi_dbi_stm32_fmc.c

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
* Copyright (c) 2024 Bootlin
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT st_stm32_fmc_mipi_dbi
8+
9+
#include <zephyr/drivers/gpio.h>
10+
#include <zephyr/drivers/mipi_dbi.h>
11+
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
12+
#include <zephyr/sys/barrier.h>
13+
#include <zephyr/sys/sys_io.h>
14+
#include <zephyr/sys/byteorder.h>
15+
16+
#include <zephyr/logging/log.h>
17+
LOG_MODULE_REGISTER(mipi_dbi_stm32_fmc, CONFIG_MIPI_DBI_LOG_LEVEL);
18+
19+
struct mipi_dbi_stm32_fmc_config {
20+
/* Reset GPIO */
21+
const struct gpio_dt_spec reset;
22+
/* Power GPIO */
23+
const struct gpio_dt_spec power;
24+
mem_addr_t register_addr;
25+
mem_addr_t data_addr;
26+
uint32_t fmc_address_setup_time;
27+
uint32_t fmc_data_setup_time;
28+
uint32_t fmc_memory_width;
29+
};
30+
31+
struct mipi_dbi_stm32_fmc_data {
32+
const struct mipi_dbi_config *dbi_config;
33+
};
34+
35+
int mipi_dbi_stm32_fmc_check_config(const struct device *dev,
36+
const struct mipi_dbi_config *dbi_config)
37+
{
38+
const struct mipi_dbi_stm32_fmc_config *config = dev->config;
39+
struct mipi_dbi_stm32_fmc_data *data = dev->data;
40+
uint32_t fmc_write_cycles;
41+
42+
if (data->dbi_config == dbi_config) {
43+
return 0;
44+
}
45+
46+
if (dbi_config->mode != MIPI_DBI_MODE_8080_BUS_16_BIT) {
47+
LOG_ERR("Only support Intel 8080 16-bits");
48+
return -ENOTSUP;
49+
}
50+
51+
if (config->fmc_memory_width != FMC_NORSRAM_MEM_BUS_WIDTH_16) {
52+
LOG_ERR("Only supports 16-bit bus width");
53+
return -EINVAL;
54+
}
55+
56+
uint32_t hclk_freq =
57+
STM32_AHB_PRESCALER * DT_PROP(STM32_CLOCK_CONTROL_NODE, clock_frequency);
58+
59+
/* According to the FMC documentation*/
60+
fmc_write_cycles =
61+
((config->fmc_address_setup_time + 1) + (config->fmc_data_setup_time + 1)) * 1;
62+
63+
if (hclk_freq / fmc_write_cycles > dbi_config->config.frequency) {
64+
LOG_ERR("Frequency is too high for the display controller");
65+
return -EINVAL;
66+
}
67+
68+
data->dbi_config = dbi_config;
69+
return 0;
70+
}
71+
72+
int mipi_dbi_stm32_fmc_command_write(const struct device *dev,
73+
const struct mipi_dbi_config *dbi_config, uint8_t cmd,
74+
const uint8_t *data_buf, size_t len)
75+
{
76+
const struct mipi_dbi_stm32_fmc_config *config = dev->config;
77+
int ret;
78+
size_t i;
79+
80+
ret = mipi_dbi_stm32_fmc_check_config(dev, dbi_config);
81+
if (ret < 0) {
82+
return ret;
83+
}
84+
85+
sys_write16(cmd, config->register_addr);
86+
if (IS_ENABLED(CONFIG_MIPI_DBI_STM32_FMC_MEM_BARRIER)) {
87+
barrier_dsync_fence_full();
88+
}
89+
90+
for (i = 0U; i < len; i++) {
91+
sys_write16((uint16_t)data_buf[i], config->data_addr);
92+
if (IS_ENABLED(CONFIG_MIPI_DBI_STM32_FMC_MEM_BARRIER)) {
93+
barrier_dsync_fence_full();
94+
}
95+
}
96+
97+
return 0;
98+
}
99+
100+
static int mipi_dbi_stm32_fmc_write_display(const struct device *dev,
101+
const struct mipi_dbi_config *dbi_config,
102+
const uint8_t *framebuf,
103+
struct display_buffer_descriptor *desc,
104+
enum display_pixel_format pixfmt)
105+
{
106+
const struct mipi_dbi_stm32_fmc_config *config = dev->config;
107+
size_t i;
108+
int ret;
109+
110+
ret = mipi_dbi_stm32_fmc_check_config(dev, dbi_config);
111+
if (ret < 0) {
112+
return ret;
113+
}
114+
115+
for (i = 0U; i < desc->buf_size; i += 2) {
116+
sys_write16(sys_get_le16(&framebuf[i]), config->data_addr);
117+
if (IS_ENABLED(CONFIG_MIPI_DBI_STM32_FMC_MEM_BARRIER)) {
118+
barrier_dsync_fence_full();
119+
}
120+
}
121+
122+
return 0;
123+
}
124+
125+
static int mipi_dbi_stm32_fmc_reset(const struct device *dev, uint32_t delay)
126+
{
127+
const struct mipi_dbi_stm32_fmc_config *config = dev->config;
128+
int ret;
129+
130+
if (config->reset.port == NULL) {
131+
return -ENOTSUP;
132+
}
133+
134+
ret = gpio_pin_set_dt(&config->reset, 1);
135+
if (ret < 0) {
136+
return ret;
137+
}
138+
139+
k_msleep(delay);
140+
141+
return gpio_pin_set_dt(&config->reset, 0);
142+
}
143+
144+
static int mipi_dbi_stm32_fmc_init(const struct device *dev)
145+
{
146+
const struct mipi_dbi_stm32_fmc_config *config = dev->config;
147+
148+
if (config->reset.port) {
149+
if (!gpio_is_ready_dt(&config->reset)) {
150+
LOG_ERR("Reset GPIO device not ready");
151+
return -ENODEV;
152+
}
153+
154+
if (gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE)) {
155+
LOG_ERR("Couldn't configure reset pin");
156+
return -EIO;
157+
}
158+
}
159+
160+
if (config->power.port) {
161+
if (!gpio_is_ready_dt(&config->power)) {
162+
LOG_ERR("Power GPIO device not ready");
163+
return -ENODEV;
164+
}
165+
166+
if (gpio_pin_configure_dt(&config->power, GPIO_OUTPUT)) {
167+
LOG_ERR("Couldn't configure power pin");
168+
return -EIO;
169+
}
170+
}
171+
172+
return 0;
173+
}
174+
175+
static struct mipi_dbi_driver_api mipi_dbi_stm32_fmc_driver_api = {
176+
.reset = mipi_dbi_stm32_fmc_reset,
177+
.command_write = mipi_dbi_stm32_fmc_command_write,
178+
.write_display = mipi_dbi_stm32_fmc_write_display,
179+
};
180+
181+
#define MIPI_DBI_FMC_GET_ADDRESS(n) _CONCAT(FMC_BANK1_, UTIL_INC(DT_REG_ADDR(DT_INST_PARENT(n))))
182+
183+
#define MIPI_DBI_FMC_GET_DATA_ADDRESS(n) \
184+
MIPI_DBI_FMC_GET_ADDRESS(n) + (1 << (DT_INST_PROP(n, register_select_pin) + 1))
185+
186+
#define MIPI_DBI_STM32_FMC_INIT(n) \
187+
static const struct mipi_dbi_stm32_fmc_config mipi_dbi_stm32_fmc_config_##n = { \
188+
.reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \
189+
.power = GPIO_DT_SPEC_INST_GET_OR(n, power_gpios, {}), \
190+
.register_addr = MIPI_DBI_FMC_GET_ADDRESS(n), \
191+
.data_addr = MIPI_DBI_FMC_GET_DATA_ADDRESS(n), \
192+
.fmc_address_setup_time = DT_PROP_BY_IDX(DT_INST_PARENT(n), st_timing, 0), \
193+
.fmc_data_setup_time = DT_PROP_BY_IDX(DT_INST_PARENT(n), st_timing, 2), \
194+
.fmc_memory_width = DT_PROP_BY_IDX(DT_INST_PARENT(n), st_control, 2), \
195+
}; \
196+
\
197+
static struct mipi_dbi_stm32_fmc_data mipi_dbi_stm32_fmc_data_##n; \
198+
\
199+
DEVICE_DT_INST_DEFINE(n, mipi_dbi_stm32_fmc_init, NULL, &mipi_dbi_stm32_fmc_data_##n, \
200+
&mipi_dbi_stm32_fmc_config_##n, POST_KERNEL, \
201+
CONFIG_MIPI_DBI_INIT_PRIORITY, &mipi_dbi_stm32_fmc_driver_api);
202+
203+
DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_STM32_FMC_INIT)

0 commit comments

Comments
 (0)