1 /*
2 * Copyright (c) 2006-2022, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2011-12-16 onelife Initial creation of address mapped method (pixel
9 * drive) for EFM32GG_DK3750 board
10 * 2011-12-29 onelife Add direct drive method (frame buffer) support
11 */
12
13 /***************************************************************************//**
14 * @addtogroup EFM32GG_DK3750
15 * @{
16 ******************************************************************************/
17
18 /* Includes ------------------------------------------------------------------*/
19 #include "board.h"
20 #include "drv_usart.h"
21 #include "dev_lcd.h"
22
23 #if defined(EFM32_USING_LCD)
24 #if (!defined(LCD_MAPPED) && !defined(LCD_DIRECT))
25 #error "Unknown LCD access mode"
26 #endif
27 #include <rtgui/rtgui.h>
28 #include <rtgui/driver.h>
29
30 #include <dmd_ssd2119.h>
31
32 /* Private typedef -----------------------------------------------------------*/
33 /* Private define ------------------------------------------------------------*/
34 /* Private macro -------------------------------------------------------------*/
35 #ifdef EFM32_LCD_DEBUG
36 #define lcd_debug(format,args...) rt_kprintf(format, ##args)
37 #else
38 #define lcd_debug(format,args...)
39 #endif
40
41 /* Private function prototypes -----------------------------------------------*/
42 #if defined(LCD_MAPPED)
43 static void efm32_spiLcd_setPixel(rtgui_color_t *c, int x, int y);
44 static void efm32_spiLcd_getPixel(rtgui_color_t *c, int x, int y);
45 static void efm32_spiLcd_drawRawHLine(rt_uint8_t *pixels, int x1, int x2, int y);
46 static void efm32_spiLcd_drawHLine(rtgui_color_t *c, int x1, int x2, int y);
47 static void efm32_spiLcd_drawVLine(rtgui_color_t *c, int x1, int x2, int y);
48 #endif
49
50 /* Private variables ---------------------------------------------------------*/
51 static rt_device_t lcd;
52 static struct rt_device lcd_device;
53 static rt_bool_t lcdAutoCs = true;
54 static struct rt_device_graphic_info lcd_info;
55 #if defined(LCD_MAPPED)
56 static const struct rtgui_graphic_driver_ops lcd_ops =
57 {
58 efm32_spiLcd_setPixel,
59 efm32_spiLcd_getPixel,
60 efm32_spiLcd_drawHLine,
61 efm32_spiLcd_drawVLine,
62 efm32_spiLcd_drawRawHLine
63 };
64
65 /* Private functions ---------------------------------------------------------*/
66 /***************************************************************************//**
67 * @brief
68 * Draw a pixel with specified color
69 *
70 * @details
71 *
72 * @note
73 *
74 * @param[in] c
75 * Pointer to color
76 *
77 * @param[in] x
78 * Horizontal position
79 *
80 * @param[in] y
81 * Vertical position
82 ******************************************************************************/
efm32_spiLcd_setPixel(rtgui_color_t * c,int x,int y)83 static void efm32_spiLcd_setPixel(rtgui_color_t *c, int x, int y)
84 {
85 rt_uint32_t ret = RT_EOK;
86
87 do
88 {
89 /* Check if pixel is outside clipping region */
90 if ((x < 0) || (x > lcd_info.width))
91 {
92 break;
93 }
94 if ((y < 0) || (y > lcd_info.height))
95 {
96 break;
97 }
98
99 /* Write color */
100 ret = DMD_writePixel((rt_uint16_t)x, (rt_uint16_t)y, (rt_uint16_t)*c, 1);
101 if (ret != 0)
102 {
103 break;
104 }
105 return;
106 } while(0);
107
108 // lcd_debug("LCD err: Set pixel at (%d,%d: %x) failed (%x)!\n", x, y, *c, ret);
109 }
110
111 /***************************************************************************//**
112 * @brief
113 * Get the color of a pixel
114 *
115 * @details
116 *
117 * @note
118 *
119 * @param[out] c
120 * Pointer to color
121 *
122 * @param[in] x
123 * Horizontal position
124 *
125 * @param[in] y
126 * Vertical position
127 ******************************************************************************/
efm32_spiLcd_getPixel(rtgui_color_t * c,int x,int y)128 static void efm32_spiLcd_getPixel(rtgui_color_t *c, int x, int y)
129 {
130 rt_uint32_t ret = RT_EOK;
131
132 do
133 {
134 /* Check if pixel is outside clipping region */
135 if ((x < 0) || (x > lcd_info.width))
136 {
137 break;
138 }
139 if ((y < 0) || (y > lcd_info.height))
140 {
141 break;
142 }
143
144 /* Read color */
145 ret = DMD_readPixel((rt_uint16_t)x, (rt_uint16_t)y, (rt_uint16_t *)c);
146 if (ret != 0)
147 {
148 break;
149 }
150 return;
151 } while(0);
152
153 lcd_debug("LCD err: Get pixel at (%d,%d: %x) failed (%x)!\n",
154 x, y, *c, ret);
155 }
156
157 /***************************************************************************//**
158 * @brief
159 * Draw a horizontal line with raw color
160 *
161 * @details
162 *
163 * @note
164 *
165 * @param[in] pixels
166 * Pointer to raw color
167 *
168 * @param[in] x1
169 * Horizontal start position
170 *
171 * @param[in] x2
172 * Horizontal end position
173 *
174 * @param[in] y
175 * Vertical position
176 ******************************************************************************/
efm32_spiLcd_drawRawHLine(rt_uint8_t * pixels,int x1,int x2,int y)177 static void efm32_spiLcd_drawRawHLine(rt_uint8_t *pixels, int x1, int x2, int y)
178 {
179 lcd_debug("LCD: RAW H LINE!\n");
180 }
181
182 /***************************************************************************//**
183 * @brief
184 * Draw a horizontal line with specified color
185 *
186 * @details
187 *
188 * @note
189 *
190 * @param[in] c
191 * Pointer to color
192 *
193 * @param[in] x1
194 * Horizontal start position
195 *
196 * @param[in] x2
197 * Horizontal end position
198 *
199 * @param[in] y
200 * Vertical position
201 ******************************************************************************/
efm32_spiLcd_drawHLine(rtgui_color_t * c,int x1,int x2,int y)202 static void efm32_spiLcd_drawHLine(rtgui_color_t *c, int x1, int x2, int y)
203 {
204 rt_uint32_t ret = RT_EOK;
205
206 do
207 {
208 /* Check if line is outside of clipping region */
209 if ((y < 0) || (y > lcd_info.height))
210 {
211 break;
212 }
213
214 /* Swap the coordinates if x1 is larger than x2 */
215 if (x1 > x2)
216 {
217 int swap;
218 swap = x1;
219 x1 = x2;
220 x2 = swap;
221 }
222
223 /* Check if entire line is outside clipping region */
224 if ((x1 > lcd_info.width) || (x2 < 0))
225 {
226 /* Nothing to draw */
227 break;
228 }
229
230 /* Clip the line if necessary */
231 if (x1 < 0)
232 {
233 x1 = 0;
234 }
235 if (x2 > lcd_info.width)
236 {
237 x2 = lcd_info.width;
238 }
239
240 /* Write color */
241 rt_uint32_t length = x2 - x1 + 1;
242 ret = DMD_writePixel((rt_uint16_t)x1, (rt_uint16_t)y,
243 (rt_uint16_t)*c, length);
244 if (ret != 0)
245 {
246 break;
247 }
248 return;
249 } while(0);
250
251 // lcd_debug("LCD err: Draw hline at (%d-%d,%d: %x) failed (%x)!\n", x1, x2, y, *c, ret);
252 }
253
254 /***************************************************************************//**
255 * @brief
256 * Draw a vertical line with specified color
257 *
258 * @details
259 *
260 * @note
261 *
262 * @param[in] c
263 * Pointer to color
264 *
265 * @param[in] x
266 * Horizontal position
267 *
268 * @param[in] y1
269 * Vertical start position
270 *
271 * @param[in] y2
272 * Vertical end position
273 ******************************************************************************/
efm32_spiLcd_drawVLine(rtgui_color_t * c,int x,int y1,int y2)274 static void efm32_spiLcd_drawVLine(rtgui_color_t *c, int x , int y1, int y2)
275 {
276 rt_uint32_t ret = RT_EOK;
277
278 do
279 {
280 /* Check if line is outside of clipping region */
281 if ((x < 0) || (x > lcd_info.width))
282 {
283 break;
284 }
285
286 /* Swap the coordinates if y1 is larger than y2 */
287 if (y1 > y2)
288 {
289 rt_uint16_t swap;
290 swap = y1;
291 y1 = y2;
292 y2 = swap;
293 }
294
295 /* Check if entire line is outside clipping region */
296 if ((y1 > lcd_info.height) || (y2 < 0))
297 {
298 /* Nothing to draw */
299 break;
300 }
301
302 /* Clip the line if necessary */
303 if (y1 < 0)
304 {
305 y1 = 0;
306 }
307
308 if (y2 > lcd_info.height)
309 {
310 y2 = lcd_info.height;
311 }
312
313 /* Set clipping area */
314 rt_uint16_t length = y2 - y1 + 1;
315 ret = DMD_setClippingArea((rt_uint16_t)x, (rt_uint16_t)y1, 1, length);
316 if (ret != DMD_OK)
317 {
318 break;
319 }
320
321 /* Write color */
322 ret= DMD_writePixel(0, 0, (rt_uint16_t)*c, length);
323 if (ret != DMD_OK)
324 {
325 break;
326 }
327
328 /* Reset clipping area */
329 ret = DMD_setClippingArea(0, 0, lcd_info.width, lcd_info.height);
330 if (ret != DMD_OK)
331 {
332 break;
333 }
334 return;
335 } while(0);
336
337 // lcd_debug("LCD err: Draw vline at (%d,%d-%d: %x) failed (%x)!\n", x, y1, y2, *c, ret);
338 }
339 #endif
340
341 /***************************************************************************//**
342 * @brief
343 * Configure LCD device
344 *
345 * @details
346 *
347 * @note
348 *
349 * @param[in] dev
350 * Pointer to device descriptor
351 *
352 * @param[in] cmd
353 * IIC control command
354 *
355 * @param[in] args
356 * Arguments
357 *
358 * @return
359 * Error code
360 ******************************************************************************/
efm32_spiLcd_control(rt_device_t dev,int cmd,void * args)361 static rt_err_t efm32_spiLcd_control (rt_device_t dev, int cmd, void *args)
362 {
363 switch (cmd)
364 {
365 case RTGRAPHIC_CTRL_RECT_UPDATE:
366 break;
367 case RTGRAPHIC_CTRL_POWERON:
368 break;
369 case RTGRAPHIC_CTRL_POWEROFF:
370 break;
371 case RTGRAPHIC_CTRL_GET_INFO:
372 rt_memcpy(args, &lcd_info, sizeof(struct rt_device_graphic_info));
373 break;
374 case RTGRAPHIC_CTRL_SET_MODE:
375 break;
376 }
377
378 return RT_EOK;
379 }
380
381 /***************************************************************************//**
382 * @brief
383 * Set/Clear chip select
384 *
385 * @details
386 *
387 * @note
388 *
389 * @param[in] enable
390 * Chip select pin setting
391 ******************************************************************************/
efm32_spiLcd_cs(rt_uint8_t enable)392 static void efm32_spiLcd_cs(rt_uint8_t enable)
393 {
394 if (!lcdAutoCs)
395 {
396 if (enable)
397 {
398 GPIO_PinOutClear(LCD_CS_PORT, LCD_CS_PIN);
399 }
400 else
401 {
402 GPIO_PinOutSet(LCD_CS_PORT, LCD_CS_PIN);
403 }
404 }
405 }
406
407 /***************************************************************************//**
408 * @brief
409 * Write data to SSD2119 controller
410 *
411 * @param[in] reg
412 * Register to write to
413 *
414 * @param[in] data
415 * 16-bit data to write into register
416 *
417 * @note
418 * It's not possible to read back register value through SSD2119 SPI interface
419 ******************************************************************************/
efm32_spiLcd_writeRegister(rt_uint8_t reg,rt_uint16_t data)420 rt_err_t efm32_spiLcd_writeRegister(rt_uint8_t reg, rt_uint16_t data)
421 {
422 struct efm32_usart_device_t *usart;
423 rt_uint8_t buf_ins[3];
424 rt_uint8_t buf_res[3];
425
426 RT_ASSERT(lcd != RT_NULL);
427 usart = (struct efm32_usart_device_t *)(lcd->user_data);
428
429 /* Build instruction buffer */
430 buf_res[0] = (data & 0xff00) >> 8;
431 buf_res[1] = data & 0x00ff;
432 buf_ins[0] = 1; /* Instruction length */
433 buf_ins[1] = reg; /* Instruction */
434 *(rt_uint8_t **)(&buf_ins[2]) = buf_res; /* Data */
435 efm32_spiLcd_cs(1);
436 if (lcd->write(lcd, EFM32_NO_DATA, buf_ins, 2) == 0)
437 {
438 lcd_debug("LCD: Write data failed!\n");
439 return -RT_ERROR;
440 }
441 efm32_spiLcd_cs(0);
442
443 return RT_EOK;
444 }
445
446 /***************************************************************************//**
447 * @brief
448 * Register LCD device
449 *
450 * @details
451 *
452 * @note
453 *
454 * @param[in] device
455 * Pointer to device descriptor
456 *
457 * @param[in] name
458 * Device name
459 *
460 * @param[in] flag
461 * Configuration flags
462 *
463 * @param[in] iic
464 * Pointer to IIC device descriptor
465 *
466 * @return
467 * Error code
468 ******************************************************************************/
efm32_spiLcd_register(rt_device_t device,const char * name,rt_uint32_t flag,void * data)469 rt_err_t efm32_spiLcd_register(
470 rt_device_t device,
471 const char *name,
472 rt_uint32_t flag,
473 void *data)
474 {
475 RT_ASSERT(device != RT_NULL);
476
477 device->type = RT_Device_Class_Graphic;
478 device->rx_indicate = RT_NULL;
479 device->tx_complete = RT_NULL;
480 device->init = RT_NULL;
481 device->open = RT_NULL;
482 device->close = RT_NULL;
483 device->read = RT_NULL;
484 device->write = RT_NULL;
485 device->control = efm32_spiLcd_control;
486 device->user_data = data;
487
488 /* register a character device */
489 return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | flag);
490 }
491
492 /***************************************************************************//**
493 * @brief
494 * Initialize LCD device
495 *
496 * @details
497 *
498 * @note
499 *
500 ******************************************************************************/
efm32_spiLcd_init(void)501 void efm32_spiLcd_init(void)
502 {
503 struct efm32_usart_device_t *usart;
504 rt_uint32_t flag;
505 DMD_DisplayGeometry *geometry;
506 rt_uint32_t ret;
507
508 do
509 {
510 USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT;
511
512 /* Find SPI device */
513 lcd = rt_device_find(LCD_USING_DEVICE_NAME);
514 if (lcd == RT_NULL)
515 {
516 lcd_debug("LCD err: Can't find %s!\n", LCD_USING_DEVICE_NAME);
517 break;
518 }
519 lcd_debug("LCD: Find device %s\n", LCD_USING_DEVICE_NAME);
520
521 /* Config CS pin */
522 usart = (struct efm32_usart_device_t *)(lcd->user_data);
523 if (!(usart->state & USART_STATE_AUTOCS))
524 {
525 GPIO_PinModeSet(LCD_CS_PORT, LCD_CS_PIN, gpioModePushPull, 1);
526 lcdAutoCs = false;
527 }
528
529 /* TFT initialize or reinitialize. Assumes EBI has been configured
530 correctly in DVK_init(DVK_Init_EBI) */
531 rt_uint32_t freq = SystemCoreClockGet();
532 rt_uint32_t i;
533 rt_bool_t warning = RT_FALSE;
534
535 /* If we are in BC_UIF_AEM_EFM state, we can redraw graphics */
536 while (DVK_readRegister(&BC_REGISTER->UIF_AEM) != BC_UIF_AEM_EFM)
537 {
538 if (!warning)
539 {
540 lcd_debug("LCD: Please press AEM button!!!\n");
541 warning = RT_TRUE;
542 }
543 }
544
545 lcd_debug("LCD: Got LCD control\n");
546 /* If we're not BC_ARB_CTRL_EBI state, we need to reconfigure display controller */
547 if (DVK_readRegister(&BC_REGISTER->ARB_CTRL) != BC_ARB_CTRL_EBI)
548 {
549 lcd_debug("LCD: Set to EBI mode\n");
550 /* Configure for EBI mode and reset display */
551 DVK_displayControl(DVK_Display_EBI);
552 DVK_displayControl(DVK_Display_ResetAssert);
553 DVK_displayControl(DVK_Display_PowerDisable);
554 /* Short delay */
555 freq = SystemCoreClockGet();
556 for(i = 0; i < (freq / 100); i++)
557 {
558 __NOP();
559 }
560 #if defined(LCD_MAPPED)
561 /* Configure display for address mapped method + 3-wire SPI mode */
562 DVK_displayControl(DVK_Display_Mode8080);
563 DVK_displayControl(DVK_Display_PowerEnable);
564 DVK_displayControl(DVK_Display_ResetRelease);
565
566 /* Initialize graphics - abort on failure */
567 ret = DMD_init(BC_SSD2119_BASE, BC_SSD2119_BASE + 2);
568 if (ret == DMD_OK)
569 {
570 /* Make sure display is configured with correct rotation */
571 DMD_flipDisplay(1, 1);
572 }
573 else if (ret != DMD_ERROR_DRIVER_ALREADY_INITIALIZED)
574 {
575 lcd_debug("LCD err: driver init failed %x\n", ret);
576 break;
577 }
578 #elif defined(LCD_DIRECT)
579 /* Configure TFT direct drive method from EBI BANK2 */
580 const EBI_TFTInit_TypeDef tftInit =
581 {
582 ebiTFTBank2, /* Select EBI Bank 2 */
583 ebiTFTWidthHalfWord, /* Select 2-byte (16-bit RGB565) increments */
584 ebiTFTColorSrcMem, /* Use memory as source for mask/blending */
585 ebiTFTInterleaveUnlimited, /* Unlimited interleaved accesses */
586 ebiTFTFrameBufTriggerVSync, /* VSYNC as frame buffer update trigger */
587 false, /* Drive DCLK from negative edge of internal clock */
588 ebiTFTMBDisabled, /* No masking and alpha blending enabled */
589 ebiTFTDDModeExternal, /* Drive from external memory */
590 ebiActiveLow, /* CS Active Low polarity */
591 ebiActiveHigh, /* DCLK Active High polarity */
592 ebiActiveLow, /* DATAEN Active Low polarity */
593 ebiActiveLow, /* HSYNC Active Low polarity */
594 ebiActiveLow, /* VSYNC Active Low polarity */
595 320, /* Horizontal size in pixels */
596 1, /* Horizontal Front Porch */
597 30, /* Horizontal Back Porch */
598 2, /* Horizontal Synchronization Pulse Width */
599 240, /* Vertical size in pixels */
600 1, /* Vertical Front Porch */
601 4, /* Vertical Back Porch */
602 2, /* Vertical Synchronization Pulse Width */
603 0x0000, /* Frame Address pointer offset to EBI memory base */
604 4, /* DCLK Period */
605 0, /* DCLK Start cycles */
606 0, /* DCLK Setup cycles */
607 0, /* DCLK Hold cycles */
608 };
609
610 DVK_enablePeripheral(DVK_TFT);
611
612 /* Configure display for Direct Drive + 3-wire SPI mode */
613 DVK_displayControl(DVK_Display_ModeGeneric);
614 DVK_displayControl(DVK_Display_PowerEnable);
615 DVK_displayControl(DVK_Display_ResetRelease);
616
617 /* Configure GPIO for EBI and TFT */
618 /* EBI TFT DCLK/Dot Clock */
619 GPIO_PinModeSet(gpioPortA, 8, gpioModePushPull, 0);
620 /* EBI TFT DATAEN */
621 GPIO_PinModeSet(gpioPortA, 9, gpioModePushPull, 0);
622 /* EBI TFT VSYNC */
623 GPIO_PinModeSet(gpioPortA, 10, gpioModePushPull, 0);
624 /* EBI TFT HSYNC */
625 GPIO_PinModeSet(gpioPortA, 11, gpioModePushPull, 0);
626
627 /* Initialize display */
628 DMD_init(0, (rt_uint32_t)EBI_BankAddress(EBI_BANK2));
629
630 /* Configure EBI TFT direct drive */
631 EBI_TFTInit(&tftInit);
632 #endif
633 }
634
635 /* Get LCD geometry */
636 ret = DMD_getDisplayGeometry(&geometry);
637 if (ret != DMD_OK)
638 {
639 lcd_debug("LCD err: get geometry failed!\n");
640 break;
641 }
642
643 /* Init LCD info */
644 flag = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
645 lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565P;
646 lcd_info.bits_per_pixel = 16;
647 lcd_info.width = geometry->xSize;
648 lcd_info.height = geometry->ySize;
649 #if defined(LCD_MAPPED)
650 lcd_info.framebuffer = RT_NULL;
651 efm32_spiLcd_register(&lcd_device, LCD_DEVICE_NAME, flag, (void *)&lcd_ops);
652 #elif defined(LCD_DIRECT)
653 lcd_info.framebuffer = (rt_uint8_t *)EBI_BankAddress(EBI_BANK2);
654 efm32_spiLcd_register(&lcd_device, LCD_DEVICE_NAME, flag, RT_NULL);
655 #endif
656
657 /* Set clipping area */
658 ret = DMD_setClippingArea(0, 0, geometry->xSize, geometry->ySize);
659 if (ret != DMD_OK)
660 {
661 lcd_debug("LCD err: set clipping area failed!\n");
662 break;
663 }
664 /* Read device code */
665 rt_uint16_t code = 0xFFFF;
666 #if defined(LCD_MAPPED)
667 code = DMDIF_readDeviceCode();
668 #endif
669 /* Set as rtgui graphic driver */
670 rtgui_graphic_set_device(&lcd_device);
671
672 lcd_debug("LCD: H/W init OK!\n");
673 return;
674 } while(0);
675
676 lcd_debug("LCD err: H/W init failed!\n");
677 }
678
679 #endif /* defined(EFM32_USING_LCD) */
680 /***************************************************************************//**
681 * @}
682 ******************************************************************************/
683