1 /**
2  * @file lv_gpu_stm32_dma2d.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_gpu_stm32_dma2d.h"
10 #include "../lv_core/lv_disp.h"
11 #include "../lv_core/lv_refr.h"
12 
13 #if LV_USE_GPU_STM32_DMA2D
14 
15 #include LV_GPU_DMA2D_CMSIS_INCLUDE
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 
21 #if LV_COLOR_16_SWAP
22     // TODO: F7 has red blue swap bit in control register for all layers and output
23     #error "Can't use DMA2D with LV_COLOR_16_SWAP 1"
24 #endif
25 
26 #if LV_COLOR_DEPTH == 8
27     #error "Can't use DMA2D with LV_COLOR_DEPTH == 8"
28 #endif
29 
30 #if LV_COLOR_DEPTH == 16
31     #define LV_DMA2D_COLOR_FORMAT LV_DMA2D_RGB565
32 #elif LV_COLOR_DEPTH == 32
33     #define LV_DMA2D_COLOR_FORMAT LV_DMA2D_ARGB8888
34 #else
35     /*Can't use GPU with other formats*/
36 #endif
37 
38 /**********************
39  *      TYPEDEFS
40  **********************/
41 
42 /**********************
43  *  STATIC PROTOTYPES
44  **********************/
45 static void invalidate_cache(void);
46 static void dma2d_wait(void);
47 
48 /**********************
49  *  STATIC VARIABLES
50  **********************/
51 
52 /**********************
53  *      MACROS
54  **********************/
55 
56 /**********************
57  *   GLOBAL FUNCTIONS
58  **********************/
59 
60 /**
61  * Turn on the peripheral and set output color mode, this only needs to be done once
62  */
lv_gpu_stm32_dma2d_init(void)63 void lv_gpu_stm32_dma2d_init(void)
64 {
65     /* Enable DMA2D clock */
66     RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
67 
68     /* Delay after setting peripheral clock */
69     volatile uint32_t temp = RCC->AHB1ENR;
70 
71     /* set output colour mode */
72     DMA2D->OPFCCR = LV_DMA2D_COLOR_FORMAT;
73 }
74 
75 /**
76  * Fill an area in the buffer with a color
77  * @param buf a buffer which should be filled
78  * @param buf_w width of the buffer in pixels
79  * @param color fill color
80  * @param fill_w width to fill in pixels (<= buf_w)
81  * @param fill_h height to fill in pixels
82  * @note `buf_w - fill_w` is offset to the next line after fill
83  */
lv_gpu_stm32_dma2d_fill(lv_color_t * buf,lv_coord_t buf_w,lv_color_t color,lv_coord_t fill_w,lv_coord_t fill_h)84 void lv_gpu_stm32_dma2d_fill(lv_color_t * buf, lv_coord_t buf_w, lv_color_t color, lv_coord_t fill_w, lv_coord_t fill_h)
85 {
86     invalidate_cache();
87 
88     DMA2D->CR = 0x30000;
89     DMA2D->OMAR = (uint32_t)buf;
90     /* as input color mode is same as output we don't need to convert here do we? */
91     DMA2D->OCOLR = color.full;
92     DMA2D->OOR = buf_w - fill_w;
93     DMA2D->NLR = (fill_w << DMA2D_NLR_PL_Pos) | (fill_h << DMA2D_NLR_NL_Pos);
94 
95     /* start transfer */
96     DMA2D->CR |= DMA2D_CR_START_Msk;
97 
98     dma2d_wait();
99 }
100 
101 /**
102  * Fill an area in the buffer with a color but take into account a mask which describes the opacity of each pixel
103  * @param buf a buffer which should be filled using a mask
104  * @param buf_w width of the buffer in pixels
105  * @param color fill color
106  * @param mask 0..255 values describing the opacity of the corresponding pixel. It's width is `fill_w`
107  * @param opa overall opacity. 255 in `mask` should mean this opacity.
108  * @param fill_w width to fill in pixels (<= buf_w)
109  * @param fill_h height to fill in pixels
110  * @note `buf_w - fill_w` is offset to the next line after fill
111  */
lv_gpu_stm32_dma2d_fill_mask(lv_color_t * buf,lv_coord_t buf_w,lv_color_t color,const lv_opa_t * mask,lv_opa_t opa,lv_coord_t fill_w,lv_coord_t fill_h)112 void lv_gpu_stm32_dma2d_fill_mask(lv_color_t * buf, lv_coord_t buf_w, lv_color_t color, const lv_opa_t * mask,
113                                   lv_opa_t opa, lv_coord_t fill_w, lv_coord_t fill_h)
114 {
115 #if 0
116     invalidate_cache();
117 
118     /* Configure the DMA2D Mode, Color Mode and line output offset */
119     hdma2d.Init.Mode         = DMA2D_M2M_BLEND;
120     hdma2d.Init.ColorMode    = DMA2D_OUTPUT_FORMAT;
121     hdma2d.Init.OutputOffset = buf_w - fill_w;
122 
123     /* Configure the foreground -> The character */
124     lv_color32_t c32;
125     c32.full = lv_color_to32(color);
126     c32.ch.alpha = opa;
127     hdma2d.LayerCfg[1].AlphaMode       = DMA2D_COMBINE_ALPHA;
128     hdma2d.LayerCfg[1].InputAlpha      = c32.full;
129     hdma2d.LayerCfg[1].InputColorMode  = DMA2D_INPUT_A8;
130     hdma2d.LayerCfg[1].InputOffset     = 0;
131 
132     /* Configure the background -> Display buffer */
133     hdma2d.LayerCfg[0].AlphaMode       = DMA2D_NO_MODIF_ALPHA;
134     hdma2d.LayerCfg[0].InputAlpha      = 0x00;
135     hdma2d.LayerCfg[0].InputColorMode  = DMA2D_INPUT_FORMAT;
136     hdma2d.LayerCfg[0].InputOffset     = buf_w - fill_w;
137 
138     /* DMA2D Initialization */
139     HAL_DMA2D_Init(&hdma2d);
140     HAL_DMA2D_ConfigLayer(&hdma2d, 0);
141     HAL_DMA2D_ConfigLayer(&hdma2d, 1);
142     HAL_DMA2D_BlendingStart(&hdma2d, (uint32_t) mask, (uint32_t) buf, (uint32_t)buf, fill_w, fill_h);
143     dma2d_wait();
144 #endif
145 }
146 
147 /**
148  * Copy a map (typically RGB image) to a buffer
149  * @param buf a buffer where map should be copied
150  * @param buf_w width of the buffer in pixels
151  * @param map an "image" to copy
152  * @param map_w width of the map in pixels
153  * @param copy_w width of the area to copy in pixels (<= buf_w)
154  * @param copy_h height of the area to copy in pixels
155  * @note `map_w - fill_w` is offset to the next line after copy
156  */
lv_gpu_stm32_dma2d_copy(lv_color_t * buf,lv_coord_t buf_w,const lv_color_t * map,lv_coord_t map_w,lv_coord_t copy_w,lv_coord_t copy_h)157 void lv_gpu_stm32_dma2d_copy(lv_color_t * buf, lv_coord_t buf_w, const lv_color_t * map, lv_coord_t map_w,
158                              lv_coord_t copy_w, lv_coord_t copy_h)
159 {
160     invalidate_cache();
161 
162     DMA2D->CR = 0;
163     /* copy output colour mode, this register controls both input and output colour format */
164     DMA2D->FGPFCCR = LV_DMA2D_COLOR_FORMAT;
165     DMA2D->FGMAR = (uint32_t)map;
166     DMA2D->FGOR = map_w - copy_w;
167     DMA2D->OMAR = (uint32_t)buf;
168     DMA2D->OOR = buf_w - copy_w;
169     DMA2D->NLR = (copy_w << DMA2D_NLR_PL_Pos) | (copy_h << DMA2D_NLR_NL_Pos);
170 
171     /* start transfer */
172     DMA2D->CR |= DMA2D_CR_START_Msk;
173     dma2d_wait();
174 }
175 
176 /**
177  * Blend a map (e.g. ARGB image or RGB image with opacity) to a buffer
178  * @param buf a buffer where `map` should be copied
179  * @param buf_w width of the buffer in pixels
180  * @param map an "image" to copy
181  * @param opa opacity of `map`
182  * @param map_w width of the map in pixels
183  * @param copy_w width of the area to copy in pixels (<= buf_w)
184  * @param copy_h height of the area to copy in pixels
185  * @note `map_w - fill_w` is offset to the next line after copy
186  */
lv_gpu_stm32_dma2d_blend(lv_color_t * buf,lv_coord_t buf_w,const lv_color_t * map,lv_opa_t opa,lv_coord_t map_w,lv_coord_t copy_w,lv_coord_t copy_h)187 void lv_gpu_stm32_dma2d_blend(lv_color_t * buf, lv_coord_t buf_w, const lv_color_t * map, lv_opa_t opa,
188                               lv_coord_t map_w, lv_coord_t copy_w, lv_coord_t copy_h)
189 {
190     invalidate_cache();
191     DMA2D->CR = 0x20000;
192 
193     DMA2D->BGPFCCR = LV_DMA2D_COLOR_FORMAT;
194     DMA2D->BGMAR = (uint32_t)buf;
195     DMA2D->BGOR = buf_w - copy_w;
196 
197     DMA2D->FGPFCCR = (uint32_t)LV_DMA2D_COLOR_FORMAT
198                      /* alpha mode 2, replace with foreground * alpha value */
199                      | (2 << DMA2D_FGPFCCR_AM_Pos)
200                      /* alpha value */
201                      | (opa << DMA2D_FGPFCCR_ALPHA_Pos);
202     DMA2D->FGMAR = (uint32_t)map;
203     DMA2D->FGOR = map_w - copy_w;
204 
205     DMA2D->OMAR = (uint32_t)buf;
206     DMA2D->OOR = buf_w - copy_w;
207     DMA2D->NLR = (copy_w << DMA2D_NLR_PL_Pos) | (copy_h << DMA2D_NLR_NL_Pos);
208 
209     /* start transfer */
210     DMA2D->CR |= DMA2D_CR_START_Msk;
211     dma2d_wait();
212 }
213 
214 /**********************
215  *   STATIC FUNCTIONS
216  **********************/
217 
invalidate_cache(void)218 static void invalidate_cache(void)
219 {
220     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
221     if(disp->driver.clean_dcache_cb) disp->driver.clean_dcache_cb(&disp->driver);
222     else {
223 #if __CORTEX_M >= 0x07
224         SCB_CleanInvalidateDCache();
225 #endif
226     }
227 }
228 
dma2d_wait(void)229 static void dma2d_wait(void)
230 {
231     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
232     while(DMA2D->CR & DMA2D_CR_START_Msk) {
233         if(disp->driver.wait_cb) disp->driver.wait_cb(&disp->driver);
234     }
235 }
236 
237 #endif
238