1 /*
2  * Copyright (c) 2019, NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT fsl_imx6sx_lcdif
8 
9 #include <drivers/display.h>
10 #include <fsl_elcdif.h>
11 
12 #ifdef CONFIG_HAS_MCUX_CACHE
13 #include <fsl_cache.h>
14 #endif
15 
16 #include <logging/log.h>
17 
18 LOG_MODULE_REGISTER(display_mcux_elcdif, CONFIG_DISPLAY_LOG_LEVEL);
19 
20 K_HEAP_DEFINE(mcux_elcdif_pool,
21 	      CONFIG_MCUX_ELCDIF_POOL_BLOCK_MAX *
22 	      CONFIG_MCUX_ELCDIF_POOL_BLOCK_NUM);
23 
24 struct mcux_elcdif_config {
25 	LCDIF_Type *base;
26 	void (*irq_config_func)(const struct device *dev);
27 	elcdif_rgb_mode_config_t rgb_mode;
28 	enum display_pixel_format pixel_format;
29 	uint8_t bits_per_pixel;
30 };
31 
32 struct mcux_mem_block {
33 	void *data;
34 };
35 
36 struct mcux_elcdif_data {
37 	struct mcux_mem_block fb[2];
38 	struct k_sem sem;
39 	size_t pixel_bytes;
40 	size_t fb_bytes;
41 	uint8_t write_idx;
42 };
43 
mcux_elcdif_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)44 static int mcux_elcdif_write(const struct device *dev, const uint16_t x,
45 			     const uint16_t y,
46 			     const struct display_buffer_descriptor *desc,
47 			     const void *buf)
48 {
49 	const struct mcux_elcdif_config *config = dev->config;
50 	struct mcux_elcdif_data *data = dev->data;
51 
52 	uint8_t write_idx = data->write_idx;
53 	uint8_t read_idx = !write_idx;
54 
55 	int h_idx;
56 	const uint8_t *src;
57 	uint8_t *dst;
58 
59 	__ASSERT((data->pixel_bytes * desc->pitch * desc->height) <=
60 		 desc->buf_size, "Input buffer too small");
61 
62 	LOG_DBG("W=%d, H=%d, @%d,%d", desc->width, desc->height, x, y);
63 
64 	k_sem_take(&data->sem, K_FOREVER);
65 
66 	memcpy(data->fb[write_idx].data, data->fb[read_idx].data,
67 	       data->fb_bytes);
68 
69 	src = buf;
70 	dst = data->fb[data->write_idx].data;
71 	dst += data->pixel_bytes * (y * config->rgb_mode.panelWidth + x);
72 
73 	for (h_idx = 0; h_idx < desc->height; h_idx++) {
74 		memcpy(dst, src, data->pixel_bytes * desc->width);
75 		src += data->pixel_bytes * desc->pitch;
76 		dst += data->pixel_bytes * config->rgb_mode.panelWidth;
77 	}
78 
79 #ifdef CONFIG_HAS_MCUX_CACHE
80 	DCACHE_CleanByRange((uint32_t) data->fb[write_idx].data,
81 			    data->fb_bytes);
82 #endif
83 
84 	ELCDIF_SetNextBufferAddr(config->base,
85 				 (uint32_t) data->fb[write_idx].data);
86 
87 	data->write_idx = read_idx;
88 
89 	return 0;
90 }
91 
mcux_elcdif_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)92 static int mcux_elcdif_read(const struct device *dev, const uint16_t x,
93 			    const uint16_t y,
94 			    const struct display_buffer_descriptor *desc,
95 			    void *buf)
96 {
97 	LOG_ERR("Read not implemented");
98 	return -ENOTSUP;
99 }
100 
mcux_elcdif_get_framebuffer(const struct device * dev)101 static void *mcux_elcdif_get_framebuffer(const struct device *dev)
102 {
103 	LOG_ERR("Direct framebuffer access not implemented");
104 	return NULL;
105 }
106 
mcux_elcdif_display_blanking_off(const struct device * dev)107 static int mcux_elcdif_display_blanking_off(const struct device *dev)
108 {
109 	LOG_ERR("Display blanking control not implemented");
110 	return -ENOTSUP;
111 }
112 
mcux_elcdif_display_blanking_on(const struct device * dev)113 static int mcux_elcdif_display_blanking_on(const struct device *dev)
114 {
115 	LOG_ERR("Display blanking control not implemented");
116 	return -ENOTSUP;
117 }
118 
mcux_elcdif_set_brightness(const struct device * dev,const uint8_t brightness)119 static int mcux_elcdif_set_brightness(const struct device *dev,
120 				      const uint8_t brightness)
121 {
122 	LOG_WRN("Set brightness not implemented");
123 	return -ENOTSUP;
124 }
125 
mcux_elcdif_set_contrast(const struct device * dev,const uint8_t contrast)126 static int mcux_elcdif_set_contrast(const struct device *dev,
127 				    const uint8_t contrast)
128 {
129 	LOG_ERR("Set contrast not implemented");
130 	return -ENOTSUP;
131 }
132 
mcux_elcdif_set_pixel_format(const struct device * dev,const enum display_pixel_format pixel_format)133 static int mcux_elcdif_set_pixel_format(const struct device *dev,
134 					const enum display_pixel_format
135 					pixel_format)
136 {
137 	const struct mcux_elcdif_config *config = dev->config;
138 
139 	if (pixel_format == config->pixel_format) {
140 		return 0;
141 	}
142 	LOG_ERR("Pixel format change not implemented");
143 	return -ENOTSUP;
144 }
145 
mcux_elcdif_set_orientation(const struct device * dev,const enum display_orientation orientation)146 static int mcux_elcdif_set_orientation(const struct device *dev,
147 		const enum display_orientation orientation)
148 {
149 	if (orientation == DISPLAY_ORIENTATION_NORMAL) {
150 		return 0;
151 	}
152 	LOG_ERR("Changing display orientation not implemented");
153 	return -ENOTSUP;
154 }
155 
mcux_elcdif_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)156 static void mcux_elcdif_get_capabilities(const struct device *dev,
157 		struct display_capabilities *capabilities)
158 {
159 	const struct mcux_elcdif_config *config = dev->config;
160 
161 	memset(capabilities, 0, sizeof(struct display_capabilities));
162 	capabilities->x_resolution = config->rgb_mode.panelWidth;
163 	capabilities->y_resolution = config->rgb_mode.panelHeight;
164 	capabilities->supported_pixel_formats = config->pixel_format;
165 	capabilities->current_pixel_format = config->pixel_format;
166 	capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL;
167 }
168 
mcux_elcdif_isr(const struct device * dev)169 static void mcux_elcdif_isr(const struct device *dev)
170 {
171 	const struct mcux_elcdif_config *config = dev->config;
172 	struct mcux_elcdif_data *data = dev->data;
173 	uint32_t status;
174 
175 	status = ELCDIF_GetInterruptStatus(config->base);
176 	ELCDIF_ClearInterruptStatus(config->base, status);
177 
178 	k_sem_give(&data->sem);
179 }
180 
mcux_elcdif_init(const struct device * dev)181 static int mcux_elcdif_init(const struct device *dev)
182 {
183 	const struct mcux_elcdif_config *config = dev->config;
184 	struct mcux_elcdif_data *data = dev->data;
185 	int i;
186 
187 	elcdif_rgb_mode_config_t rgb_mode = config->rgb_mode;
188 
189 	data->pixel_bytes = config->bits_per_pixel / 8U;
190 	data->fb_bytes = data->pixel_bytes *
191 			 rgb_mode.panelWidth * rgb_mode.panelHeight;
192 	data->write_idx = 1U;
193 
194 	for (i = 0; i < ARRAY_SIZE(data->fb); i++) {
195 		data->fb[i].data = k_heap_alloc(&mcux_elcdif_pool,
196 						data->fb_bytes, K_NO_WAIT);
197 		if (data->fb[i].data == NULL) {
198 			LOG_ERR("Could not allocate frame buffer %d", i);
199 			return -ENOMEM;
200 		}
201 		memset(data->fb[i].data, 0, data->fb_bytes);
202 	}
203 	rgb_mode.bufferAddr = (uint32_t) data->fb[0].data;
204 
205 	k_sem_init(&data->sem, 1, 1);
206 
207 	config->irq_config_func(dev);
208 
209 	ELCDIF_RgbModeInit(config->base, &rgb_mode);
210 	ELCDIF_EnableInterrupts(config->base,
211 				kELCDIF_CurFrameDoneInterruptEnable);
212 	ELCDIF_RgbModeStart(config->base);
213 
214 	return 0;
215 }
216 
217 static const struct display_driver_api mcux_elcdif_api = {
218 	.blanking_on = mcux_elcdif_display_blanking_on,
219 	.blanking_off = mcux_elcdif_display_blanking_off,
220 	.write = mcux_elcdif_write,
221 	.read = mcux_elcdif_read,
222 	.get_framebuffer = mcux_elcdif_get_framebuffer,
223 	.set_brightness = mcux_elcdif_set_brightness,
224 	.set_contrast = mcux_elcdif_set_contrast,
225 	.get_capabilities = mcux_elcdif_get_capabilities,
226 	.set_pixel_format = mcux_elcdif_set_pixel_format,
227 	.set_orientation = mcux_elcdif_set_orientation,
228 };
229 
230 static void mcux_elcdif_config_func_1(const struct device *dev);
231 
232 static struct mcux_elcdif_config mcux_elcdif_config_1 = {
233 	.base = (LCDIF_Type *) DT_INST_REG_ADDR(0),
234 	.irq_config_func = mcux_elcdif_config_func_1,
235 #ifdef CONFIG_MCUX_ELCDIF_PANEL_RK043FN02H
236 	.rgb_mode = {
237 		.panelWidth = 480,
238 		.panelHeight = 272,
239 		.hsw = 41,
240 		.hfp = 4,
241 		.hbp = 8,
242 		.vsw = 10,
243 		.vfp = 4,
244 		.vbp = 2,
245 		.polarityFlags = kELCDIF_DataEnableActiveHigh |
246 				 kELCDIF_VsyncActiveLow |
247 				 kELCDIF_HsyncActiveLow |
248 				 kELCDIF_DriveDataOnRisingClkEdge,
249 		.pixelFormat = kELCDIF_PixelFormatRGB565,
250 		.dataBus = kELCDIF_DataBus16Bit,
251 	},
252 	.pixel_format = PIXEL_FORMAT_BGR_565,
253 	.bits_per_pixel = 16,
254 #endif
255 };
256 
257 static struct mcux_elcdif_data mcux_elcdif_data_1;
258 
259 DEVICE_DT_INST_DEFINE(0,
260 		    &mcux_elcdif_init,
261 		    NULL,
262 		    &mcux_elcdif_data_1, &mcux_elcdif_config_1,
263 		    POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
264 		    &mcux_elcdif_api);
265 
mcux_elcdif_config_func_1(const struct device * dev)266 static void mcux_elcdif_config_func_1(const struct device *dev)
267 {
268 	IRQ_CONNECT(DT_INST_IRQN(0),
269 		    DT_INST_IRQ(0, priority),
270 		    mcux_elcdif_isr, DEVICE_DT_INST_GET(0), 0);
271 
272 	irq_enable(DT_INST_IRQN(0));
273 }
274