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