diff --git a/include/unicore-mx/lcd_tft/lcd_tft.h b/include/unicore-mx/lcd_tft/lcd_tft.h new file mode 100644 index 00000000..f7c9d00c --- /dev/null +++ b/include/unicore-mx/lcd_tft/lcd_tft.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2017 Kuldeep Singh Dhaka + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef UNICOREMX_LCD_TFT_H +#define UNICOREMX_LCD_TFT_H + +#include +#include +#include +#include + +BEGIN_DECLS + +typedef struct lcd_tft lcd_tft; +typedef struct lcd_tft_backend lcd_tft_backend; + +extern const lcd_tft_backend lcd_tft_stm32_ltdc; + +/** + * @note Single transparent color supported + */ +#define LCD_TFT_STM32_LTDC (&lcd_tft_stm32_ltdc) + +struct lcd_tft_config { + struct { + unsigned vsync, hsync; + unsigned vbp, hbp; + unsigned height, width; + unsigned vfp, hfp; + } timing; + + enum { + LCD_TFT_HSYNC_ACTIVE_LOW = 0 << 0, + LCD_TFT_HSYNC_ACTIVE_HIGH = 1 << 0, + LCD_TFT_VSYNC_ACTIVE_LOW = 0 << 1, + LCD_TFT_VSYNC_ACTIVE_HIGH = 1 << 1, + LCD_TFT_DE_ACTIVE_LOW = 0 << 2, + LCD_TFT_DE_ACTIVE_HIGH = 1 << 2, + LCD_TFT_CLK_ACTIVE_LOW = 0 << 3, + LCD_TFT_CLK_ACTIVE_HIGH = 1 << 3, + + /* https://en.wikipedia.org/wiki/Dither */ + LCD_TFT_DITHER_ENABLE = 1 << 4, + + LCD_TFT_HSYNC_ACTIVE_MASK = 1 << 0, + LCD_TFT_VSYNC_ACTIVE_MASK = 1 << 1, + LCD_TFT_DE_ACTIVE_MASK = 1 << 2, + LCD_TFT_CLK_ACTIVE_MASK = 1 << 3, + LCD_TFT_DITHER_MASK = 1 << 4 + } features; + + /* Number of output lines */ + enum { + LCD_TFT_OUTPUT_RGB565, /* 16bit */ + LCD_TFT_OUTPUT_RGB666, /* 18bit */ + LCD_TFT_OUTPUT_RGB888 /* 24bit */ + } output; + + /** Background color - all pixel in layer are transparent (Format: RGB888) */ + uint32_t background; +}; + +enum lcd_tft_pixel_format { + LCD_TFT_ARGB8888, + LCD_TFT_RGB888, + LCD_TFT_RGB565, + LCD_TFT_ARGB1555, + LCD_TFT_ARGB4444, + LCD_TFT_L8, + LCD_TFT_AL44, + LCD_TFT_AL88 +}; + +struct lcd_tft_layer { + struct { + unsigned x, y, width, height; + + /** + * Note: Pointer should be aligned to + * 32bit if 1 pixel contain 4byte data + * 16bit if 1 pixel contain 2byte data + */ + enum lcd_tft_pixel_format format; + const void *data; + } framebuffer; + + /** + * Hardware palette or Color Look-Up Table (CLUT). + * Only valid for format = L8, AL44, AL88 + * Pass @a data = NULL and @a count = 0 to disable. + * @note @a data format is RGB888 + */ + struct { + const uint32_t *data; + size_t count; + } palette; + + /** + * These colors are seen as transparent if found in frame buffer. + * Pass @a data = NULL and @a count = 0 to disable. + * @note @a data format is RGB888 + */ + struct { + const uint32_t *data; + size_t count; + } transparent; +}; + +/** + * Initalize LCD TFT + * @param backend LCD TFT backend + * @param config Configuration + * @return lcd_tft LCD TFT object + */ +lcd_tft *lcd_tft_init(const lcd_tft_backend *backend, + const struct lcd_tft_config *config); + +/** + * Enable the LCD TFT + * @param backend LCD TFT backend + * @param enable Enable + */ +void lcd_tft_enable(lcd_tft *lt, bool enable); + +/** + * Setup a layer for LCD TFT + * @param ldc_tft LCD TFT object + * @param index Layer index (0...N) + * @param layer Layer data + */ +void lcd_tft_layer_set(lcd_tft *lt, unsigned index, + const struct lcd_tft_layer *layer); + +/** + * Enable the LCD TFT layer + * @param lt LCD TFT object + * @param index Layer index (0...N) + * @param enable Enable + */ +void lcd_tft_layer_enable(lcd_tft *lt, unsigned index, + bool enable); + +END_DECLS + +#endif diff --git a/lib/lcd_tft/backend/lcd_tft_stm32_ltdc.c b/lib/lcd_tft/backend/lcd_tft_stm32_ltdc.c new file mode 100644 index 00000000..75f78a02 --- /dev/null +++ b/lib/lcd_tft/backend/lcd_tft_stm32_ltdc.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2017 Kuldeep Singh Dhaka + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "../lcd_tft_private.h" +#include +#include + +static struct lcd_tft *init(const struct lcd_tft_backend *backend, + const struct lcd_tft_config *config); +static void enable(struct lcd_tft *lt, bool enable); +static void layer_set(struct lcd_tft *lt, unsigned index, + const struct lcd_tft_layer *layer); +static void layer_enable(struct lcd_tft *lt, unsigned index, + bool enable); + +const struct lcd_tft_backend lcd_tft_stm32_ltdc = { + .init = init, + .enable = enable, + .layer_set = layer_set, + .layer_enable = layer_enable +}; + +static const struct lcd_tft _lt = { + .backend = &lcd_tft_stm32_ltdc +}; + +static void timing(const struct lcd_tft_config *config) +{ + uint16_t w, h; + w = config->timing.hsync - 1; + h = config->timing.vsync - 1; + LTDC_SSCR = (w << 16) | (h << 0); + + w += config->timing.hbp; + h += config->timing.vbp; + LTDC_BPCR = (w << 16) | (h << 0); + + w += config->timing.width; + h += config->timing.height; + LTDC_AWCR = (w << 16) | (h << 0); + + w += config->timing.hfp; + h += config->timing.vfp; + LTDC_TWCR = (w << 16) | (h << 0); +} + +static void features(const struct lcd_tft_config *config) +{ + uint32_t gcr = 0; + + if (config->features & LCD_TFT_HSYNC_ACTIVE_MASK) { + gcr |= LTDC_GCR_HSPOL_ACTIVE_HIGH; + } + + if (config->features & LCD_TFT_VSYNC_ACTIVE_MASK) { + gcr |= LTDC_GCR_VSPOL_ACTIVE_HIGH; + } + + if (config->features & LCD_TFT_DE_ACTIVE_MASK) { + gcr |= LTDC_GCR_DEPOL_ACTIVE_HIGH; + } + + if (config->features & LCD_TFT_CLK_ACTIVE_MASK) { + gcr |= LTDC_GCR_PCPOL_ACTIVE_HIGH; + } + + if (config->features & LCD_TFT_DITHER_MASK) { + gcr |= LTDC_GCR_DITHER_ENABLE; + } + + LTDC_GCR = gcr; +} + +static struct lcd_tft *init(const struct lcd_tft_backend *backend, + const struct lcd_tft_config *config) +{ + (void) backend; + + rcc_periph_clock_enable(RCC_LTDC); + + features(config); + timing(config); + + LTDC_BCCR = config->background; + LTDC_GCR |= LTDC_GCR_LTDC_ENABLE; + + return (struct lcd_tft *) &_lt; +} + +static void enable(struct lcd_tft *lt, bool enable) +{ + (void) lt; + + if (enable) { + LTDC_GCR |= LTDC_GCR_LTDC_ENABLE; + } else { + LTDC_GCR &= ~LTDC_GCR_LTDC_ENABLE; + } +} + +static void layer_window(unsigned num, const struct lcd_tft_layer *layer) +{ + uint32_t hbp = (LTDC_BPCR >> LTDC_BPCR_AHBP_SHIFT) & LTDC_BPCR_AHBP_MASK; + uint32_t vbp = (LTDC_BPCR >> LTDC_BPCR_AVBP_SHIFT) & LTDC_BPCR_AVBP_MASK; + uint16_t start, stop; + + start = hbp + layer->framebuffer.x + 1; + stop = hbp + layer->framebuffer.x + layer->framebuffer.width; + LTDC_LxWHPCR(num) = (stop << 16) | (start << 0); + + start = vbp + layer->framebuffer.y + 1; + stop = vbp + layer->framebuffer.y + layer->framebuffer.height; + LTDC_LxWVPCR(num) = (stop << 16) | (start << 0); +} + +static const uint32_t pixel_format_conv[] = { + [LCD_TFT_ARGB8888] = LTDC_LxPFCR_ARGB8888, + [LCD_TFT_RGB888] = LTDC_LxPFCR_RGB888, + [LCD_TFT_RGB565] = LTDC_LxPFCR_RGB565, + [LCD_TFT_ARGB1555] = LTDC_LxPFCR_ARGB1555, + [LCD_TFT_ARGB4444] = LTDC_LxPFCR_ARGB4444, + [LCD_TFT_L8] = LTDC_LxPFCR_L8, + [LCD_TFT_AL44] = LTDC_LxPFCR_AL44, + [LCD_TFT_AL88] = LTDC_LxPFCR_AL88 +}; + +static const unsigned pixel_format_bytes[] = { + [LCD_TFT_ARGB8888] = 4, + [LCD_TFT_RGB888] = 3, + [LCD_TFT_RGB565] = 2, + [LCD_TFT_ARGB1555] = 2, + [LCD_TFT_ARGB4444] = 2, + [LCD_TFT_L8] = 1, + [LCD_TFT_AL44] = 1, + [LCD_TFT_AL88] = 2 +}; + +static void layer_data(unsigned num, const struct lcd_tft_layer *layer) +{ + LTDC_LxPFCR(num) = pixel_format_conv[layer->framebuffer.format]; + LTDC_LxCFBAR(num) = (uint32_t) layer->framebuffer.data; + + unsigned bpp = pixel_format_bytes[layer->framebuffer.format]; + unsigned bytes = bpp * layer->framebuffer.width; + LTDC_LxCFBLR(num) = (bytes << 16) | ((bytes + 3) << 0); + LTDC_LxCFBLNR(num) = layer->framebuffer.height; +} + +static void layer_clut(unsigned num, const struct lcd_tft_layer *layer) +{ + if (!layer->palette.count) { + /* Not provided */ + LTDC_LxCR(num) &= ~LTDC_LxCR_CLUT_ENABLE; + return; + } + + LTDC_LxCR(num) |= LTDC_LxCR_CLUT_ENABLE; + + for (uint8_t i = 0; i < layer->palette.count; i++) { + LTDC_LxCLUTWR(num) = (i << 24) | layer->palette.data[i]; + } +} + +static void layer_key_color(unsigned num, const struct lcd_tft_layer *layer) +{ + if (!layer->transparent.count) { + /* Not provided */ + LTDC_LxCR(num) &= ~LTDC_LxCR_COLKEY_ENABLE; + return; + } + + /* Warning: Only single transparent color supported */ + LTDC_LxCR(num) |= LTDC_LxCR_COLKEY_ENABLE; + LTDC_LxCKCR(num) = layer->transparent.data[0]; +} + +static void layer_set(struct lcd_tft *lt, unsigned index, + const struct lcd_tft_layer *layer) +{ + (void) lt; + + unsigned num = index + 1; + + layer_window(num, layer); + layer_data(num, layer); + layer_clut(num, layer); + layer_key_color(num, layer); + LTDC_LxCR(num) |= LTDC_LxCR_LAYER_ENABLE; + + /* Reload immediate */ + LTDC_SRCR = LTDC_SRCR_IMR; +} + +static void layer_enable(struct lcd_tft *lt, unsigned index, bool enable) +{ + (void) lt; + + unsigned num = index + 1; + + if (enable) { + LTDC_LxCR(num) |= LTDC_LxCR_LAYER_ENABLE; + } else { + LTDC_LxCR(num) &= ~LTDC_LxCR_LAYER_ENABLE; + } +} diff --git a/lib/lcd_tft/lcd_tft.c b/lib/lcd_tft/lcd_tft.c new file mode 100644 index 00000000..8973b36b --- /dev/null +++ b/lib/lcd_tft/lcd_tft.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 Kuldeep Singh Dhaka + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "lcd_tft_private.h" + +struct lcd_tft *lcd_tft_init(const struct lcd_tft_backend *backend, + const struct lcd_tft_config *config) +{ + return backend->init(backend, config); +} + +void lcd_tft_enable(struct lcd_tft *lt, bool enable) +{ + if (lt->backend->enable) { + lt->backend->enable(lt, enable); + } +} + +void lcd_tft_layer_set(struct lcd_tft *lt, unsigned index, + const struct lcd_tft_layer *layer) +{ + if (lt->backend->layer_set) { + lt->backend->layer_set(lt, index, layer); + } +} + +void lcd_tft_layer_enable(struct lcd_tft *lt, unsigned index, + bool enable) +{ + if (lt->backend->layer_enable) { + lt->backend->layer_enable(lt, index, enable); + } +} diff --git a/lib/lcd_tft/lcd_tft_private.h b/lib/lcd_tft/lcd_tft_private.h new file mode 100644 index 00000000..2631cb60 --- /dev/null +++ b/lib/lcd_tft/lcd_tft_private.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Kuldeep Singh Dhaka + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef UNICOREMX_LCD_TFT_PRIVATE_H +#define UNICOREMX_LCD_TFT_PRIVATE_H + +#include +#include +#include + +BEGIN_DECLS + +struct lcd_tft { + const struct lcd_tft_backend *backend; +}; + +struct lcd_tft_backend { + struct lcd_tft *(*init)(const struct lcd_tft_backend *backend, + const struct lcd_tft_config *config); + void (*enable)(struct lcd_tft *lt, bool enable); + void (*layer_set)(struct lcd_tft *lt, unsigned index, + const struct lcd_tft_layer *layer); + void (*layer_enable)(struct lcd_tft *lt, unsigned index, bool enable); +}; + +END_DECLS + +#endif diff --git a/lib/stm32/f4/Makefile b/lib/stm32/f4/Makefile index 0a1955db..c303a0ed 100644 --- a/lib/stm32/f4/Makefile +++ b/lib/stm32/f4/Makefile @@ -61,9 +61,12 @@ OBJS += usbh_dwc_otg.o usbh_stm32_otg_fs.o OBJS += usbh_hid.o OBJS += usbh_ctrlreq.o +OBJS += lcd_tft.o lcd_tft_stm32_ltdc.o + VPATH += ../:../../cm3:../common VPATH += ../../usbd:../../usbd/class:../../usbd/backend VPATH += ../../ethernet VPATH += ../../usbh:../../usbh/backend:../../usbh/helper:../../usbh/class +VPATH += ../../lcd_tft:../../lcd_tft/backend include ../../Makefile.include diff --git a/lib/stm32/f7/Makefile b/lib/stm32/f7/Makefile index 850d40ad..d37becb1 100644 --- a/lib/stm32/f7/Makefile +++ b/lib/stm32/f7/Makefile @@ -64,10 +64,13 @@ OBJS += usbh_dwc_otg.o usbh_stm32_otg_fs.o OBJS += usbh_hid.o OBJS += usbh_ctrlreq.o +OBJS += lcd_tft.o lcd_tft_stm32_ltdc.o + VPATH += ../:../../cm3:../common VPATH += ../../usbd:../../usbd/class:../../usbd/backend VPATH += ../../ethernet VPATH += ../../usbh:../../usbh/backend:../../usbh/helper:../../usbh/class +VPATH += ../../lcd_tft:../../lcd_tft/backend ############################################################################### # Checking CPU support in the toolchain