| /* |
| * Copyright (c) 2019 Intel Corp. |
| * |
| * 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 |
| */ |
| |
| #define DT_DRV_COMPAT intel_multiboot_framebuffer |
| |
| #include <zephyr/arch/x86/multiboot.h> |
| #include <zephyr/devicetree.h> |
| #include <zephyr/drivers/display.h> |
| #include <string.h> |
| |
| struct framebuf_dev_config { |
| uint16_t width; |
| uint16_t height; |
| }; |
| |
| struct framebuf_dev_data { |
| void *buffer; |
| uint32_t pitch; |
| }; |
| |
| 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 uint8_t brightness) |
| { |
| return -ENOTSUP; |
| } |
| |
| static int framebuf_set_contrast(const struct device *dev, |
| const uint8_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) |
| { |
| const struct framebuf_dev_config *config = dev->config; |
| |
| caps->x_resolution = config->width; |
| caps->y_resolution = config->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 uint16_t x, |
| const uint16_t y, |
| const struct display_buffer_descriptor *desc, |
| const void *buf) |
| { |
| struct framebuf_dev_data *data = dev->data; |
| uint32_t *dst = data->buffer; |
| const uint32_t *src = buf; |
| uint32_t row; |
| |
| dst += x; |
| dst += (y * data->pitch); |
| |
| for (row = 0; row < desc->height; ++row) { |
| (void) memcpy(dst, src, desc->width * sizeof(uint32_t)); |
| dst += data->pitch; |
| src += desc->pitch; |
| } |
| |
| return 0; |
| } |
| |
| static int framebuf_read(const struct device *dev, const uint16_t x, |
| const uint16_t y, |
| const struct display_buffer_descriptor *desc, |
| void *buf) |
| { |
| struct framebuf_dev_data *data = dev->data; |
| uint32_t *src = data->buffer; |
| uint32_t *dst = buf; |
| uint32_t row; |
| |
| src += x; |
| src += (y * data->pitch); |
| |
| for (row = 0; row < desc->height; ++row) { |
| (void) memcpy(dst, src, desc->width * sizeof(uint32_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 |
| }; |
| |
| static int multiboot_framebuf_init(const struct device *dev) |
| { |
| const struct framebuf_dev_config *config = dev->config; |
| struct framebuf_dev_data *data = dev->data; |
| struct multiboot_info *info = &multiboot_info; |
| |
| if ((info->flags & MULTIBOOT_INFO_FLAGS_FB) && |
| (info->fb_width >= config->width) && |
| (info->fb_height >= config->height) && |
| (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. |
| */ |
| |
| uint16_t adj_x; |
| uint16_t adj_y; |
| uint32_t *buffer; |
| |
| adj_x = info->fb_width - config->width; |
| adj_y = info->fb_height - config->height; |
| data->pitch = (info->fb_pitch / 4) + adj_x; |
| adj_x /= 2U; |
| adj_y /= 2U; |
| buffer = (uint32_t *) (uintptr_t) info->fb_addr_lo; |
| buffer += adj_x; |
| buffer += adj_y * data->pitch; |
| data->buffer = buffer; |
| return 0; |
| } else { |
| return -ENOTSUP; |
| } |
| } |
| |
| static const struct framebuf_dev_config config = { |
| .width = DT_INST_PROP(0, width), |
| .height = DT_INST_PROP(0, height), |
| }; |
| |
| static struct framebuf_dev_data data; |
| |
| DEVICE_DT_INST_DEFINE(0, multiboot_framebuf_init, NULL, &data, &config, |
| PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &framebuf_display_api); |