diff options
author | Patrick Rudolph <patrick.rudolph@9elements.com> | 2019-09-30 11:02:04 +0200 |
---|---|---|
committer | Patrick Rudolph <siro@das-labor.org> | 2019-12-12 12:49:01 +0000 |
commit | f1a4ae0a48e90de2a8a029c38c4583506b02a676 (patch) | |
tree | 1aa0739c6af8df97746f2bfc420cb94201cf458d /src/drivers/aspeed/common/ast_mode_corebootfb.c | |
parent | 199f98bc433f36fd0f91edd2495ecc3fd23afdbf (diff) |
drivers/aspeed/common: Add support for high resolution framebuffer
* Implement reading EDID over software I2C.
* Fall back to VGA if no monitor connected for BMC KVM
* Copy the linux kernel code and add a bunch of wrapper structs to make it
compile.
* Convert the EDID to a drm_display_mode, which is understood by the
driver.
* Properly select HAVE_LINEAR_FRAMEBUFFER and HAVE_VGA_TEXT_FRAMEBUFFER
Tested on Supermicro X11SSH-TF using FullHD VGA monitor.
Initializes the graphics in about 1 second, which is twice as fast as the
VGA Option ROM.
The framebuffer is advertised and working in tianocore.
Change-Id: I7803566b64158405efc04a39f80a0ec98b44e646
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/35726
Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/drivers/aspeed/common/ast_mode_corebootfb.c')
-rw-r--r-- | src/drivers/aspeed/common/ast_mode_corebootfb.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/drivers/aspeed/common/ast_mode_corebootfb.c b/src/drivers/aspeed/common/ast_mode_corebootfb.c new file mode 100644 index 0000000000..2ec85ac31f --- /dev/null +++ b/src/drivers/aspeed/common/ast_mode_corebootfb.c @@ -0,0 +1,256 @@ +/* + * Copied from Linux drivers/gpu/drm/ast/ast_mode.c + * + * Copyright 2012 Red Hat Inc. + * Parts based on xf86-video-ast + * Copyright (c) 2005 ASPEED Technology Inc. + * Copyright Dave Airlie <airlied@redhat.com> + * Copyright 2019 9Elements Agency GmbH <patrick.rudolph@9elements.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ + +#include <edid.h> + +#include "ast_drv.h" + +/* + * Set framebuffer MMIO address, which must fall into BAR0 MMIO window. + * + * Complete reimplementation as the original expects multiple kernel internal + * subsystems to be present. + */ +int ast_crtc_do_set_base(struct drm_crtc *crtc) +{ + struct ast_private *ast = crtc->dev->dev_private; + struct drm_framebuffer *fb = crtc->primary->fb; + + /* PCI BAR 0 */ + struct resource *res = find_resource(crtc->dev->pdev, 0x10); + if (!res) { + printk(BIOS_ERR, "BAR0 resource not found.\n"); + return -EIO; + } + + if (res->size < fb->pitches[0] * crtc->mode.vdisplay) { + dev_err(dev->pdev, "Framebuffer doesn't fit into BAR0 MMIO window\n"); + return -ENOMEM; + } + + fb->mmio_addr = (u32)res2mmio(res, 4095, 4095); + + ast_set_offset_reg(crtc); + ast_set_start_address_crt1(ast, fb->mmio_addr); + + return 0; +} + +static void ast_edid_to_drmmode(struct edid *edid, struct drm_display_mode *mode) +{ + memset(mode, 0, sizeof(*mode)); + + mode->hdisplay = edid->mode.ha; + mode->vdisplay = edid->mode.va; + mode->crtc_hdisplay = edid->mode.ha; + mode->crtc_vdisplay = edid->mode.va; + + /* EDID clock is in 10kHz, but drm clock is in KHz */ + mode->clock = edid->mode.pixel_clock * 10; + mode->vrefresh = edid->mode.refresh; + + mode->crtc_hblank_start = edid->mode.ha; + mode->crtc_hblank_end = edid->mode.ha + edid->mode.hbl; + mode->crtc_hsync_start = edid->mode.ha + edid->mode.hso; + mode->crtc_hsync_end = edid->mode.ha + edid->mode.hso + edid->mode.hspw; + mode->crtc_htotal = mode->crtc_hblank_end; + + mode->crtc_vblank_start = edid->mode.va; + mode->crtc_vblank_end = edid->mode.va + edid->mode.vbl; + mode->crtc_vsync_start = edid->mode.va + edid->mode.vso; + mode->crtc_vsync_end = edid->mode.va + edid->mode.vso + edid->mode.vspw; + mode->crtc_vtotal = mode->crtc_vblank_end; + + mode->flags = 0; + if (edid->mode.phsync == '+') + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else + mode->flags |= DRM_MODE_FLAG_NHSYNC; + + if (edid->mode.pvsync == '+') + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else + mode->flags |= DRM_MODE_FLAG_NVSYNC; +} + +static int ast_select_mode(struct drm_connector *connector, + struct edid *edid) +{ + struct ast_private *ast = connector->dev->dev_private; + bool widescreen; + u8 raw[128]; + bool flags = false; + + if (ast->tx_chip_type == AST_TX_DP501) { + ast->dp501_maxclk = 0xff; + flags = ast_dp501_read_edid(connector->dev, (u8 *)raw); + if (flags) + ast->dp501_maxclk = ast_get_dp501_max_clk(connector->dev); + else + dev_err(dev->pdev, "I2C transmission error\n"); + } + + if (!flags) + ast_software_i2c_read(ast, raw); + + if (decode_edid(raw, sizeof(raw), edid) != EDID_CONFORMANT) { + dev_err(dev->pdev, "Failed to decode EDID\n"); + printk(BIOS_DEBUG, "Assuming VGA for KVM\n"); + + memset(edid, 0, sizeof(*edid)); + + edid->mode.pixel_clock = 6411; + edid->mode.refresh = 60; + edid->mode.ha = 1024; + edid->mode.hspw = 4; + edid->mode.hso = 56; + edid->mode.hbl = 264; + edid->mode.phsync = '-'; + + edid->mode.va = 768; + edid->mode.vspw = 3; + edid->mode.vso = 1; + edid->mode.vbl = 26; + edid->mode.pvsync = '+'; + } + + printk(BIOS_DEBUG, "AST: Display has %dpx x %dpx\n", edid->mode.ha, edid->mode.va); + + widescreen = !!(((edid->mode.ha * 4) % (edid->mode.va * 3))); + + while (ast_mode_valid(connector, edid->mode.ha, edid->mode.va) != MODE_OK) { + /* Select a compatible smaller mode */ + if (edid->mode.ha > 1920 && widescreen) { + edid->mode.ha = 1920; + edid->mode.va = 1080; + } else if (edid->mode.ha >= 1920 && widescreen) { + edid->mode.ha = 1680; + edid->mode.va = 1050; + } else if (edid->mode.ha >= 1680 && widescreen) { + edid->mode.ha = 1600; + edid->mode.va = 900; + } else if (edid->mode.ha >= 1680 && !widescreen) { + edid->mode.ha = 1600; + edid->mode.va = 1200; + } else if (edid->mode.ha >= 1600 && widescreen) { + edid->mode.ha = 1440; + edid->mode.va = 900; + } else if (edid->mode.ha >= 1440 && widescreen) { + edid->mode.ha = 1360; + edid->mode.va = 768; + } else if (edid->mode.ha >= 1360 && widescreen) { + edid->mode.ha = 1280; + edid->mode.va = 800; + } else if (edid->mode.ha >= 1360 && !widescreen) { + edid->mode.ha = 1280; + edid->mode.va = 1024; + } else if (edid->mode.ha >= 1280) { + edid->mode.ha = 1024; + edid->mode.va = 768; + } else if (edid->mode.ha >= 1024) { + edid->mode.ha = 800; + edid->mode.va = 600; + } else if (edid->mode.ha >= 800) { + edid->mode.ha = 640; + edid->mode.va = 480; + } else { + dev_err(dev->pdev, "No compatible mode found.\n"); + + return -EIO; + } + }; + + return 0; +} + +int ast_driver_framebuffer_init(struct drm_device *dev, int flags) +{ + struct drm_display_mode adjusted_mode; + struct drm_crtc crtc; + struct drm_format format; + struct drm_primary primary; + struct drm_framebuffer fb; + struct drm_connector connector; + struct edid edid; + int ret; + + /* Init wrapper structs */ + connector.dev = dev; + + format.cpp[0] = 4; /* 32 BPP */ + fb.format = &format; + + primary.fb = &fb; + + crtc.dev = dev; + crtc.primary = &primary; + + /* Read EDID and find mode */ + ret = ast_select_mode(&connector, &edid); + if (ret) { + dev_err(dev->pdev, "Failed to select mode.\n"); + return ret; + } + + /* Updated edid for set_vbe_mode_info_valid */ + edid.x_resolution = edid.mode.ha; + edid.y_resolution = edid.mode.va; + edid.framebuffer_bits_per_pixel = format.cpp[0] * 8; + edid.bytes_per_line = ALIGN_UP(edid.x_resolution * format.cpp[0], 8); + + /* Updated framebuffer info for ast_crtc_mode_set */ + fb.pitches[0] = edid.bytes_per_line; + + printk(BIOS_DEBUG, "Using framebuffer %dpx x %dpx pitch %d @ %d BPP\n", + edid.x_resolution, edid.y_resolution, edid.bytes_per_line, + edid.framebuffer_bits_per_pixel); + + /* Convert EDID to AST DRM mode */ + ast_edid_to_drmmode(&edid, &crtc.mode); + + memcpy(&adjusted_mode, &crtc.mode, sizeof(crtc.mode)); + + ret = ast_crtc_mode_set(&crtc, &crtc.mode, &adjusted_mode); + if (ret) { + dev_err(dev->pdev, "Failed to set mode.\n"); + return ret; + } + + ast_hide_cursor(&crtc); + + /* Advertise new mode */ + set_vbe_mode_info_valid(&edid, fb.mmio_addr); + + /* Clear display */ + memset((void *)fb.mmio_addr, 0, edid.bytes_per_line * edid.y_resolution); + + return 0; +} |