µGFX library fork

gdisp_lld_STM32LTDC.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /*
  2. * This file is subject to the terms of the GFX License. If a copy of
  3. * the license was not distributed with this file, you can obtain one at:
  4. *
  5. * http://ugfx.org/license.html
  6. */
  7. #include "gfx.h"
  8. #if GFX_USE_GDISP
  9. #define GDISP_DRIVER_VMT GDISPVMT_STM32LTDC
  10. #include "gdisp_lld_config.h"
  11. #include "../../../src/gdisp/gdisp_driver.h"
  12. #if defined(GDISP_SCREEN_HEIGHT) || defined(GDISP_SCREEN_HEIGHT)
  13. #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
  14. #warning "GDISP: This low level driver does not support setting a screen size. It is being ignored."
  15. #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
  16. COMPILER_WARNING("GDISP: This low level driver does not support setting a screen size. It is being ignored.")
  17. #endif
  18. #undef GDISP_SCREEN_WIDTH
  19. #undef GDISP_SCREEN_HEIGHT
  20. #endif
  21. #ifndef LTDC_USE_DMA2D
  22. #define LTDC_USE_DMA2D FALSE
  23. #endif
  24. #ifndef LTDC_NO_CLOCK_INIT
  25. #define LTDC_NO_CLOCK_INIT FALSE
  26. #endif
  27. #include "stm32_ltdc.h"
  28. #if LTDC_USE_DMA2D
  29. #include "stm32_dma2d.h"
  30. #endif
  31. typedef struct ltdcLayerConfig {
  32. // Frame
  33. LLDCOLOR_TYPE* frame; // Frame buffer address
  34. coord_t width, height; // Frame size in pixels
  35. coord_t pitch; // Line pitch, in bytes
  36. uint16_t fmt; // Pixel format in LTDC format
  37. // Window
  38. coord_t x, y; // Start pixel position of the virtual layer
  39. coord_t cx, cy; // Size of the virtual layer
  40. uint32_t defcolor; // Default color, ARGB8888
  41. uint32_t keycolor; // Color key, RGB888
  42. uint32_t blending; // Blending factors
  43. const uint32_t* palette; // The palette, RGB888 (can be NULL)
  44. uint16_t palettelen; // Palette length
  45. uint8_t alpha; // Constant alpha factor
  46. uint8_t layerflags; // Layer configuration
  47. } ltdcLayerConfig;
  48. typedef struct ltdcConfig {
  49. coord_t width, height; // Screen size
  50. coord_t hsync, vsync; // Horizontal and Vertical sync pixels
  51. coord_t hbackporch, vbackporch; // Horizontal and Vertical back porch pixels
  52. coord_t hfrontporch, vfrontporch; // Horizontal and Vertical front porch pixels
  53. uint32_t syncflags; // Sync flags
  54. uint32_t bgcolor; // Clear screen color RGB888
  55. ltdcLayerConfig bglayer; // Background layer config
  56. ltdcLayerConfig fglayer; // Foreground layer config
  57. } ltdcConfig;
  58. #define LTDC_UNUSED_LAYER_CONFIG { 0, 1, 1, 1, LTDC_FMT_L8, 0, 0, 1, 1, 0x000000, 0x000000, LTDC_BLEND_FIX1_FIX2, 0, 0, 0, 0 }
  59. #if GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_RGB565
  60. #define LTDC_PIXELFORMAT LTDC_FMT_RGB565
  61. #define LTDC_PIXELBYTES 2
  62. #define LTDC_PIXELBITS 16
  63. #elif GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_RGB888
  64. #define LTDC_PIXELFORMAT LTDC_FMT_ARGB8888
  65. #define LTDC_PIXELBYTES 4
  66. #define LTDC_PIXELBITS 32
  67. #else
  68. #error "GDISP: STM32LTDC - unsupported pixel format"
  69. #endif
  70. #include "board_STM32LTDC.h"
  71. /*===========================================================================*/
  72. /* Driver local definitions. */
  73. /*===========================================================================*/
  74. #ifndef GDISP_INITIAL_CONTRAST
  75. #define GDISP_INITIAL_CONTRAST 50
  76. #endif
  77. #ifndef GDISP_INITIAL_BACKLIGHT
  78. #define GDISP_INITIAL_BACKLIGHT 100
  79. #endif
  80. /*===========================================================================*/
  81. /* Driver local routines. */
  82. /*===========================================================================*/
  83. #define PIXIL_POS(g, x, y) ((y) * driverCfg.bglayer.pitch + (x) * LTDC_PIXELBYTES)
  84. #define PIXEL_ADDR(g, pos) ((LLDCOLOR_TYPE *)((uint8_t *)driverCfg.bglayer.frame+pos))
  85. /*===========================================================================*/
  86. /* Driver exported functions. */
  87. /*===========================================================================*/
  88. static void _ltdc_reload(void) {
  89. LTDC->SRCR |= LTDC_SRCR_IMR;
  90. while (LTDC->SRCR & (LTDC_SRCR_IMR | LTDC_SRCR_VBR))
  91. gfxYield();
  92. }
  93. static void _ltdc_layer_init(LTDC_Layer_TypeDef* pLayReg, const ltdcLayerConfig* pCfg) {
  94. static const uint8_t fmt2Bpp[] = {
  95. 4, /* LTDC_FMT_ARGB8888 */
  96. 3, /* LTDC_FMT_RGB888 */
  97. 2, /* LTDC_FMT_RGB565 */
  98. 2, /* LTDC_FMT_ARGB1555 */
  99. 2, /* LTDC_FMT_ARGB4444 */
  100. 1, /* LTDC_FMT_L8 */
  101. 1, /* LTDC_FMT_AL44 */
  102. 2 /* LTDC_FMT_AL88 */
  103. };
  104. uint32_t start, stop;
  105. // Set the framebuffer dimensions and format
  106. pLayReg->PFCR = (pLayReg->PFCR & ~LTDC_LxPFCR_PF) | ((uint32_t)pCfg->fmt & LTDC_LxPFCR_PF);
  107. pLayReg->CFBAR = (uint32_t)pCfg->frame & LTDC_LxCFBAR_CFBADD;
  108. pLayReg->CFBLR = ((((uint32_t)pCfg->pitch << 16) & LTDC_LxCFBLR_CFBP) | (((uint32_t)fmt2Bpp[pCfg->fmt] * pCfg->width + 3) & LTDC_LxCFBLR_CFBLL));
  109. pLayReg->CFBLNR = (uint32_t)pCfg->height & LTDC_LxCFBLNR_CFBLNBR;
  110. // Set the display window boundaries
  111. start = (uint32_t)pCfg->x + driverCfg.hsync + driverCfg.hbackporch;
  112. stop = start + pCfg->cx - 1;
  113. pLayReg->WHPCR = ((start << 0) & LTDC_LxWHPCR_WHSTPOS) | ((stop << 16) & LTDC_LxWHPCR_WHSPPOS);
  114. start = (uint32_t)pCfg->y + driverCfg.vsync + driverCfg.vbackporch;
  115. stop = start + pCfg->cy - 1;
  116. pLayReg->WVPCR = ((start << 0) & LTDC_LxWVPCR_WVSTPOS) | ((stop << 16) & LTDC_LxWVPCR_WVSPPOS);
  117. // Set colors
  118. pLayReg->DCCR = pCfg->defcolor;
  119. pLayReg->CKCR = (pLayReg->CKCR & ~0x00FFFFFF) | (pCfg->keycolor & 0x00FFFFFF);
  120. pLayReg->CACR = (pLayReg->CACR & ~LTDC_LxCACR_CONSTA) | ((uint32_t)pCfg->alpha & LTDC_LxCACR_CONSTA);
  121. pLayReg->BFCR = (pLayReg->BFCR & ~(LTDC_LxBFCR_BF1 | LTDC_LxBFCR_BF2)) | ((uint32_t)pCfg->blending & (LTDC_LxBFCR_BF1 | LTDC_LxBFCR_BF2));
  122. for (start = 0; start < pCfg->palettelen; start++)
  123. pLayReg->CLUTWR = ((uint32_t)start << 24) | (pCfg->palette[start] & 0x00FFFFFF);
  124. // Final flags
  125. pLayReg->CR = (pLayReg->CR & ~LTDC_LEF_MASK) | ((uint32_t)pCfg->layerflags & LTDC_LEF_MASK);
  126. }
  127. static void _ltdc_init(void) {
  128. // Set up the display scanning
  129. uint32_t hacc, vacc;
  130. // Reset the LTDC peripheral
  131. RCC->APB2RSTR |= RCC_APB2RSTR_LTDCRST;
  132. RCC->APB2RSTR = 0;
  133. // Enable the LTDC clock
  134. #if !LTDC_NO_CLOCK_INIT
  135. #if defined(STM32F469xx)
  136. RCC->DCKCFGR = (RCC->DCKCFGR & ~RCC_DCKCFGR_PLLSAIDIVR);
  137. #elif defined(STM32F4) || defined(STM32F429_439xx) || defined(STM32F429xx)
  138. RCC->DCKCFGR = (RCC->DCKCFGR & ~RCC_DCKCFGR_PLLSAIDIVR) | (1 << 16);
  139. #elif defined(STM32F7) || defined(STM32F746xx)
  140. RCC->DCKCFGR1 = (RCC->DCKCFGR1 & ~RCC_DCKCFGR1_PLLSAIDIVR) | (1 << 16);
  141. #else
  142. #error STM32LTDC driver not implemented for your platform
  143. #endif
  144. #endif
  145. // Enable the peripheral
  146. RCC->APB2ENR |= RCC_APB2ENR_LTDCEN;
  147. // Turn off the controller and its interrupts
  148. LTDC->GCR = 0;
  149. LTDC->IER = 0;
  150. _ltdc_reload();
  151. // Set synchronization params
  152. hacc = driverCfg.hsync - 1;
  153. vacc = driverCfg.vsync - 1;
  154. LTDC->SSCR = ((hacc << 16) & LTDC_SSCR_HSW) | ((vacc << 0) & LTDC_SSCR_VSH);
  155. // Set accumulated back porch params
  156. hacc += driverCfg.hbackporch;
  157. vacc += driverCfg.vbackporch;
  158. LTDC->BPCR = ((hacc << 16) & LTDC_BPCR_AHBP) | ((vacc << 0) & LTDC_BPCR_AVBP);
  159. // Set accumulated active params
  160. hacc += driverCfg.width;
  161. vacc += driverCfg.height;
  162. LTDC->AWCR = ((hacc << 16) & LTDC_AWCR_AAW) | ((vacc << 0) & LTDC_AWCR_AAH);
  163. // Set accumulated total params
  164. hacc += driverCfg.hfrontporch;
  165. vacc += driverCfg.vfrontporch;
  166. LTDC->TWCR = ((hacc << 16) & LTDC_TWCR_TOTALW) | ((vacc << 0) & LTDC_TWCR_TOTALH);
  167. // Set signal polarities and other flags
  168. LTDC->GCR = driverCfg.syncflags & (LTDC_EF_MASK & ~LTDC_EF_ENABLE);
  169. // Set background color
  170. LTDC->BCCR = (LTDC->BCCR & ~0x00FFFFFF) | (driverCfg.bgcolor & 0x00FFFFFF);
  171. // Load the background layer
  172. _ltdc_layer_init(LTDC_Layer1, &driverCfg.bglayer);
  173. // Load the foreground layer
  174. _ltdc_layer_init(LTDC_Layer2, &driverCfg.fglayer);
  175. // Interrupt handling
  176. // Possible flags - LTDC_IER_RRIE, LTDC_IER_LIE, LTDC_IER_FUIE, LTDC_IER_TERRIE etc
  177. LTDC->IER = 0;
  178. // Set everything going
  179. _ltdc_reload();
  180. LTDC->GCR |= LTDC_GCR_LTDCEN;
  181. _ltdc_reload();
  182. }
  183. LLDSPEC bool_t gdisp_lld_init(GDisplay* g) {
  184. // Initialize the private structure
  185. g->priv = 0;
  186. g->board = 0;
  187. // Init the board
  188. init_board(g);
  189. // Initialise the LTDC controller
  190. _ltdc_init();
  191. // Initialise DMA2D
  192. #if LTDC_USE_DMA2D
  193. dma2d_init();
  194. #endif
  195. // Finish Init the board
  196. post_init_board(g);
  197. // Turn on the back-light
  198. set_backlight(g, GDISP_INITIAL_BACKLIGHT);
  199. // Initialise the GDISP structure
  200. g->g.Width = driverCfg.bglayer.width;
  201. g->g.Height = driverCfg.bglayer.height;
  202. g->g.Orientation = GDISP_ROTATE_0;
  203. g->g.Powermode = powerOn;
  204. g->g.Backlight = GDISP_INITIAL_BACKLIGHT;
  205. g->g.Contrast = GDISP_INITIAL_CONTRAST;
  206. return TRUE;
  207. }
  208. LLDSPEC void gdisp_lld_draw_pixel(GDisplay* g) {
  209. unsigned pos;
  210. #if GDISP_NEED_CONTROL
  211. switch(g->g.Orientation) {
  212. case GDISP_ROTATE_0:
  213. default:
  214. pos = PIXIL_POS(g, g->p.x, g->p.y);
  215. break;
  216. case GDISP_ROTATE_90:
  217. pos = PIXIL_POS(g, g->p.y, g->g.Width-g->p.x-1);
  218. break;
  219. case GDISP_ROTATE_180:
  220. pos = PIXIL_POS(g, g->g.Width-g->p.x-1, g->g.Height-g->p.y-1);
  221. break;
  222. case GDISP_ROTATE_270:
  223. pos = PIXIL_POS(g, g->g.Height-g->p.y-1, g->p.x);
  224. break;
  225. }
  226. #else
  227. pos = PIXIL_POS(g, g->p.x, g->p.y);
  228. #endif
  229. #if LTDC_USE_DMA2D
  230. while(DMA2D->CR & DMA2D_CR_START);
  231. #endif
  232. PIXEL_ADDR(g, pos)[0] = gdispColor2Native(g->p.color);
  233. }
  234. LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay* g) {
  235. unsigned pos;
  236. LLDCOLOR_TYPE color;
  237. #if GDISP_NEED_CONTROL
  238. switch(g->g.Orientation) {
  239. case GDISP_ROTATE_0:
  240. default:
  241. pos = PIXIL_POS(g, g->p.x, g->p.y);
  242. break;
  243. case GDISP_ROTATE_90:
  244. pos = PIXIL_POS(g, g->p.y, g->g.Width-g->p.x-1);
  245. break;
  246. case GDISP_ROTATE_180:
  247. pos = PIXIL_POS(g, g->g.Width-g->p.x-1, g->g.Height-g->p.y-1);
  248. break;
  249. case GDISP_ROTATE_270:
  250. pos = PIXIL_POS(g, g->g.Height-g->p.y-1, g->p.x);
  251. break;
  252. }
  253. #else
  254. pos = PIXIL_POS(g, g->p.x, g->p.y);
  255. #endif
  256. #if LTDC_USE_DMA2D
  257. while(DMA2D->CR & DMA2D_CR_START);
  258. #endif
  259. color = PIXEL_ADDR(g, pos)[0];
  260. return gdispNative2Color(color);
  261. }
  262. #if GDISP_NEED_CONTROL
  263. LLDSPEC void gdisp_lld_control(GDisplay* g) {
  264. switch(g->p.x) {
  265. case GDISP_CONTROL_POWER:
  266. if (g->g.Powermode == (powermode_t)g->p.ptr)
  267. return;
  268. switch((powermode_t)g->p.ptr) {
  269. case powerOff: case powerOn: case powerSleep: case powerDeepSleep:
  270. // TODO
  271. break;
  272. default:
  273. return;
  274. }
  275. g->g.Powermode = (powermode_t)g->p.ptr;
  276. return;
  277. case GDISP_CONTROL_ORIENTATION:
  278. if (g->g.Orientation == (orientation_t)g->p.ptr)
  279. return;
  280. switch((orientation_t)g->p.ptr) {
  281. case GDISP_ROTATE_0:
  282. case GDISP_ROTATE_180:
  283. if (g->g.Orientation == GDISP_ROTATE_90 || g->g.Orientation == GDISP_ROTATE_270) {
  284. coord_t tmp;
  285. tmp = g->g.Width;
  286. g->g.Width = g->g.Height;
  287. g->g.Height = tmp;
  288. }
  289. break;
  290. case GDISP_ROTATE_90:
  291. case GDISP_ROTATE_270:
  292. if (g->g.Orientation == GDISP_ROTATE_0 || g->g.Orientation == GDISP_ROTATE_180) {
  293. coord_t tmp;
  294. tmp = g->g.Width;
  295. g->g.Width = g->g.Height;
  296. g->g.Height = tmp;
  297. }
  298. break;
  299. default:
  300. return;
  301. }
  302. g->g.Orientation = (orientation_t)g->p.ptr;
  303. return;
  304. case GDISP_CONTROL_BACKLIGHT:
  305. if ((unsigned)g->p.ptr > 100) g->p.ptr = (void *)100;
  306. set_backlight(g, (unsigned)g->p.ptr);
  307. g->g.Backlight = (unsigned)g->p.ptr;
  308. return;
  309. case GDISP_CONTROL_CONTRAST:
  310. if ((unsigned)g->p.ptr > 100) g->p.ptr = (void *)100;
  311. // TODO
  312. g->g.Contrast = (unsigned)g->p.ptr;
  313. return;
  314. }
  315. }
  316. #endif
  317. #if LTDC_USE_DMA2D
  318. static void dma2d_init(void) {
  319. // Enable DMA2D clock
  320. RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
  321. // Output color format
  322. #if GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_RGB565
  323. DMA2D->OPFCCR = OPFCCR_RGB565;
  324. #elif GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_RGB888
  325. DMA2D->OPFCCR = OPFCCR_ARGB8888;
  326. #endif
  327. // Foreground color format
  328. #if GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_RGB565
  329. DMA2D->FGPFCCR = FGPFCCR_CM_RGB565;
  330. #elif GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_RGB888
  331. DMA2D->FGPFCCR = FGPFCCR_CM_ARGB8888;
  332. #endif
  333. }
  334. // Uses p.x,p.y p.cx,p.cy p.color
  335. LLDSPEC void gdisp_lld_fill_area(GDisplay* g)
  336. {
  337. uint32_t pos;
  338. uint32_t lineadd;
  339. uint32_t shape;
  340. // Wait until DMA2D is ready
  341. while(DMA2D->CR & DMA2D_CR_START);
  342. #if GDISP_NEED_CONTROL
  343. switch(g->g.Orientation) {
  344. case GDISP_ROTATE_0:
  345. default:
  346. pos = PIXIL_POS(g, g->p.x, g->p.y);
  347. lineadd = g->g.Width - g->p.cx;
  348. shape = (g->p.cx << 16) | (g->p.cy);
  349. break;
  350. case GDISP_ROTATE_90:
  351. pos = PIXIL_POS(g, g->p.y, g->g.Width-g->p.x-g->p.cx);
  352. lineadd = g->g.Height - g->p.cy;
  353. shape = (g->p.cy << 16) | (g->p.cx);
  354. break;
  355. case GDISP_ROTATE_180:
  356. pos = PIXIL_POS(g, g->g.Width-g->p.x-g->p.cx, g->g.Height-g->p.y-g->p.cy);
  357. lineadd = g->g.Width - g->p.cx;
  358. shape = (g->p.cx << 16) | (g->p.cy);
  359. break;
  360. case GDISP_ROTATE_270:
  361. pos = PIXIL_POS(g, g->g.Height-g->p.y-g->p.cy, g->p.x);
  362. lineadd = g->g.Height - g->p.cy;
  363. shape = (g->p.cy << 16) | (g->p.cx);
  364. break;
  365. }
  366. #else
  367. pos = PIXIL_POS(g, g->p.x, g->p.y);
  368. lineadd = g->g.Width - g->p.cx;
  369. shape = (g->p.cx << 16) | (g->p.cy);
  370. #endif
  371. // Start the DMA2D
  372. DMA2D->OMAR = (uint32_t)PIXEL_ADDR(g, pos);
  373. DMA2D->OOR = lineadd;
  374. DMA2D->NLR = shape;
  375. DMA2D->OCOLR = (uint32_t)(gdispColor2Native(g->p.color));
  376. DMA2D->CR = DMA2D_CR_MODE_R2M | DMA2D_CR_START;
  377. }
  378. /* Oops - the DMA2D only supports GDISP_ROTATE_0.
  379. *
  380. * Where the width is 1 we can trick it for other orientations.
  381. * That is worthwhile as a width of 1 is common. For other
  382. * situations we need to fall back to pixel pushing.
  383. *
  384. * Additionally, although DMA2D can translate color formats
  385. * it can only do it for a small range of formats. For any
  386. * other formats we also need to fall back to pixel pushing.
  387. *
  388. * As the code to actually do all that for other than the
  389. * simplest case (orientation == GDISP_ROTATE_0 and
  390. * GDISP_PIXELFORMAT == GDISP_LLD_PIXELFORMAT) is very complex
  391. * we will always pixel push for now. In practice that is OK as
  392. * access to the framebuffer is fast - probably faster than DMA2D.
  393. * It just uses more CPU.
  394. */
  395. #if GDISP_HARDWARE_BITFILLS
  396. // Uses p.x,p.y p.cx,p.cy p.x1,p.y1 (=srcx,srcy) p.x2 (=srccx), p.ptr (=buffer)
  397. LLDSPEC void gdisp_lld_blit_area(GDisplay* g) {
  398. // Wait until DMA2D is ready
  399. while(DMA2D->CR & DMA2D_CR_START);
  400. // Source setup
  401. DMA2D->FGMAR = LTDC_PIXELBYTES * (g->p.y1 * g->p.x2 + g->p.x1) + (uint32_t)g->p.ptr;
  402. DMA2D->FGOR = g->p.x2 - g->p.cx;
  403. // Output setup
  404. DMA2D->OMAR = (uint32_t)PIXEL_ADDR(g, PIXIL_POS(g, g->p.x, g->p.y));
  405. DMA2D->OOR = g->g.Width - g->p.cx;
  406. DMA2D->NLR = (g->p.cx << 16) | (g->p.cy);
  407. // Set MODE to M2M and Start the process
  408. DMA2D->CR = DMA2D_CR_MODE_M2M | DMA2D_CR_START;
  409. }
  410. #endif
  411. #endif /* LTDC_USE_DMA2D */
  412. #endif /* GFX_USE_GDISP */