You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

424 lines
14 KiB

/*
* EEZ Modular Firmware
* Copyright (C) 2021-present, Envox d.o.o.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <math.h>
#include <memory.h>
#include <stdio.h>
#include <string>
#include <dma2d.h>
#include <i2c.h>
#include <ltdc.h>
#ifdef EEZ_PLATFORM_STM32F469I_DISCO
#include "stm32469i_discovery_lcd.h"
extern "C" LTDC_HandleTypeDef hltdc_eval;
extern "C" DMA2D_HandleTypeDef hdma2d_eval;
#define hltdc hltdc_eval
#define hdma2d hdma2d_eval
#endif
#include <eez/core/util.h>
#include <eez/core/memory.h>
#include <eez/core/os.h>
#include <eez/gui/gui.h>
#include <eez/gui/thread.h>
#include <eez/gui/display.h>
#include <eez/gui/display-private.h>
void HAL_LTDC_LineEventCallback(LTDC_HandleTypeDef *phltdc) {
using namespace eez::gui;
using namespace eez::gui::display;
LTDC_LAYER(phltdc, 0)->CFBAR = (uint32_t)g_syncedBuffer;
__HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(phltdc);
sendMessageToGuiThread(GUI_QUEUE_MESSAGE_TYPE_DISPLAY_VSYNC, 0, 0);
}
namespace eez {
namespace gui {
namespace display {
#define DMA2D_WAIT if (g_waitDMA) { while (HAL_DMA2D_PollForTransfer(&hdma2d, 1000) != HAL_OK); g_waitDMA = false; }
static bool g_waitDMA;
void initDriver() {
__HAL_RCC_DMA2D_CLK_ENABLE();
}
void syncBuffer() {
DMA2D_WAIT;
static const uint32_t LINE_INTERRUPT_POSITION = (LTDC->AWCR & 0x7FF) - 1;
HAL_LTDC_ProgramLineEvent(&hltdc, LINE_INTERRUPT_POSITION);
}
static void bitBltRGB888(uint16_t *src, uint8_t *dst, int x, int y, int width, int height);
void copySyncedBufferToScreenshotBuffer() {
bitBltRGB888(g_syncedBuffer, SCREENSHOOT_BUFFER_START_ADDRESS, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
DMA2D_WAIT;
}
////////////////////////////////////////////////////////////////////////////////
inline uint32_t vramOffset(uint16_t *vram, int x, int y) {
return (uint32_t)(vram + y * DISPLAY_WIDTH + x);
}
inline uint32_t vramOffsetRGB888(uint8_t *vram, int x, int y) {
return (uint32_t)(vram + (y * DISPLAY_WIDTH + x) * 3);
}
inline uint32_t vramOffset(uint32_t *vram, int x, int y) {
return (uint32_t)(vram + y * DISPLAY_WIDTH + x);
}
void fillRect(uint16_t *dst, int x, int y, int width, int height, uint16_t color) {
if (g_opacity == 255) {
hdma2d.Init.Mode = DMA2D_R2M;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width;
uint32_t colorBGRA;
uint8_t *pcolorBGRA = (uint8_t *)&colorBGRA;
pcolorBGRA[0] = COLOR_TO_B(color);
pcolorBGRA[1] = COLOR_TO_G(color);
pcolorBGRA[2] = COLOR_TO_R(color);
pcolorBGRA[3] = 255;
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_Start(&hdma2d, colorBGRA, vramOffset(dst, x, y), width, height);
g_waitDMA = true;
} else {
// fill aux. buffer with BGRA color
uint32_t colorBGRA;
uint8_t *pcolorBGRA = (uint8_t *)&colorBGRA;
pcolorBGRA[0] = COLOR_TO_B(color);
pcolorBGRA[1] = COLOR_TO_G(color);
pcolorBGRA[2] = COLOR_TO_R(color);
pcolorBGRA[3] = g_opacity;
// blend aux. buffer with dst buffer
hdma2d.Init.Mode = DMA2D_M2M_BLEND;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[0].InputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[0].InputAlpha = 0;
hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_A8;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = colorBGRA;
auto dstOffset = vramOffset(dst, x, y);
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
HAL_DMA2D_BlendingStart(&hdma2d, dstOffset, dstOffset, dstOffset, width, height);
g_waitDMA = true;
}
}
void fillRect(void *dst, int x1, int y1, int x2, int y2) {
fillRect((uint16_t *)dst, x1, y1, x2 - x1 + 1, y2 - y1 + 1, g_fc);
setDirty();
}
void bitBlt(void *src, int srcBpp, uint32_t srcLineOffset, uint16_t *dst, int x, int y, int width, int height) {
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width;
if (srcBpp == 32) {
hdma2d.Init.Mode = DMA2D_M2M_BLEND;
hdma2d.LayerCfg[0].InputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[0].InputAlpha = 0;
hdma2d.LayerCfg[1].InputOffset = srcLineOffset;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
} else if (srcBpp == 24) {
hdma2d.Init.Mode = DMA2D_M2M_PFC;
#ifndef EEZ_PLATFORM_STM32F469I_DISCO
hdma2d.Init.RedBlueSwap = DMA2D_RB_SWAP;
#endif
hdma2d.LayerCfg[1].InputOffset = srcLineOffset;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB888;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
} else {
hdma2d.Init.Mode = DMA2D_M2M;
hdma2d.LayerCfg[1].InputOffset = srcLineOffset;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
}
auto dstOffset = vramOffset(dst, x, y);
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
if (srcBpp == 32) {
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
HAL_DMA2D_BlendingStart(&hdma2d, (uint32_t)src, dstOffset, dstOffset, width, height);
} else {
HAL_DMA2D_Start(&hdma2d, (uint32_t)src, dstOffset, width, height);
}
g_waitDMA = true;
if (srcBpp == 24) {
#ifndef EEZ_PLATFORM_STM32F469I_DISCO
hdma2d.Init.RedBlueSwap = DMA2D_RB_REGULAR;
#endif
}
}
void bitBlt(void *src, int x1, int y1, int x2, int y2) {
bitBlt(src, g_renderBuffer, x1, y1, x2, y2);
setDirty();
}
void bitBlt(uint16_t *src, uint16_t *dst, int x, int y, int width, int height) {
hdma2d.Init.Mode = DMA2D_M2M;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_Start(&hdma2d, vramOffset(src, x, y), vramOffset(dst, x, y), width, height);
g_waitDMA = true;
}
static void bitBltRGB888(uint16_t *src, uint8_t *dst, int x, int y, int width, int height) {
hdma2d.Init.Mode = DMA2D_M2M_PFC;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB888;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width;
#ifndef EEZ_PLATFORM_STM32F469I_DISCO
hdma2d.Init.RedBlueSwap = DMA2D_RB_SWAP;
#endif
hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_Start(&hdma2d, vramOffset(src, x, y), vramOffsetRGB888(dst, x, y), width, height);
g_waitDMA = true;
#ifndef EEZ_PLATFORM_STM32F469I_DISCO
hdma2d.Init.RedBlueSwap = DMA2D_RB_REGULAR;
#endif
}
void bitBlt(void *src, void *dst, int x1, int y1, int x2, int y2) {
bitBlt((uint16_t *)src, (uint16_t *)dst, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
setDirty();
}
void bitBlt(uint16_t *src, uint16_t *dst, int x, int y, int width, int height, int dstx, int dsty) {
hdma2d.Init.Mode = DMA2D_M2M;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_Start(&hdma2d, vramOffset(src, x, y), vramOffset(dst, dstx, dsty), width, height);
g_waitDMA = true;
}
void bitBlt(void *src, void *dst, int sx, int sy, int sw, int sh, int dx, int dy, uint8_t opacity) {
if (dst == nullptr) {
dst = g_renderBuffer;
}
auto srcOffset = vramOffset((uint16_t *)src, sx, sy);
auto dstOffset = vramOffset((uint16_t *)dst, dx, dy);
if (opacity == 255) {
hdma2d.Init.Mode = DMA2D_M2M;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - sw;
hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - sw;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_Start(&hdma2d, srcOffset, dstOffset, sw, sh);
g_waitDMA = true;
} else {
hdma2d.Init.Mode = DMA2D_M2M_BLEND;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = DISPLAY_WIDTH - sw;
hdma2d.LayerCfg[0].InputOffset = DISPLAY_WIDTH - sw;
hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[0].AlphaMode = DMA2D_COMBINE_ALPHA;
hdma2d.LayerCfg[0].InputAlpha = 0xFF;
hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - sw;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_COMBINE_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = opacity;
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
HAL_DMA2D_BlendingStart(&hdma2d, srcOffset, dstOffset, dstOffset, sw, sh);
g_waitDMA = true;
}
}
////////////////////////////////////////////////////////////////////////////////
void startPixelsDraw() {
DMA2D_WAIT;
}
void drawPixel(int x, int y) {
*(g_renderBuffer + y * DISPLAY_WIDTH + x) = g_fc;
}
void drawPixel(int x, int y, uint8_t opacity) {
auto dest = g_renderBuffer + y * DISPLAY_WIDTH + x;
*dest = color32to16(
blendColor(
color16to32(g_fc, opacity),
color16to32(*dest, 255 - opacity)
)
);
}
void endPixelsDraw() {
setDirty();
}
void fillRect(int x1, int y1, int x2, int y2) {
fillRect(g_renderBuffer, x1, y1, x2 - x1 + 1, y2 - y1 + 1, g_fc);
setDirty();
}
void bitBlt(int x1, int y1, int x2, int y2, int dstx, int dsty) {
bitBlt(g_renderBuffer, g_renderBuffer, x1, y1, x2-x1+1, y2-y1+1, dstx, dsty);
setDirty();
}
void drawBitmap(Image *image, int x, int y) {
bitBlt(image->pixels, image->bpp, image->lineOffset, g_renderBuffer, x, y, image->width, image->height);
setDirty();
}
void drawStrInit() {
auto color = g_fc;
// initialize everything except lineOffset
hdma2d.Init.Mode = DMA2D_M2M_BLEND;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = 0;
// background
hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565;
hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[0].InputAlpha = 0;
hdma2d.LayerCfg[0].InputOffset = 0;
// foreground
uint32_t colorBGRA;
uint8_t *pcolorBGRA = (uint8_t *)&colorBGRA;
pcolorBGRA[0] = COLOR_TO_B(color);
pcolorBGRA[1] = COLOR_TO_G(color);
pcolorBGRA[2] = COLOR_TO_R(color);
pcolorBGRA[3] = 255;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_A8;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = colorBGRA;
hdma2d.LayerCfg[1].InputOffset = 0;
DMA2D_WAIT;
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
g_waitDMA = true;
}
void drawGlyph(const uint8_t *src, uint32_t srcLineOffset, int x, int y, int width, int height) {
uint32_t lineOffset = DISPLAY_WIDTH - width;
uint32_t dst = vramOffset(g_renderBuffer, x, y);
DMA2D_WAIT;
// initialize lineOffset
WRITE_REG(hdma2d.Instance->OOR, lineOffset);
WRITE_REG(hdma2d.Instance->BGOR, lineOffset);
WRITE_REG(hdma2d.Instance->FGOR, srcLineOffset);
HAL_DMA2D_BlendingStart(&hdma2d, (uint32_t)src, dst, dst, width, height);
g_waitDMA = true;
}
} // namespace display
} // namespace gui
} // namespace eez