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
mipi_dbi_stm32_fmc_check_config(const struct device * dev,const struct mipi_dbi_config * dbi_config)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
mipi_dbi_stm32_fmc_command_write(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t cmd,const uint8_t * data_buf,size_t len)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
mipi_dbi_stm32_fmc_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)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
mipi_dbi_stm32_fmc_reset(const struct device * dev,k_timeout_t delay)125 static int mipi_dbi_stm32_fmc_reset(const struct device *dev, k_timeout_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_sleep(delay);
140
141 return gpio_pin_set_dt(&config->reset, 0);
142 }
143
mipi_dbi_stm32_fmc_init(const struct device * dev)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 DEVICE_API(mipi_dbi, 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_, \
182 UTIL_INC(DT_REG_ADDR_RAW(DT_INST_PARENT(n))))
183
184 #define MIPI_DBI_FMC_GET_DATA_ADDRESS(n) \
185 MIPI_DBI_FMC_GET_ADDRESS(n) + (1 << (DT_INST_PROP(n, register_select_pin) + 1))
186
187 #define MIPI_DBI_STM32_FMC_INIT(n) \
188 static const struct mipi_dbi_stm32_fmc_config mipi_dbi_stm32_fmc_config_##n = { \
189 .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \
190 .power = GPIO_DT_SPEC_INST_GET_OR(n, power_gpios, {}), \
191 .register_addr = MIPI_DBI_FMC_GET_ADDRESS(n), \
192 .data_addr = MIPI_DBI_FMC_GET_DATA_ADDRESS(n), \
193 .fmc_address_setup_time = DT_PROP_BY_IDX(DT_INST_PARENT(n), st_timing, 0), \
194 .fmc_data_setup_time = DT_PROP_BY_IDX(DT_INST_PARENT(n), st_timing, 2), \
195 .fmc_memory_width = DT_PROP_BY_IDX(DT_INST_PARENT(n), st_control, 2), \
196 }; \
197 \
198 static struct mipi_dbi_stm32_fmc_data mipi_dbi_stm32_fmc_data_##n; \
199 \
200 DEVICE_DT_INST_DEFINE(n, mipi_dbi_stm32_fmc_init, NULL, &mipi_dbi_stm32_fmc_data_##n, \
201 &mipi_dbi_stm32_fmc_config_##n, POST_KERNEL, \
202 CONFIG_MIPI_DBI_INIT_PRIORITY, &mipi_dbi_stm32_fmc_driver_api);
203
204 DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_STM32_FMC_INIT)
205