From 4c63e29aeceb054cf0bb1fab26f5a6c8616bd6a4 Mon Sep 17 00:00:00 2001 From: "Charles E. Youse" Date: Wed, 29 May 2019 10:52:31 -0700 Subject: [PATCH] arch/x86: drivers/display: add framebuffer driver w/ multiboot support A basic display driver is added for a generic 32-bpp framebuffer. Glue logic is added to the x86 arch to request the intitialization of a linear framebuffer by the Multiboot loader (GRUB) and connect it to this generic driver. Signed-off-by: Charles E. Youse --- CODEOWNERS | 2 + arch/x86/Kconfig | 23 +++++ arch/x86/core/crt0.S | 28 +++--- arch/x86/core/multiboot.c | 55 ++++++++++++ drivers/display/CMakeLists.txt | 19 ++-- drivers/display/Kconfig | 10 ++- drivers/display/display_framebuf.c | 134 +++++++++++++++++++++++++++++ include/arch/x86/multiboot.h | 12 +++ include/display/framebuf.h | 23 +++++ 9 files changed, 275 insertions(+), 31 deletions(-) create mode 100644 drivers/display/display_framebuf.c create mode 100644 include/display/framebuf.h diff --git a/CODEOWNERS b/CODEOWNERS index d51782315f..e19a1718db 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -113,6 +113,7 @@ /drivers/clock_control/*stm32f4* @rsalveti @idlethread /drivers/counter/ @nordic-krch /drivers/display/ @vanwinkeljan +/drivers/display/display_framebuf.c @gnuless /drivers/dma/*sam0* @Sizurka /drivers/ethernet/ @jukkar @tbursztyka @pfalcon /drivers/flash/ @nashif @@ -217,6 +218,7 @@ /include/device.h @wentongwu @nashif /include/display.h @vanwinkeljan /include/display/ @vanwinkeljan +/include/display/framebuf.h @gnuless /include/drivers/bluetooth/ @joerchan @jhedberg @Vudentz /include/drivers/led/ht16k33.h @henrikbrixandersen /include/drivers/modem/ @mike-scott diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6f417b2a56..2b6d31b4b6 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -30,6 +30,29 @@ config X86_MULTIBOOT_INFO display driver) need to refer to information in this structure, and so set this option to preserve the data in a permanent location. +if X86_MULTIBOOT + +config X86_MULTIBOOT_FRAMEBUF + bool "Multiboot framebuffer support" + default n + select DISPLAY + select FRAMEBUF_DISPLAY + select X86_MULTIBOOT_INFO + +if X86_MULTIBOOT_FRAMEBUF + +config X86_MULTIBOOT_FRAMEBUF_X + int "Multiboot framebuffer X pixels" + default 640 + +config X86_MULTIBOOT_FRAMEBUF_Y + int "Multiboot framebuffer Y pixels" + default 480 + +endif # X86_MULTIBOOT_FRAMEBUF + +endif # X86_MULTIBOOT + source "arch/x86/core/Kconfig" # diff --git a/arch/x86/core/crt0.S b/arch/x86/core/crt0.S index 43964492bd..0419e0502d 100644 --- a/arch/x86/core/crt0.S +++ b/arch/x86/core/crt0.S @@ -507,34 +507,26 @@ z_x86_idt: #ifdef CONFIG_X86_MULTIBOOT - /* Multiboot header definition is needed for some bootloaders */ - /* * The multiboot header must be in the first 8 Kb of the kernel image * (not including the ELF section header(s)) and be aligned on a - * 4 byte boundary. + * 4 byte boundary. See include/arch/x86/multiboot.h for more info. */ .balign 4,0x90 .long X86_MULTIBOOT_HEADER_MAGIC + .long X86_MULTIBOOT_HEADER_FLAGS + .long -(X86_MULTIBOOT_HEADER_MAGIC + X86_MULTIBOOT_HEADER_FLAGS) - /* - * Flags = no bits are being set, specifically bit 16 is not being - * set since the supplied kernel image is an ELF file, and the - * multiboot loader shall use the information from the program and - * section header to load and boot the kernel image. - */ +#ifdef CONFIG_X86_MULTIBOOT_FRAMEBUF + .fill 5,4,0 /* (unused exec layout) */ + .long 0 /* linear graphics mode */ + .long CONFIG_X86_MULTIBOOT_FRAMEBUF_X /* width */ + .long CONFIG_X86_MULTIBOOT_FRAMEBUF_Y /* height */ + .long 32 /* depth */ +#endif /* CONFIG_X86_MULTIBOOT_FRAMEBUF */ - .long 0x00000000 - - /* - * checksum = 32-bit unsigned value which, when added to the other - * magic fields (i.e. "magic" and "flags"), must have a 32-bit - * unsigned sum of zero. - */ - - .long -(X86_MULTIBOOT_HEADER_MAGIC + 0) #endif /* CONFIG_X86_MULTIBOOT */ #ifdef CONFIG_SET_GDT diff --git a/arch/x86/core/multiboot.c b/arch/x86/core/multiboot.c index 1a35e7a60a..99de3e6206 100644 --- a/arch/x86/core/multiboot.c +++ b/arch/x86/core/multiboot.c @@ -11,4 +11,59 @@ struct x86_multiboot_info x86_multiboot_info; +#ifdef CONFIG_X86_MULTIBOOT_FRAMEBUF + +#include + +static struct framebuf_dev_data multiboot_framebuf_data = { + .width = CONFIG_X86_MULTIBOOT_FRAMEBUF_X, + .height = CONFIG_X86_MULTIBOOT_FRAMEBUF_Y +}; + +static int multiboot_framebuf_init(struct device *dev) +{ + struct framebuf_dev_data *data = FRAMEBUF_DATA(dev); + struct x86_multiboot_info *info = &x86_multiboot_info; + + if ((info->flags & X86_MULTIBOOT_INFO_FLAGS_FB) && + (info->fb_width >= CONFIG_X86_MULTIBOOT_FRAMEBUF_X) && + (info->fb_height >= CONFIG_X86_MULTIBOOT_FRAMEBUF_Y) && + (info->fb_bpp == 32) && (info->fb_addr_hi == 0)) { + /* + * We have a usable multiboot framebuffer - it is 32 bpp + * and at least as large as the requested dimensions. Compute + * the pitch and adjust the start address center our canvas. + */ + + u16_t adj_x; + u16_t adj_y; + u32_t *buffer; + + adj_x = info->fb_width - CONFIG_X86_MULTIBOOT_FRAMEBUF_X; + adj_y = info->fb_height - CONFIG_X86_MULTIBOOT_FRAMEBUF_Y; + buffer = (uint32_t *) info->fb_addr_lo; + data->pitch = (info->fb_pitch / 4) + adj_x; + adj_x /= 2; + adj_y /= 2; + buffer = (uint32_t *) info->fb_addr_lo; + buffer += adj_x; + buffer += adj_y * data->pitch; + data->buffer = buffer; + return 0; + } else { + return -ENOTSUP; + } +} + +DEVICE_AND_API_INIT(multiboot_framebuf, + "FRAMEBUF", + multiboot_framebuf_init, + &multiboot_framebuf_data, + NULL, + PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &framebuf_display_api); + +#endif /* CONFIG_X86_MULTIBOOT_FRAMEBUF */ + #endif /* CONFIG_X86_MULTIBOOT_INFO */ diff --git a/drivers/display/CMakeLists.txt b/drivers/display/CMakeLists.txt index fbe371f6d5..558fdef081 100644 --- a/drivers/display/CMakeLists.txt +++ b/drivers/display/CMakeLists.txt @@ -1,23 +1,22 @@ # SPDX-License-Identifier: Apache-2.0 - zephyr_sources_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF display_mcux_elcdif.c) -zephyr_sources_ifdef(CONFIG_GROVE_LCD_RGB grove_lcd_rgb.c) +zephyr_sources_ifdef(CONFIG_GROVE_LCD_RGB grove_lcd_rgb.c) +zephyr_sources_ifdef(CONFIG_SSD1306 ssd1306.c) +zephyr_sources_ifdef(CONFIG_SSD1673 ssd1673.c) +zephyr_sources_ifdef(CONFIG_SDL_DISPLAY display_sdl.c) +zephyr_sources_ifdef(CONFIG_DUMMY_DISPLAY display_dummy.c) +zephyr_sources_ifdef(CONFIG_FRAMEBUF_DISPLAY display_framebuf.c) +zephyr_sources_ifdef(CONFIG_ILI9340 display_ili9340.c) + zephyr_sources_ifdef(CONFIG_MICROBIT_DISPLAY mb_display.c mb_font.c ) -zephyr_sources_ifdef(CONFIG_ILI9340 display_ili9340.c) zephyr_sources_ifdef(CONFIG_ILI9340_LCD_ADAFRUIT_1480 display_ili9340_adafruit_1480.c ) + zephyr_sources_ifdef(CONFIG_ILI9340_LCD_SEEED_TFTV2 display_ili9340_seeed_tftv2.c ) -zephyr_sources_ifdef(CONFIG_SSD1306 ssd1306.c) -zephyr_sources_ifdef(CONFIG_SSD1673 ssd1673.c) - -zephyr_sources_ifdef(CONFIG_SDL_DISPLAY display_sdl.c) - -zephyr_sources_ifdef(CONFIG_DUMMY_DISPLAY display_dummy.c) - diff --git a/drivers/display/Kconfig b/drivers/display/Kconfig index dbf1998c04..17fd9ff6c3 100644 --- a/drivers/display/Kconfig +++ b/drivers/display/Kconfig @@ -22,11 +22,15 @@ source "drivers/display/Kconfig.mcux_elcdif" source "drivers/display/Kconfig.microbit" source "drivers/display/Kconfig.ili9340" source "drivers/display/Kconfig.sdl" - source "drivers/display/Kconfig.ssd1306" - source "drivers/display/Kconfig.ssd1673" - source "drivers/display/Kconfig.dummy" +config FRAMEBUF_DISPLAY + # Hidden, selected by client drivers. + bool + default n + help + Enable framebuffer-based display 'helper' driver. + endif # DISPLAY diff --git a/drivers/display/display_framebuf.c b/drivers/display/display_framebuf.c new file mode 100644 index 0000000000..c61a87f534 --- /dev/null +++ b/drivers/display/display_framebuf.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019 Intel Corp. + * + * This is most of the display driver for a "standard" 32-bpp framebuffer. + * Device-specific drivers must still create the device instance and initialize + * it accordingly, but this driver implements most/all of the API functions. + * This code attempts to be endian-agnostic. It manipulates the framebuffer + * address space only in 32-bit words (and assumes those words are 0xAARRGGBB). + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +static int framebuf_blanking_on(const struct device *dev) +{ + return -ENOTSUP; +} + +static int framebuf_blanking_off(const struct device *dev) +{ + return -ENOTSUP; +} + +static void *framebuf_get_framebuffer(const struct device *dev) +{ + return NULL; +} + +static int framebuf_set_brightness(const struct device *dev, + const u8_t brightness) +{ + return -ENOTSUP; +} + +static int framebuf_set_contrast(const struct device *dev, + const u8_t contrast) +{ + return -ENOTSUP; +} + +static int framebuf_set_pixel_format(const struct device *dev, + const enum display_pixel_format format) +{ + switch (format) { + case PIXEL_FORMAT_ARGB_8888: + return 0; + default: + return -ENOTSUP; + } +} + +static int framebuf_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + switch (orientation) { + case DISPLAY_ORIENTATION_NORMAL: + return 0; + default: + return -ENOTSUP; + } +} + +static void framebuf_get_capabilities(const struct device *dev, + struct display_capabilities *caps) +{ + struct framebuf_dev_data *data = FRAMEBUF_DATA(dev); + + caps->x_resolution = data->width; + caps->y_resolution = data->height; + caps->supported_pixel_formats = PIXEL_FORMAT_ARGB_8888; + caps->screen_info = 0; + caps->current_pixel_format = PIXEL_FORMAT_ARGB_8888; + caps->current_orientation = DISPLAY_ORIENTATION_NORMAL; +} + +static int framebuf_write(const struct device *dev, const u16_t x, + const u16_t y, + const struct display_buffer_descriptor *desc, + const void *buf) +{ + struct framebuf_dev_data *data = FRAMEBUF_DATA(dev); + u32_t *dst = data->buffer; + const u32_t *src = buf; + u32_t row; + + dst += x; + dst += (y * data->pitch); + + for (row = 0; row < desc->height; ++row) { + (void) memcpy(dst, src, desc->width * sizeof(u32_t)); + dst += data->pitch; + src += desc->pitch; + } + + return 0; +} + +static int framebuf_read(const struct device *dev, const u16_t x, + const u16_t y, + const struct display_buffer_descriptor *desc, + void *buf) +{ + struct framebuf_dev_data *data = FRAMEBUF_DATA(dev); + u32_t *src = data->buffer; + u32_t *dst = buf; + u32_t row; + + src += x; + src += (y * data->pitch); + + for (row = 0; row < desc->height; ++row) { + (void) memcpy(dst, src, desc->width * sizeof(u32_t)); + src += data->pitch; + dst += desc->pitch; + } + + return 0; +} + +const struct display_driver_api framebuf_display_api = { + .blanking_on = framebuf_blanking_on, + .blanking_off = framebuf_blanking_off, + .write = framebuf_write, + .read = framebuf_read, + .get_framebuffer = framebuf_get_framebuffer, + .set_brightness = framebuf_set_brightness, + .set_contrast = framebuf_set_contrast, + .get_capabilities = framebuf_get_capabilities, + .set_pixel_format = framebuf_set_pixel_format, + .set_orientation = framebuf_set_orientation +}; diff --git a/include/arch/x86/multiboot.h b/include/arch/x86/multiboot.h index 0bcb409066..2c168cebfa 100644 --- a/include/arch/x86/multiboot.h +++ b/include/arch/x86/multiboot.h @@ -42,6 +42,18 @@ extern struct x86_multiboot_info x86_multiboot_info; #define X86_MULTIBOOT_HEADER_MAGIC 0x1BADB002 #define X86_MULTIBOOT_EAX_MAGIC 0x2BADB002 +/* + * Typically, we put no flags in the multiboot header, as it exists solely + * to reassure the loader that we're a valid binary. The exception to this + * is when we want the loader to configure the framebuffer for us. + */ + +#ifdef CONFIG_X86_MULTIBOOT_FRAMEBUF +#define X86_MULTIBOOT_HEADER_FLAGS 4 +#else +#define X86_MULTIBOOT_HEADER_FLAGS 0 +#endif + /* The fb_* fields are valid if X86_MULTIBOOT_INFO_FLAGS_FB is set. */ #define X86_MULTIBOOT_INFO_FLAGS_FB (1 << 12) diff --git a/include/display/framebuf.h b/include/display/framebuf.h new file mode 100644 index 0000000000..3f39520819 --- /dev/null +++ b/include/display/framebuf.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019 Intel Corp. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DISPLAY_FRAMEBUF_H_ +#define ZEPHYR_INCLUDE_DISPLAY_FRAMEBUF_H_ + +#include + +extern const struct display_driver_api framebuf_display_api; + +struct framebuf_dev_data { + void *buffer; + u32_t pitch; + u16_t width; + u16_t height; +}; + +#define FRAMEBUF_DATA(dev) ((struct framebuf_dev_data *) ((dev)->driver_data)) + +#endif /* ZEPHYR_INCLUDE_DISPLAY_FRAMEBUF_H_ */