| /* |
| * Copyright (c) 2018 PHYTEC Messtechnik GmbH |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <string.h> |
| #include <zephyr/display/cfb.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/sys/minmax.h> |
| |
| #define LOG_LEVEL CONFIG_CFB_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(cfb); |
| |
| STRUCT_SECTION_START_EXTERN(cfb_font); |
| STRUCT_SECTION_END_EXTERN(cfb_font); |
| |
| #define LSB_BIT_MASK(x) BIT_MASK(x) |
| #define MSB_BIT_MASK(x) (BIT_MASK(x) << (8 - x)) |
| |
| static inline uint8_t byte_reverse(uint8_t b) |
| { |
| b = (b & 0xf0) >> 4 | (b & 0x0f) << 4; |
| b = (b & 0xcc) >> 2 | (b & 0x33) << 2; |
| b = (b & 0xaa) >> 1 | (b & 0x55) << 1; |
| return b; |
| } |
| |
| struct char_framebuffer { |
| /** Pointer to a buffer in RAM */ |
| uint8_t *buf; |
| |
| /** Size of the framebuffer */ |
| uint32_t size; |
| |
| /** Pointer to the font entry array */ |
| const struct cfb_font *fonts; |
| |
| /** Display pixel format */ |
| enum display_pixel_format pixel_format; |
| |
| /** Display screen info */ |
| enum display_screen_info screen_info; |
| |
| /** Resolution of a framebuffer in pixels in X direction */ |
| uint16_t x_res; |
| |
| /** Resolution of a framebuffer in pixels in Y direction */ |
| uint16_t y_res; |
| |
| /** Number of pixels per tile, typically 8 */ |
| uint8_t ppt; |
| |
| /** Number of available fonts */ |
| uint8_t numof_fonts; |
| |
| /** Current font index */ |
| uint8_t font_idx; |
| |
| /** Font kerning */ |
| int8_t kerning; |
| |
| /** Inverted */ |
| bool inverted; |
| }; |
| |
| static struct char_framebuffer char_fb; |
| |
| static inline const uint8_t *get_glyph_ptr(const struct cfb_font *fptr, uint8_t c) |
| { |
| if (c < fptr->first_char || c > fptr->last_char) { |
| return NULL; |
| } |
| |
| const size_t glyph_size = (fptr->caps & CFB_FONT_MONO_HPACKED) |
| ? fptr->height * DIV_ROUND_UP(fptr->width, 8U) |
| : fptr->width * DIV_ROUND_UP(fptr->height, 8U); |
| |
| return (const uint8_t *)fptr->data + (c - fptr->first_char) * glyph_size; |
| } |
| |
| static inline bool is_tofu_border_pixel(const struct cfb_font *fptr, uint8_t px, uint8_t py) |
| { |
| if (px >= fptr->width || py >= fptr->height) { |
| return false; |
| } |
| |
| const uint8_t min_dim = MIN(fptr->width, fptr->height); |
| const uint8_t max_margin = (min_dim > 2U) ? (min_dim - 2U) / 2U : 0U; |
| const uint8_t margin = MIN(min_dim / 10U, max_margin); |
| const uint8_t left = margin; |
| const uint8_t top = margin; |
| const uint8_t right = fptr->width - margin; |
| const uint8_t bottom = fptr->height - margin; |
| const uint8_t max_stroke_x = (right > (left + 1U)) ? (right - left - 1U) / 2U : 1U; |
| const uint8_t max_stroke_y = (bottom > (top + 1U)) ? (bottom - top - 1U) / 2U : 1U; |
| const uint8_t stroke = min3(max(min(right - left, bottom - top) / 10U, 1U), |
| max_stroke_x, max_stroke_y); |
| const bool inside_box = (px >= left) && (px < right) && (py >= top) && (py < bottom); |
| const bool on_left_or_right_edge = (px < (left + stroke)) || (px >= (right - stroke)); |
| const bool on_top_or_bottom_edge = (py < (top + stroke)) || (py >= (bottom - stroke)); |
| |
| return inside_box && (on_left_or_right_edge || on_top_or_bottom_edge); |
| } |
| |
| static inline uint8_t get_tofu_glyph_byte(const struct cfb_font *fptr, uint8_t x, uint8_t y, |
| bool vtiled) |
| { |
| const bool font_is_msbfirst = ((fptr->caps & CFB_FONT_MSB_FIRST) != 0); |
| uint8_t byte = 0; |
| |
| if (fptr->caps & CFB_FONT_MONO_VPACKED) { |
| const uint16_t tile = vtiled ? y : (y / 8U); |
| const uint16_t base_y = tile * 8U; |
| |
| for (uint8_t bit = 0U; bit < 8U; bit++) { |
| const uint16_t py = base_y + bit; |
| |
| if (py >= fptr->height) { |
| break; |
| } |
| |
| if (is_tofu_border_pixel(fptr, x, (uint8_t)py)) { |
| byte |= font_is_msbfirst ? BIT(7U - bit) : BIT(bit); |
| } |
| } |
| } else if (fptr->caps & CFB_FONT_MONO_HPACKED) { |
| const uint16_t base_x = (x / 8U) * 8U; |
| |
| for (uint8_t bit = 0U; bit < 8U; bit++) { |
| const uint16_t px = base_x + bit; |
| |
| if (px >= fptr->width) { |
| break; |
| } |
| |
| if (is_tofu_border_pixel(fptr, (uint8_t)px, y)) { |
| byte |= font_is_msbfirst ? BIT(7U - bit) : BIT(bit); |
| } |
| } |
| } else { |
| LOG_WRN("Unknown font type"); |
| } |
| |
| return byte; |
| } |
| |
| static inline uint8_t get_glyph_byte(const uint8_t *glyph_ptr, const struct cfb_font *fptr, |
| uint8_t x, uint8_t y, bool vtiled) |
| { |
| if (!glyph_ptr) { |
| return get_tofu_glyph_byte(fptr, x, y, vtiled); |
| } |
| |
| if (fptr->caps & CFB_FONT_MONO_VPACKED) { |
| const uint8_t bytes_per_col = DIV_ROUND_UP(fptr->height, 8U); |
| const uint8_t byte_idx = vtiled ? y : (y / 8U); |
| |
| if (byte_idx >= bytes_per_col) { |
| return 0; |
| } |
| |
| return glyph_ptr[x * bytes_per_col + byte_idx]; |
| } else if (fptr->caps & CFB_FONT_MONO_HPACKED) { |
| return glyph_ptr[y * DIV_ROUND_UP(fptr->width, 8U) + (x / 8U)]; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Draw the monochrome character in the monochrome tiled framebuffer, |
| * a byte is interpreted as 8 pixels ordered vertically among each other. |
| */ |
| static uint8_t draw_char_vtmono_vpacked(const struct char_framebuffer *fb, |
| const struct cfb_font *fptr, uint8_t c, uint16_t x, |
| uint16_t y, bool draw_bg) |
| { |
| const uint8_t *glyph_ptr = get_glyph_ptr(fptr, c); |
| const bool font_is_msbfirst = ((fptr->caps & CFB_FONT_MSB_FIRST) != 0); |
| const bool need_reverse = |
| (((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0) != font_is_msbfirst); |
| |
| for (size_t g_x = 0; g_x < fptr->width; g_x++) { |
| const int16_t fb_x = x + g_x; |
| |
| for (size_t g_y = 0; g_y < fptr->height;) { |
| /* |
| * Process glyph rendering in the y direction |
| * by separating per 8-line boundaries. |
| */ |
| |
| const int16_t fb_y = y + g_y; |
| const size_t fb_index = (fb_y / 8U) * fb->x_res + fb_x; |
| const size_t offset = ((y % 8) + 8) % 8; |
| const uint8_t bottom_lines = ((offset + fptr->height) % 8); |
| uint8_t bg_mask; |
| uint8_t byte; |
| uint8_t next_byte; |
| |
| if (fb_x < 0 || fb->x_res <= fb_x || fb_y < 0 || fb->y_res <= fb_y) { |
| g_y++; |
| continue; |
| } |
| |
| if (offset == 0 || g_y == 0) { |
| /* |
| * The case of drawing the first line of the glyphs or |
| * starting to draw with a tile-aligned position case. |
| * In this case, no character is above it. |
| * So, we process assume that nothing is drawn above. |
| */ |
| byte = 0; |
| next_byte = get_glyph_byte(glyph_ptr, fptr, g_x, g_y / 8, true); |
| } else { |
| byte = get_glyph_byte(glyph_ptr, fptr, g_x, g_y / 8, true); |
| next_byte = |
| get_glyph_byte(glyph_ptr, fptr, g_x, (g_y + 8) / 8, true); |
| } |
| |
| if (font_is_msbfirst) { |
| /* |
| * Extract the necessary 8 bits from the combined 2 tiles of glyphs. |
| */ |
| byte = ((byte << 8) | next_byte) >> (offset); |
| |
| if (g_y == 0) { |
| /* |
| * Create a mask that does not draw offset white space. |
| */ |
| bg_mask = BIT_MASK(8 - offset); |
| } else { |
| /* |
| * The drawing of the second line onwards |
| * is aligned with the tile, so it draws all the bits. |
| */ |
| bg_mask = 0xFF; |
| } |
| } else { |
| byte = ((next_byte << 8) | byte) >> (8 - offset); |
| if (g_y == 0) { |
| bg_mask = BIT_MASK(8 - offset) << offset; |
| } else { |
| bg_mask = 0xFF; |
| } |
| } |
| |
| /* |
| * Clip the bottom margin to protect existing draw contents. |
| */ |
| if (((fptr->height - g_y) < 8) && (bottom_lines != 0)) { |
| const uint8_t clip = font_is_msbfirst ? MSB_BIT_MASK(bottom_lines) |
| : LSB_BIT_MASK(bottom_lines); |
| |
| bg_mask &= clip; |
| byte &= clip; |
| } |
| |
| if (draw_bg) { |
| if (need_reverse) { |
| bg_mask = byte_reverse(bg_mask); |
| } |
| fb->buf[fb_index] &= ~bg_mask; |
| } |
| |
| if (need_reverse) { |
| byte = byte_reverse(byte); |
| } |
| fb->buf[fb_index] |= byte; |
| |
| if (g_y == 0) { |
| g_y += (8 - offset); |
| } else if ((fptr->height - g_y) >= 8) { |
| g_y += 8; |
| } else { |
| g_y += bottom_lines; |
| } |
| } |
| } |
| |
| return fptr->width; |
| } |
| |
| static uint8_t draw_char_vtmono_hpacked(const struct char_framebuffer *fb, |
| const struct cfb_font *fptr, uint8_t c, uint16_t x, |
| uint16_t y, bool draw_bg) |
| { |
| const uint8_t *glyph_ptr = get_glyph_ptr(fptr, c); |
| const bool font_is_msbfirst = ((fptr->caps & CFB_FONT_MSB_FIRST) != 0); |
| const bool disp_is_msbfirst = (fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0; |
| |
| for (size_t g_y = 0; g_y < fptr->height; g_y++) { |
| const int16_t fb_y = y + g_y; |
| |
| if (fb_y < 0 || fb->y_res <= fb_y) { |
| continue; |
| } |
| |
| const uint8_t bg_mask = disp_is_msbfirst ? BIT(7U - (fb_y % 8U)) : BIT(fb_y % 8U); |
| const size_t fb_row_base = (fb_y / 8U) * fb->x_res; |
| |
| for (size_t g_x = 0; g_x < fptr->width; g_x += 8U) { |
| const uint8_t gbyte = get_glyph_byte(glyph_ptr, fptr, g_x, g_y, true); |
| const uint8_t byte = font_is_msbfirst ? byte_reverse(gbyte) : gbyte; |
| const size_t pixels_in_byte = |
| (fptr->width - g_x) > 8U ? 8U : (fptr->width - g_x); |
| |
| for (size_t g_bit = 0; g_bit < pixels_in_byte; g_bit++) { |
| const int16_t fb_x = x + g_x + g_bit; |
| |
| if (fb_x < 0 || fb->x_res <= fb_x) { |
| continue; |
| } |
| |
| const size_t fb_index = fb_row_base + fb_x; |
| |
| if (draw_bg) { |
| fb->buf[fb_index] &= ~bg_mask; |
| } |
| |
| if (BIT(g_bit) & byte) { |
| fb->buf[fb_index] |= bg_mask; |
| } |
| } |
| } |
| } |
| |
| return fptr->width; |
| } |
| |
| static inline uint8_t draw_char_vtmono(const struct char_framebuffer *fb, uint8_t c, uint16_t x, |
| uint16_t y, bool draw_bg) |
| { |
| const struct cfb_font *fptr = &(fb->fonts[fb->font_idx]); |
| |
| if (fptr->caps & CFB_FONT_MONO_HPACKED) { |
| return draw_char_vtmono_hpacked(fb, fptr, c, x, y, draw_bg); |
| } else { |
| return draw_char_vtmono_vpacked(fb, fptr, c, x, y, draw_bg); |
| } |
| } |
| |
| /* |
| * Draw the monochrome character in the monochrome tiled framebuffer, |
| * a byte is interpreted as 8 pixels ordered horizontally among each other. |
| */ |
| static uint8_t draw_char_htmono(const struct char_framebuffer *fb, uint8_t c, uint16_t x, |
| uint16_t y, bool draw_bg) |
| { |
| const struct cfb_font *fptr = &(fb->fonts[fb->font_idx]); |
| const uint8_t *glyph_ptr = get_glyph_ptr(fptr, c); |
| const bool font_is_msbfirst = (fptr->caps & CFB_FONT_MSB_FIRST) != 0; |
| const bool display_is_msbfirst = (fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0; |
| |
| for (size_t g_y = 0; g_y < fptr->height; g_y++) { |
| const int16_t fb_y = y + g_y; |
| |
| for (size_t g_x = 0; g_x < fptr->width; g_x++) { |
| const int16_t fb_x = x + g_x; |
| const size_t fb_pixel_index = fb_y * fb->x_res + fb_x; |
| const size_t fb_byte_index = fb_pixel_index / 8; |
| uint8_t byte; |
| uint8_t pixel_value; |
| |
| if (fb_x < 0 || fb->x_res <= fb_x || fb_y < 0 || fb->y_res <= fb_y) { |
| continue; |
| } |
| |
| byte = get_glyph_byte(glyph_ptr, fptr, g_x, g_y, false); |
| if (font_is_msbfirst) { |
| byte = byte_reverse(byte); |
| } |
| if (fptr->caps & CFB_FONT_MONO_HPACKED) { |
| pixel_value = byte & BIT(g_x % 8); |
| } else { |
| pixel_value = byte & BIT(g_y % 8); |
| } |
| |
| if (pixel_value) { |
| if (display_is_msbfirst) { |
| fb->buf[fb_byte_index] |= BIT(7 - (fb_x % 8)); |
| } else { |
| fb->buf[fb_byte_index] |= BIT(fb_x % 8); |
| } |
| } |
| } |
| } |
| |
| return fptr->width; |
| } |
| |
| static inline void draw_point(struct char_framebuffer *fb, int16_t x, int16_t y) |
| { |
| const bool need_reverse = ((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0); |
| size_t index; |
| uint8_t m; |
| |
| if ((fb->screen_info & SCREEN_INFO_MONO_VTILED) != 0) { |
| index = (x + (y / 8) * fb->x_res); |
| m = BIT(y % 8); |
| } else { |
| index = ((x / 8) + y * (fb->x_res / 8)); |
| m = BIT(x % 8); |
| } |
| |
| if (x < 0 || x >= fb->x_res) { |
| return; |
| } |
| |
| if (y < 0 || y >= fb->y_res) { |
| return; |
| } |
| |
| if (need_reverse) { |
| m = byte_reverse(m); |
| } |
| |
| fb->buf[index] |= m; |
| } |
| |
| static void draw_line(struct char_framebuffer *fb, int16_t x0, int16_t y0, int16_t x1, int16_t y1) |
| { |
| int16_t sx = (x0 < x1) ? 1 : -1; |
| int16_t sy = (y0 < y1) ? 1 : -1; |
| int16_t dx = (sx > 0) ? (x1 - x0) : (x0 - x1); |
| int16_t dy = (sy > 0) ? (y0 - y1) : (y1 - y0); |
| int16_t err = dx + dy; |
| int16_t e2; |
| |
| while (true) { |
| draw_point(fb, x0, y0); |
| |
| if (x0 == x1 && y0 == y1) { |
| break; |
| } |
| |
| e2 = 2 * err; |
| |
| if (e2 >= dy) { |
| err += dy; |
| x0 += sx; |
| } |
| |
| if (e2 <= dx) { |
| err += dx; |
| y0 += sy; |
| } |
| } |
| } |
| |
| static int draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y, |
| bool wrap) |
| { |
| const struct char_framebuffer *fb = &char_fb; |
| const struct cfb_font *fptr; |
| size_t len; |
| |
| if (!fb->fonts || !fb->buf) { |
| return -ENODEV; |
| } |
| |
| fptr = &(fb->fonts[fb->font_idx]); |
| len = strlen(str); |
| |
| for (size_t i = 0; i < len; i++) { |
| if ((x + fptr->width > fb->x_res) && wrap) { |
| x = 0U; |
| y += fptr->height; |
| } |
| if (fb->screen_info & SCREEN_INFO_MONO_VTILED) { |
| x += fb->kerning + draw_char_vtmono(fb, str[i], x, y, wrap); |
| } else { |
| x += fb->kerning + draw_char_htmono(fb, str[i], x, y, wrap); |
| } |
| } |
| return 0; |
| } |
| |
| int cfb_draw_point(const struct device *dev, const struct cfb_position *pos) |
| { |
| struct char_framebuffer *fb = &char_fb; |
| |
| draw_point(fb, pos->x, pos->y); |
| |
| return 0; |
| } |
| |
| int cfb_draw_line(const struct device *dev, const struct cfb_position *start, |
| const struct cfb_position *end) |
| { |
| struct char_framebuffer *fb = &char_fb; |
| |
| draw_line(fb, start->x, start->y, end->x, end->y); |
| |
| return 0; |
| } |
| |
| int cfb_draw_rect(const struct device *dev, const struct cfb_position *start, |
| const struct cfb_position *end) |
| { |
| struct char_framebuffer *fb = &char_fb; |
| |
| draw_line(fb, start->x, start->y, end->x, start->y); |
| draw_line(fb, end->x, start->y, end->x, end->y); |
| draw_line(fb, end->x, end->y, start->x, end->y); |
| draw_line(fb, start->x, end->y, start->x, start->y); |
| |
| return 0; |
| } |
| |
| int cfb_draw_circle(const struct device *dev, const struct cfb_position *center, uint16_t radius) |
| { |
| struct char_framebuffer *fb = &char_fb; |
| uint16_t x = 0; |
| int16_t y = -radius; |
| int16_t p = -radius; |
| |
| /* Using the Midpoint Circle Algorithm */ |
| while (x < -y) { |
| if (p > 0) { |
| p += 2 * (x + ++y) + 1; |
| } else { |
| p += 2 * x + 1; |
| } |
| |
| draw_point(fb, center->x + x, center->y + y); |
| draw_point(fb, center->x - x, center->y + y); |
| draw_point(fb, center->x + x, center->y - y); |
| draw_point(fb, center->x - x, center->y - y); |
| draw_point(fb, center->x + y, center->y + x); |
| draw_point(fb, center->x + y, center->y - x); |
| draw_point(fb, center->x - y, center->y + x); |
| draw_point(fb, center->x - y, center->y - x); |
| |
| x++; |
| } |
| |
| return 0; |
| } |
| |
| int cfb_draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y) |
| { |
| return draw_text(dev, str, x, y, false); |
| } |
| |
| int cfb_print(const struct device *dev, const char *const str, int16_t x, int16_t y) |
| { |
| return draw_text(dev, str, x, y, true); |
| } |
| |
| int cfb_invert_area(const struct device *dev, int16_t x, int16_t y, |
| uint16_t width, uint16_t height) |
| { |
| const struct char_framebuffer *fb = &char_fb; |
| const bool need_reverse = ((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0); |
| |
| if ((x + width) < 0 || x >= fb->x_res) { |
| return 0; |
| } |
| |
| if ((y + height) < 0 || y >= fb->y_res) { |
| return 0; |
| } |
| |
| if (x < 0) { |
| width += x; |
| x = 0; |
| } |
| |
| if (y < 0) { |
| height += y; |
| y = 0; |
| } |
| |
| if (width > (fb->x_res - x)) { |
| width = fb->x_res - x; |
| } |
| |
| if (height > (fb->y_res - y)) { |
| height = fb->y_res - y; |
| } |
| |
| |
| if ((fb->screen_info & SCREEN_INFO_MONO_VTILED)) { |
| for (size_t i = x; i < (x + width); i++) { |
| for (size_t j = y; j < (y + height); j++) { |
| /* |
| * Process inversion in the y direction |
| * by separating per 8-line boundaries. |
| */ |
| |
| const size_t index = ((j / 8) * fb->x_res) + i; |
| const uint8_t remains = y + height - j; |
| |
| /* |
| * Make mask to prevent overwriting the drawing contents that on |
| * between the start line or end line and the 8-line boundary. |
| */ |
| if ((j % 8) > 0) { |
| uint8_t m = BIT_MASK((j % 8)); |
| uint8_t b = fb->buf[index]; |
| |
| /* |
| * Generate mask for remaining lines in case of |
| * drawing within 8 lines from the start line |
| */ |
| if (remains < 8) { |
| m |= BIT_MASK((8 - (j % 8) + remains)) |
| << ((j % 8) + remains); |
| } |
| |
| if (need_reverse) { |
| m = byte_reverse(m); |
| } |
| |
| fb->buf[index] = (b ^ (~m)); |
| j += 7 - (j % 8); |
| } else if (remains >= 8) { |
| /* No mask required if no start or end line is included */ |
| fb->buf[index] = ~fb->buf[index]; |
| j += 7; |
| } else { |
| uint8_t m = BIT_MASK(8 - remains) << (remains); |
| uint8_t b = fb->buf[index]; |
| |
| if (need_reverse) { |
| m = byte_reverse(m); |
| } |
| |
| fb->buf[index] = (b ^ (~m)); |
| j += (remains - 1); |
| } |
| } |
| } |
| } else { |
| const size_t bytes_per_row = fb->x_res / 8U; |
| |
| for (uint16_t j = y; j < (y + height); j++) { |
| const uint16_t start_byte = x / 8U; |
| const uint16_t end_byte = (x + width - 1U) / 8U; |
| |
| for (uint16_t b = start_byte; b <= end_byte; b++) { |
| const size_t index = j * bytes_per_row + b; |
| const uint8_t bit_start = (b == start_byte) ? (x % 8U) : 0U; |
| const uint8_t bit_end = |
| (b == end_byte) ? ((x + width - 1U) % 8U) : 7U; |
| uint8_t m; |
| |
| if (bit_end >= bit_start) { |
| m = BIT_MASK(bit_end - bit_start + 1U) << bit_start; |
| } else { |
| m = 0U; |
| } |
| |
| if (need_reverse) { |
| m = byte_reverse(m); |
| } |
| |
| /* invert byte with computed mask */ |
| fb->buf[index] ^= m; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int cfb_invert(const struct char_framebuffer *fb) |
| { |
| for (size_t i = 0; i < fb->x_res * fb->y_res / 8U; i++) { |
| fb->buf[i] = ~fb->buf[i]; |
| } |
| |
| return 0; |
| } |
| |
| int cfb_framebuffer_clear(const struct device *dev, bool clear_display) |
| { |
| const struct char_framebuffer *fb = &char_fb; |
| |
| if (!fb->buf) { |
| return -ENODEV; |
| } |
| |
| memset(fb->buf, 0, fb->size); |
| |
| if (clear_display) { |
| cfb_framebuffer_finalize(dev); |
| } |
| |
| return 0; |
| } |
| |
| int cfb_framebuffer_invert(const struct device *dev) |
| { |
| struct char_framebuffer *fb = &char_fb; |
| |
| fb->inverted = !fb->inverted; |
| |
| return 0; |
| } |
| |
| int cfb_framebuffer_finalize(const struct device *dev) |
| { |
| const struct display_driver_api *api = dev->api; |
| const struct char_framebuffer *fb = &char_fb; |
| int err; |
| |
| __ASSERT_NO_MSG(DEVICE_API_IS(display, dev)); |
| |
| if (!fb->buf) { |
| return -ENODEV; |
| } |
| |
| struct display_buffer_descriptor desc = { |
| .buf_size = fb->size, |
| .width = fb->x_res, |
| .height = fb->y_res, |
| .pitch = fb->x_res, |
| }; |
| |
| if ((fb->pixel_format == PIXEL_FORMAT_MONO10) != fb->inverted) { |
| cfb_invert(fb); |
| err = api->write(dev, 0, 0, &desc, fb->buf); |
| cfb_invert(fb); |
| return err; |
| } |
| |
| return api->write(dev, 0, 0, &desc, fb->buf); |
| } |
| |
| int cfb_get_display_parameter(const struct device *dev, |
| enum cfb_display_param param) |
| { |
| const struct char_framebuffer *fb = &char_fb; |
| |
| switch (param) { |
| case CFB_DISPLAY_HEIGHT: |
| return fb->y_res; |
| case CFB_DISPLAY_WIDTH: |
| return fb->x_res; |
| case CFB_DISPLAY_PPT: |
| return fb->ppt; |
| case CFB_DISPLAY_ROWS: |
| if (fb->screen_info & SCREEN_INFO_MONO_VTILED) { |
| return fb->y_res / fb->ppt; |
| } |
| return fb->y_res; |
| case CFB_DISPLAY_COLS: |
| if (fb->screen_info & SCREEN_INFO_MONO_VTILED) { |
| return fb->x_res; |
| } |
| return fb->x_res / fb->ppt; |
| } |
| return 0; |
| } |
| |
| int cfb_framebuffer_set_font(const struct device *dev, uint8_t idx) |
| { |
| struct char_framebuffer *fb = &char_fb; |
| |
| if (idx >= fb->numof_fonts) { |
| return -EINVAL; |
| } |
| |
| fb->font_idx = idx; |
| |
| return 0; |
| } |
| |
| int cfb_get_font_size(const struct device *dev, uint8_t idx, uint8_t *width, |
| uint8_t *height) |
| { |
| const struct char_framebuffer *fb = &char_fb; |
| |
| if (idx >= fb->numof_fonts) { |
| return -EINVAL; |
| } |
| |
| if (width) { |
| *width = TYPE_SECTION_START(cfb_font)[idx].width; |
| } |
| |
| if (height) { |
| *height = TYPE_SECTION_START(cfb_font)[idx].height; |
| } |
| |
| return 0; |
| } |
| |
| int cfb_set_kerning(const struct device *dev, int8_t kerning) |
| { |
| char_fb.kerning = kerning; |
| |
| return 0; |
| } |
| |
| int cfb_get_numof_fonts(const struct device *dev) |
| { |
| const struct char_framebuffer *fb = &char_fb; |
| |
| return fb->numof_fonts; |
| } |
| |
| int cfb_framebuffer_init(const struct device *dev) |
| { |
| const struct display_driver_api *api = dev->api; |
| struct char_framebuffer *fb = &char_fb; |
| struct display_capabilities cfg; |
| |
| __ASSERT_NO_MSG(DEVICE_API_IS(display, dev)); |
| |
| api->get_capabilities(dev, &cfg); |
| |
| STRUCT_SECTION_COUNT(cfb_font, &fb->numof_fonts); |
| |
| LOG_DBG("number of fonts %d", fb->numof_fonts); |
| |
| fb->x_res = cfg.x_resolution; |
| fb->y_res = cfg.y_resolution; |
| fb->ppt = 8U; |
| fb->pixel_format = cfg.current_pixel_format; |
| fb->screen_info = cfg.screen_info; |
| fb->kerning = 0; |
| fb->inverted = false; |
| |
| fb->fonts = TYPE_SECTION_START(cfb_font); |
| fb->font_idx = 0U; |
| |
| fb->size = fb->x_res * fb->y_res / fb->ppt; |
| fb->buf = k_malloc(fb->size); |
| if (!fb->buf) { |
| return -ENOMEM; |
| } |
| |
| memset(fb->buf, 0, fb->size); |
| |
| return 0; |
| } |
| |
| void cfb_framebuffer_deinit(const struct device *dev) |
| { |
| struct char_framebuffer *fb = &char_fb; |
| |
| if (fb->buf) { |
| k_free(fb->buf); |
| fb->buf = NULL; |
| } |
| |
| } |