VBE subfunction 0x15, read ddc data.
Add VBE_edid where drivers can fill in a EDID data blob. If we find valid data there (checking the first two header bytes), then report the function as supported and hand out the data.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- vgasrc/vgautil.h | 1 + vgasrc/vbe.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/vgasrc/vgautil.h b/vgasrc/vgautil.h index a9940402e45b..dd1aa189547a 100644 --- a/vgasrc/vgautil.h +++ b/vgasrc/vgautil.h @@ -89,6 +89,7 @@ extern u32 VBE_total_memory; extern u32 VBE_capabilities; extern u32 VBE_framebuffer; extern u16 VBE_win_granularity; +extern u8 VBE_edid[256]; void handle_104f(struct bregs *regs);
// vgafonts.c diff --git a/vgasrc/vbe.c b/vgasrc/vbe.c index 724c1bad6d7a..66afb011ada0 100644 --- a/vgasrc/vbe.c +++ b/vgasrc/vbe.c @@ -25,6 +25,7 @@ u32 VBE_total_memory VAR16 = 256 * 1024; u32 VBE_capabilities VAR16; u32 VBE_framebuffer VAR16; u16 VBE_win_granularity VAR16; +u8 VBE_edid[256] VAR16;
static void vbe_104f00(struct bregs *regs) @@ -400,6 +401,34 @@ vbe_104f10(struct bregs *regs) regs->ax = 0x004f; }
+static void +vbe_104f15(struct bregs *regs) +{ + int offset; + + switch (regs->bl) { + case 0x00: + if (GET_GLOBAL(VBE_edid[0]) != 0x00 || + GET_GLOBAL(VBE_edid[1]) != 0xff) + goto err; + regs->bx = 0x0103; + break; + case 0x01: + offset = regs->dx * 128; + if (offset >= sizeof(VBE_edid)) + goto err; + memcpy_far(regs->es, (void*)(regs->di+0), + get_global_seg(), VBE_edid + offset, + 128); + break; + err: + default: + regs->ax = 0x014f; + return; + } + regs->ax = 0x004f; +} + static void vbe_104fXX(struct bregs *regs) { @@ -427,6 +456,7 @@ handle_104f(struct bregs *regs) case 0x08: vbe_104f08(regs); break; case 0x0a: vbe_104f0a(regs); break; case 0x10: vbe_104f10(regs); break; + case 0x15: vbe_104f15(regs); break; default: vbe_104fXX(regs); break; } }
Read EDID blob via i2c, store in VBE_edid.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- vgasrc/atiext.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+)
diff --git a/vgasrc/atiext.c b/vgasrc/atiext.c index 0586279ce214..8c9e6966db47 100644 --- a/vgasrc/atiext.c +++ b/vgasrc/atiext.c @@ -19,6 +19,8 @@ #define MM_DATA 0x0004 #define CRTC_GEN_CNTL 0x0050 #define CRTC_EXT_CNTL 0x0054 +#define GPIO_VGA_DDC 0x0060 +#define GPIO_DVI_DDC 0x0064 #define CRTC_H_TOTAL_DISP 0x0200 #define CRTC_V_TOTAL_DISP 0x0208 #define CRTC_OFFSET 0x0224 @@ -106,6 +108,20 @@ static inline void ati_write(u32 reg, u32 val) } }
+static inline u32 ati_read(u32 reg) +{ + u32 io_addr = GET_GLOBAL(ati_io_addr); + u32 val; + + if (reg < 0x100) { + val = inl(io_addr + reg); + } else { + outl(reg, io_addr + MM_INDEX); + reg = inl(io_addr + MM_DATA); + } + return val; +} + static void ati_clear(u32 offset, u32 size) { u8 data[64]; @@ -180,6 +196,97 @@ ati_set_mode(struct vgamode_s *vmode_g, int flags) return stdvga_set_mode(vmode_g, flags); }
+/**************************************************************** + * edid + ****************************************************************/ + +static void +ati_i2c_set_scl_sda(int scl, int sda) +{ + u32 data = 0; + + if (!scl) + data |= (1 << 17); + if (!sda) + data |= (1 << 16); + ati_write(GPIO_DVI_DDC, data); +} + +static int +ati_i2c_get_sda(void) +{ + u32 data = ati_read(GPIO_DVI_DDC); + + return data & (1 << 8) ? 1 : 0; +} + +static void ati_i2c_start(void) +{ + ati_i2c_set_scl_sda(1, 1); + ati_i2c_set_scl_sda(1, 0); + ati_i2c_set_scl_sda(0, 0); +} + +static void ati_i2c_ack(void) +{ + ati_i2c_set_scl_sda(0, 0); + ati_i2c_set_scl_sda(1, 0); + ati_i2c_set_scl_sda(0, 0); +} + +static void ati_i2c_stop(void) +{ + ati_i2c_set_scl_sda(0, 0); + ati_i2c_set_scl_sda(1, 0); + ati_i2c_set_scl_sda(1, 1); +} + +static void ati_i2c_send_byte(u8 byte) +{ + int i, bit; + + for (i = 0; i < 8; i++) { + bit = (1 << (7-i)) & byte ? 1 : 0; + ati_i2c_set_scl_sda(0, bit); + ati_i2c_set_scl_sda(1, bit); + ati_i2c_set_scl_sda(0, bit); + } +} + +static u8 ati_i2c_recv_byte(void) +{ + u8 byte = 0; + int i, bit; + + for (i = 0; i < 8; i++) { + ati_i2c_set_scl_sda(0, 1); + ati_i2c_set_scl_sda(1, 1); + bit = ati_i2c_get_sda(); + ati_i2c_set_scl_sda(0, 1); + if (bit) + byte |= (1 << (7-i)); + } + + return byte; +} + +static void ati_i2c_edid(void) +{ + u8 byte; + int i; + + dprintf(1, "ati: reading edid blob\n"); + ati_i2c_start(); + ati_i2c_send_byte(0x50 << 1 | 1); + ati_i2c_ack(); + for (i = 0; i < 128; i++) { + byte = ati_i2c_recv_byte(); + ati_i2c_ack(); + SET_VGA(VBE_edid[i], byte); + } + ati_i2c_stop(); +} + /**************************************************************** * init ****************************************************************/ @@ -241,5 +348,12 @@ ati_setup(void) } }
+ u16 device = pci_config_readw(bdf, PCI_DEVICE_ID); + switch (device) { + case 0x5159: + ati_i2c_edid(); + break; + } + return 0; }
Read EDID blob from mmio bar, store in VBE_edid.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- vgasrc/bochsvga.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/vgasrc/bochsvga.c b/vgasrc/bochsvga.c index 479582f1dd4e..fd5e586b00ed 100644 --- a/vgasrc/bochsvga.c +++ b/vgasrc/bochsvga.c @@ -325,19 +325,25 @@ bochsvga_setup(void) return 0;
u32 lfb_addr = VBE_DISPI_LFB_PHYSICAL_ADDRESS; + u32 io_addr = 0; int bdf = GET_GLOBAL(VgaBDF); if (CONFIG_VGA_PCI && bdf >= 0) { u16 vendor = pci_config_readw(bdf, PCI_VENDOR_ID); - int barid; + int barid, bar; switch (vendor) { case 0x15ad: /* qemu vmware vga */ barid = 1; break; - default: /* stdvga, qxl, virtio */ + case 0x1234: /* stdvga */ + bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_2); + io_addr = bar & PCI_BASE_ADDRESS_IO_MASK; + barid = 0; + break; + default: /* qxl, virtio */ barid = 0; break; } - u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_0 + barid * 4); + bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_0 + barid * 4); lfb_addr = bar & PCI_BASE_ADDRESS_MEM_MASK; dprintf(1, "VBE DISPI: bdf %02x:%02x.%x, bar %d\n", pci_bdf_to_bus(bdf) , pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf), barid); @@ -373,5 +379,12 @@ bochsvga_setup(void) } }
+ if (io_addr) { + int i; + u8 *edid = (void*)(io_addr); + for (i = 0; i < sizeof(VBE_edid); i++) + SET_VGA(VBE_edid[i], readb(edid + i)); + } + return 0; }
Read EDID blob from mmio bar, store in VBE_edid.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- vgasrc/bochsdisplay.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/vgasrc/bochsdisplay.c b/vgasrc/bochsdisplay.c index 6a75f3787efc..38891b5f6734 100644 --- a/vgasrc/bochsdisplay.c +++ b/vgasrc/bochsdisplay.c @@ -4,6 +4,7 @@ #include "bochsvga.h" // VBE_BOCHS_* #include "hw/pci.h" // pci_config_readl #include "hw/pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "vgabios.h" // SET_VGA #include "vgautil.h" // VBE_total_memory
#define FRAMEBUFFER_WIDTH 1024 @@ -40,7 +41,12 @@ bochs_display_setup(void) if (id != VBE_DISPI_ID5) return -1;
- dprintf(1, "bochs-display: using %dx%d, %d bpp (%d stride)\n" + int i; + u8 *edid = (void*)(io_addr); + for (i = 0; i < sizeof(VBE_edid); i++) + SET_VGA(VBE_edid[i], readb(edid + i)); + + dprintf(1, "bochs-display: using %dx%d, %d bpp (%d stride)\n" , FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT , FRAMEBUFFER_BPP * 8, FRAMEBUFFER_STRIDE);
Then use the resolution for the framebuffer.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- vgasrc/bochsdisplay.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/vgasrc/bochsdisplay.c b/vgasrc/bochsdisplay.c index 38891b5f6734..61689d6eb93c 100644 --- a/vgasrc/bochsdisplay.c +++ b/vgasrc/bochsdisplay.c @@ -10,8 +10,6 @@ #define FRAMEBUFFER_WIDTH 1024 #define FRAMEBUFFER_HEIGHT 768 #define FRAMEBUFFER_BPP 4 -#define FRAMEBUFFER_STRIDE (FRAMEBUFFER_BPP * FRAMEBUFFER_WIDTH) -#define FRAMEBUFFER_SIZE (FRAMEBUFFER_STRIDE * FRAMEBUFFER_HEIGHT)
int bochs_display_setup(void) @@ -46,16 +44,26 @@ bochs_display_setup(void) for (i = 0; i < sizeof(VBE_edid); i++) SET_VGA(VBE_edid[i], readb(edid + i));
+ int fb_width = FRAMEBUFFER_WIDTH; + int fb_height = FRAMEBUFFER_HEIGHT; + if (GET_GLOBAL(VBE_edid[0]) == 0x00 && + GET_GLOBAL(VBE_edid[1]) == 0xff) { + fb_width = GET_GLOBAL(VBE_edid[54 + 2]); + fb_width |= (GET_GLOBAL(VBE_edid[54 + 4]) & 0xf0) << 4; + fb_height = GET_GLOBAL(VBE_edid[54 + 5]); + fb_height |= (GET_GLOBAL(VBE_edid[54 + 7]) & 0xf0) << 4; + } + int fb_stride = FRAMEBUFFER_BPP * fb_width; + dprintf(1, "bochs-display: using %dx%d, %d bpp (%d stride)\n" - , FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT - , FRAMEBUFFER_BPP * 8, FRAMEBUFFER_STRIDE); + , fb_width, fb_height + , FRAMEBUFFER_BPP * 8, fb_stride);
cbvga_setup_modes(lfb_addr, FRAMEBUFFER_BPP * 8, - FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, - FRAMEBUFFER_STRIDE); + fb_width, fb_height, fb_stride);
- writew(dispi + VBE_DISPI_INDEX_XRES, FRAMEBUFFER_WIDTH); - writew(dispi + VBE_DISPI_INDEX_YRES, FRAMEBUFFER_HEIGHT); + writew(dispi + VBE_DISPI_INDEX_XRES, fb_width); + writew(dispi + VBE_DISPI_INDEX_YRES, fb_height); writew(dispi + VBE_DISPI_INDEX_BPP, FRAMEBUFFER_BPP * 8); writew(dispi + VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED);
On Thu, Apr 11, 2019 at 08:59:10AM +0200, Gerd Hoffmann wrote:
VBE subfunction 0x15, read ddc data.
Add VBE_edid where drivers can fill in a EDID data blob. If we find valid data there (checking the first two header bytes), then report the function as supported and hand out the data.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
FYI, the series looks fine to me.
Thanks. -Kevin
On Thu, Apr 11, 2019 at 11:14:16AM -0400, Kevin O'Connor wrote:
On Thu, Apr 11, 2019 at 08:59:10AM +0200, Gerd Hoffmann wrote:
VBE subfunction 0x15, read ddc data.
Add VBE_edid where drivers can fill in a EDID data blob. If we find valid data there (checking the first two header bytes), then report the function as supported and hand out the data.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
FYI, the series looks fine to me.
Series pushed now.
cheers, Gerd