diff mbox series

[v2] edid: add support for DisplayID extension (5k resolution)

Message ID 20210314091236.44313-1-mail@knazarov.com (mailing list archive)
State New, archived
Headers show
Series [v2] edid: add support for DisplayID extension (5k resolution) | expand

Commit Message

mail@knazarov.com March 14, 2021, 9:12 a.m. UTC
The Detailed Timing Descriptor has only 12 bits to store the
resolution. This limits the guest to 4095 pixels.

This patch adds support for the DisplayID extension, that has 2 full
bytes for that purpose, thus allowing 5k resolutions and above.

Based-on: <20210303152948.59943-2-akihiko.odaki@gmail.com>
Signed-off-by: Konstantin Nazarov <mail@knazarov.com>
---
 hw/display/edid-generate.c | 156 +++++++++++++++++++++++++++++--------
 hw/display/vga-pci.c       |   2 +-
 qemu-edid.c                |   2 +-
 3 files changed, 124 insertions(+), 36 deletions(-)

Comments

Akihiko Odaki March 14, 2021, 9:48 a.m. UTC | #1
2021年3月14日(日) 18:12 Konstantin Nazarov <mail@knazarov.com>:
>
> The Detailed Timing Descriptor has only 12 bits to store the
> resolution. This limits the guest to 4095 pixels.
>
> This patch adds support for the DisplayID extension, that has 2 full
> bytes for that purpose, thus allowing 5k resolutions and above.
>
> Based-on: <20210303152948.59943-2-akihiko.odaki@gmail.com>
> Signed-off-by: Konstantin Nazarov <mail@knazarov.com>
> ---
>  hw/display/edid-generate.c | 156 +++++++++++++++++++++++++++++--------
>  hw/display/vga-pci.c       |   2 +-
>  qemu-edid.c                |   2 +-
>  3 files changed, 124 insertions(+), 36 deletions(-)
>
> diff --git a/hw/display/edid-generate.c b/hw/display/edid-generate.c
> index b0ce583d436..db1e319e2dc 100644
> --- a/hw/display/edid-generate.c
> +++ b/hw/display/edid-generate.c
> @@ -44,6 +44,35 @@ static const struct edid_mode {
>      { .xres =  640,   .yres =  480,   .byte  = 35,   .bit = 5 },
>  };
>
> +typedef struct Timings {
> +    uint32_t xfront;
> +    uint32_t xsync;
> +    uint32_t xblank;
> +
> +    uint32_t yfront;
> +    uint32_t ysync;
> +    uint32_t yblank;
> +
> +    uint64_t clock;
> +} Timings;
> +
> +static void generate_timings(Timings *timings, uint32_t refresh_rate,
> +                             uint32_t xres, uint32_t yres)
> +{
> +    /* pull some realistic looking timings out of thin air */
> +    timings->xfront = xres * 25 / 100;
> +    timings->xsync  = xres *  3 / 100;
> +    timings->xblank = xres * 35 / 100;
> +
> +    timings->yfront = yres *  5 / 1000;
> +    timings->ysync  = yres *  5 / 1000;
> +    timings->yblank = yres * 35 / 1000;
> +
> +    timings->clock  = ((uint64_t)refresh_rate *
> +                       (xres + timings->xblank) *
> +                       (yres + timings->yblank)) / 10000000;
> +}
> +
>  static void edid_ext_dta(uint8_t *dta)
>  {
>      dta[0] = 0x02;
> @@ -129,17 +158,17 @@ static void edid_fill_modes(uint8_t *edid, uint8_t *xtra3, uint8_t *dta,
>      }
>  }
>
> -static void edid_checksum(uint8_t *edid)
> +static void edid_checksum(uint8_t *edid, size_t len)
>  {
>      uint32_t sum = 0;
>      int i;
>
> -    for (i = 0; i < 127; i++) {
> +    for (i = 0; i < len; i++) {
>          sum += edid[i];
>      }
>      sum &= 0xff;
>      if (sum) {
> -        edid[127] = 0x100 - sum;
> +        edid[len] = 0x100 - sum;
>      }
>  }
>
> @@ -180,8 +209,8 @@ static void edid_desc_ranges(uint8_t *desc)
>      desc[7] =  30;
>      desc[8] = 160;
>
> -    /* max dot clock (1200 MHz) */
> -    desc[9] = 1200 / 10;
> +    /* max dot clock (2550 MHz) */
> +    desc[9] = 2550 / 10;
>
>      /* no extended timing information */
>      desc[10] = 0x01;
> @@ -207,38 +236,29 @@ static void edid_desc_timing(uint8_t *desc, uint32_t refresh_rate,
>                               uint32_t xres, uint32_t yres,
>                               uint32_t xmm, uint32_t ymm)
>  {
> -    /* pull some realistic looking timings out of thin air */
> -    uint32_t xfront = xres * 25 / 100;
> -    uint32_t xsync  = xres *  3 / 100;
> -    uint32_t xblank = xres * 35 / 100;
> -
> -    uint32_t yfront = yres *  5 / 1000;
> -    uint32_t ysync  = yres *  5 / 1000;
> -    uint32_t yblank = yres * 35 / 1000;
> -
> -    uint64_t clock  = (uint64_t)refresh_rate * (xres + xblank) * (yres + yblank);
> -
> -    stl_le_p(desc, clock / 10000000);
> +    Timings timings;
> +    generate_timings(&timings, refresh_rate, xres, yres);
> +    stl_le_p(desc, timings.clock);
>
>      desc[2] = xres   & 0xff;
> -    desc[3] = xblank & 0xff;
> +    desc[3] = timings.xblank & 0xff;
>      desc[4] = (((xres   & 0xf00) >> 4) |
> -               ((xblank & 0xf00) >> 8));
> +               ((timings.xblank & 0xf00) >> 8));
>
>      desc[5] = yres   & 0xff;
> -    desc[6] = yblank & 0xff;
> +    desc[6] = timings.yblank & 0xff;
>      desc[7] = (((yres   & 0xf00) >> 4) |
> -               ((yblank & 0xf00) >> 8));
> +               ((timings.yblank & 0xf00) >> 8));
>
> -    desc[8] = xfront & 0xff;
> -    desc[9] = xsync  & 0xff;
> +    desc[8] = timings.xfront & 0xff;
> +    desc[9] = timings.xsync  & 0xff;
>
> -    desc[10] = (((yfront & 0x00f) << 4) |
> -                ((ysync  & 0x00f) << 0));
> -    desc[11] = (((xfront & 0x300) >> 2) |
> -                ((xsync  & 0x300) >> 4) |
> -                ((yfront & 0x030) >> 2) |
> -                ((ysync  & 0x030) >> 4));
> +    desc[10] = (((timings.yfront & 0x00f) << 4) |
> +                ((timings.ysync  & 0x00f) << 0));
> +    desc[11] = (((timings.xfront & 0x300) >> 2) |
> +                ((timings.xsync  & 0x300) >> 4) |
> +                ((timings.yfront & 0x030) >> 2) |
> +                ((timings.ysync  & 0x030) >> 4));
>
>      desc[12] = xmm & 0xff;
>      desc[13] = ymm & 0xff;
> @@ -296,15 +316,61 @@ uint32_t qemu_edid_dpi_to_mm(uint32_t dpi, uint32_t res)
>      return res * 254 / 10 / dpi;
>  }
>
> +static void dummy_displayid(uint8_t *did)
> +{
> +    did[0] = 0x70; /* display id extension */
> +    did[1] = 0x13; /* version 1.3 */
> +    did[2] = 4;    /* length */
> +    did[3] = 0x03; /* product type (0x03 == standalone display device) */
> +    edid_checksum(did + 1, did[2] + 4);
> +}
> +
> +static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate,
> +                                    uint32_t xres, uint32_t yres,
> +                                    uint32_t xmm, uint32_t ymm)
> +{
> +    Timings timings;
> +    generate_timings(&timings, refresh_rate, xres, yres);
> +
> +    did[0] = 0x70; /* display id extension */
> +    did[1] = 0x13; /* version 1.3 */
> +    did[2] = 23;   /* length */
> +    did[3] = 0x03; /* product type (0x03 == standalone display device) */
> +
> +    did[5] = 0x03; /* Detailed Timings Data Block */
> +    did[6] = 0x00; /* revision */
> +    did[7] = 0x14; /* block length */
> +
> +    did[8]  = timings.clock  & 0xff;
> +    did[9]  = (timings.clock & 0xff00) >> 8;
> +    did[10] = (timings.clock & 0xff0000) >> 16;
> +
> +    did[11] = 0x88; /* leave aspect ratio undefined */
> +
> +    stw_le_p(did + 12, 0xffff & (xres - 1));
> +    stw_le_p(did + 14, 0xffff & (timings.xblank - 1));
> +    stw_le_p(did + 16, 0xffff & (timings.xfront - 1));
> +    stw_le_p(did + 18, 0xffff & (timings.xsync - 1));
> +
> +    stw_le_p(did + 20, 0xffff & (yres - 1));
> +    stw_le_p(did + 22, 0xffff & (timings.yblank - 1));
> +    stw_le_p(did + 24, 0xffff & (timings.yfront - 1));
> +    stw_le_p(did + 26, 0xffff & (timings.ysync - 1));
> +
> +    edid_checksum(did + 1, did[2] + 4);
> +}
> +
>  void qemu_edid_generate(uint8_t *edid, size_t size,
>                          qemu_edid_info *info)
>  {
>      uint32_t desc = 54;
>      uint8_t *xtra3 = NULL;
>      uint8_t *dta = NULL;
> +    uint8_t *did = NULL;
>      uint32_t width_mm, height_mm;
>      uint32_t refresh_rate = info->refresh_rate ? info->refresh_rate : 75000;
>      uint32_t dpi = 100; /* if no width_mm/height_mm */
> +    uint32_t large_screen = 0;
>
>      /* =============== set defaults  =============== */
>
> @@ -320,6 +386,9 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
>      if (!info->prefy) {
>          info->prefy = 768;
>      }
> +    if (info->prefx >= 4096 || info->prefy >= 4096) {
> +        large_screen = 1;
> +    }
>      if (info->width_mm && info->height_mm) {
>          width_mm = info->width_mm;
>          height_mm = info->height_mm;
> @@ -401,9 +470,12 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
>
>      /* =============== descriptor blocks =============== */
>
> -    edid_desc_timing(edid + desc, refresh_rate, info->prefx, info->prefy,
> -                     width_mm, height_mm);
> -    desc += 18;
> +    /* The DTD section has only 12 bits to store the resolution */
> +    if (!large_screen) {
> +        edid_desc_timing(edid + desc, refresh_rate, info->prefx, info->prefy,
> +                         width_mm, height_mm);
> +        desc += 18;
> +    }
>
>      edid_desc_ranges(edid + desc);
>      desc += 18;
> @@ -429,12 +501,28 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
>          desc += 18;
>      }
>
> +    /* =============== display id extensions =============== */
> +
> +    if (size >= 384) {
> +        did = edid + 256;
> +        dummy_displayid(did);
> +        edid[126]++;
> +
> +        if (large_screen) {
> +            qemu_displayid_generate(did, refresh_rate, info->prefx, info->prefy,
> +                                    width_mm, height_mm);
> +        }
> +    }
> +
>      /* =============== finish up =============== */
>
>      edid_fill_modes(edid, xtra3, dta, info->maxx, info->maxy);
> -    edid_checksum(edid);
> +    edid_checksum(edid, 127);
>      if (dta) {
> -        edid_checksum(dta);
> +        edid_checksum(dta, 127);
> +    }
> +    if (did) {
> +        edid_checksum(did, 127);
>      }
>  }
>
> diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
> index 48d29630ab7..62fb5c38c1f 100644
> --- a/hw/display/vga-pci.c
> +++ b/hw/display/vga-pci.c
> @@ -49,7 +49,7 @@ struct PCIVGAState {
>      qemu_edid_info edid_info;
>      MemoryRegion mmio;
>      MemoryRegion mrs[4];
> -    uint8_t edid[256];
> +    uint8_t edid[384];
>  };
>
>  #define TYPE_PCI_VGA "pci-vga"
> diff --git a/qemu-edid.c b/qemu-edid.c
> index 1cd6a951723..028f2d181a1 100644
> --- a/qemu-edid.c
> +++ b/qemu-edid.c
> @@ -41,7 +41,7 @@ static void usage(FILE *out)
>  int main(int argc, char *argv[])
>  {
>      FILE *outfile = NULL;
> -    uint8_t blob[256];
> +    uint8_t blob[384];
>      uint32_t dpi = 100;
>      int rc;
>
> --
> 2.24.3 (Apple Git-128)
>

It looks good to me.

Reviewed-by: Akihiko Odaki <akihiko.odaki@gmail.com>
Gerd Hoffmann March 15, 2021, 7:44 a.m. UTC | #2
> +typedef struct Timings {

> +static void generate_timings(Timings *timings, uint32_t refresh_rate,
> +                             uint32_t xres, uint32_t yres)

Adding these should be splitted to a separate patch.

> -static void edid_checksum(uint8_t *edid)
> +static void edid_checksum(uint8_t *edid, size_t len)

Why this change?  Also a good candidate for a separate patch.

> +    if (size >= 384) {
> +        did = edid + 256;

"if (size >= 384 && large_screen)" ?
Also setting did should be next to setting dta.


       if (did) {
> +        dummy_displayid(did);

init_displayid() ?

Especially if we generate that only in case we actually have a large
screen so we never have an empty extension section?

take care,
  Gerd
mail@knazarov.com March 15, 2021, 9:23 a.m. UTC | #3
The change to edid_checksum is needed because the DisplayID section has another checksum inside for the actually used part of its 128-byte extension block. The checksum in this case uses the same algorithm, but for a shorter block. Thus I added a parameter to specify the size of the block.

I'll address the rest of your comments in a 3-rd version of this patch.

> On 15 Mar 2021, at 10:44, Gerd Hoffmann <kraxel@redhat.com> wrote:
> 
>> +typedef struct Timings {
> 
>> +static void generate_timings(Timings *timings, uint32_t refresh_rate,
>> +                             uint32_t xres, uint32_t yres)
> 
> Adding these should be splitted to a separate patch.
> 
>> -static void edid_checksum(uint8_t *edid)
>> +static void edid_checksum(uint8_t *edid, size_t len)
> 
> Why this change?  Also a good candidate for a separate patch.
> 
>> +    if (size >= 384) {
>> +        did = edid + 256;
> 
> "if (size >= 384 && large_screen)" ?
> Also setting did should be next to setting dta.
> 
> 
>       if (did) {
>> +        dummy_displayid(did);
> 
> init_displayid() ?
> 
> Especially if we generate that only in case we actually have a large
> screen so we never have an empty extension section?
> 
> take care,
>  Gerd
>
diff mbox series

Patch

diff --git a/hw/display/edid-generate.c b/hw/display/edid-generate.c
index b0ce583d436..db1e319e2dc 100644
--- a/hw/display/edid-generate.c
+++ b/hw/display/edid-generate.c
@@ -44,6 +44,35 @@  static const struct edid_mode {
     { .xres =  640,   .yres =  480,   .byte  = 35,   .bit = 5 },
 };
 
+typedef struct Timings {
+    uint32_t xfront;
+    uint32_t xsync;
+    uint32_t xblank;
+
+    uint32_t yfront;
+    uint32_t ysync;
+    uint32_t yblank;
+
+    uint64_t clock;
+} Timings;
+
+static void generate_timings(Timings *timings, uint32_t refresh_rate,
+                             uint32_t xres, uint32_t yres)
+{
+    /* pull some realistic looking timings out of thin air */
+    timings->xfront = xres * 25 / 100;
+    timings->xsync  = xres *  3 / 100;
+    timings->xblank = xres * 35 / 100;
+
+    timings->yfront = yres *  5 / 1000;
+    timings->ysync  = yres *  5 / 1000;
+    timings->yblank = yres * 35 / 1000;
+
+    timings->clock  = ((uint64_t)refresh_rate *
+                       (xres + timings->xblank) *
+                       (yres + timings->yblank)) / 10000000;
+}
+
 static void edid_ext_dta(uint8_t *dta)
 {
     dta[0] = 0x02;
@@ -129,17 +158,17 @@  static void edid_fill_modes(uint8_t *edid, uint8_t *xtra3, uint8_t *dta,
     }
 }
 
-static void edid_checksum(uint8_t *edid)
+static void edid_checksum(uint8_t *edid, size_t len)
 {
     uint32_t sum = 0;
     int i;
 
-    for (i = 0; i < 127; i++) {
+    for (i = 0; i < len; i++) {
         sum += edid[i];
     }
     sum &= 0xff;
     if (sum) {
-        edid[127] = 0x100 - sum;
+        edid[len] = 0x100 - sum;
     }
 }
 
@@ -180,8 +209,8 @@  static void edid_desc_ranges(uint8_t *desc)
     desc[7] =  30;
     desc[8] = 160;
 
-    /* max dot clock (1200 MHz) */
-    desc[9] = 1200 / 10;
+    /* max dot clock (2550 MHz) */
+    desc[9] = 2550 / 10;
 
     /* no extended timing information */
     desc[10] = 0x01;
@@ -207,38 +236,29 @@  static void edid_desc_timing(uint8_t *desc, uint32_t refresh_rate,
                              uint32_t xres, uint32_t yres,
                              uint32_t xmm, uint32_t ymm)
 {
-    /* pull some realistic looking timings out of thin air */
-    uint32_t xfront = xres * 25 / 100;
-    uint32_t xsync  = xres *  3 / 100;
-    uint32_t xblank = xres * 35 / 100;
-
-    uint32_t yfront = yres *  5 / 1000;
-    uint32_t ysync  = yres *  5 / 1000;
-    uint32_t yblank = yres * 35 / 1000;
-
-    uint64_t clock  = (uint64_t)refresh_rate * (xres + xblank) * (yres + yblank);
-
-    stl_le_p(desc, clock / 10000000);
+    Timings timings;
+    generate_timings(&timings, refresh_rate, xres, yres);
+    stl_le_p(desc, timings.clock);
 
     desc[2] = xres   & 0xff;
-    desc[3] = xblank & 0xff;
+    desc[3] = timings.xblank & 0xff;
     desc[4] = (((xres   & 0xf00) >> 4) |
-               ((xblank & 0xf00) >> 8));
+               ((timings.xblank & 0xf00) >> 8));
 
     desc[5] = yres   & 0xff;
-    desc[6] = yblank & 0xff;
+    desc[6] = timings.yblank & 0xff;
     desc[7] = (((yres   & 0xf00) >> 4) |
-               ((yblank & 0xf00) >> 8));
+               ((timings.yblank & 0xf00) >> 8));
 
-    desc[8] = xfront & 0xff;
-    desc[9] = xsync  & 0xff;
+    desc[8] = timings.xfront & 0xff;
+    desc[9] = timings.xsync  & 0xff;
 
-    desc[10] = (((yfront & 0x00f) << 4) |
-                ((ysync  & 0x00f) << 0));
-    desc[11] = (((xfront & 0x300) >> 2) |
-                ((xsync  & 0x300) >> 4) |
-                ((yfront & 0x030) >> 2) |
-                ((ysync  & 0x030) >> 4));
+    desc[10] = (((timings.yfront & 0x00f) << 4) |
+                ((timings.ysync  & 0x00f) << 0));
+    desc[11] = (((timings.xfront & 0x300) >> 2) |
+                ((timings.xsync  & 0x300) >> 4) |
+                ((timings.yfront & 0x030) >> 2) |
+                ((timings.ysync  & 0x030) >> 4));
 
     desc[12] = xmm & 0xff;
     desc[13] = ymm & 0xff;
@@ -296,15 +316,61 @@  uint32_t qemu_edid_dpi_to_mm(uint32_t dpi, uint32_t res)
     return res * 254 / 10 / dpi;
 }
 
+static void dummy_displayid(uint8_t *did)
+{
+    did[0] = 0x70; /* display id extension */
+    did[1] = 0x13; /* version 1.3 */
+    did[2] = 4;    /* length */
+    did[3] = 0x03; /* product type (0x03 == standalone display device) */
+    edid_checksum(did + 1, did[2] + 4);
+}
+
+static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate,
+                                    uint32_t xres, uint32_t yres,
+                                    uint32_t xmm, uint32_t ymm)
+{
+    Timings timings;
+    generate_timings(&timings, refresh_rate, xres, yres);
+
+    did[0] = 0x70; /* display id extension */
+    did[1] = 0x13; /* version 1.3 */
+    did[2] = 23;   /* length */
+    did[3] = 0x03; /* product type (0x03 == standalone display device) */
+
+    did[5] = 0x03; /* Detailed Timings Data Block */
+    did[6] = 0x00; /* revision */
+    did[7] = 0x14; /* block length */
+
+    did[8]  = timings.clock  & 0xff;
+    did[9]  = (timings.clock & 0xff00) >> 8;
+    did[10] = (timings.clock & 0xff0000) >> 16;
+
+    did[11] = 0x88; /* leave aspect ratio undefined */
+
+    stw_le_p(did + 12, 0xffff & (xres - 1));
+    stw_le_p(did + 14, 0xffff & (timings.xblank - 1));
+    stw_le_p(did + 16, 0xffff & (timings.xfront - 1));
+    stw_le_p(did + 18, 0xffff & (timings.xsync - 1));
+
+    stw_le_p(did + 20, 0xffff & (yres - 1));
+    stw_le_p(did + 22, 0xffff & (timings.yblank - 1));
+    stw_le_p(did + 24, 0xffff & (timings.yfront - 1));
+    stw_le_p(did + 26, 0xffff & (timings.ysync - 1));
+
+    edid_checksum(did + 1, did[2] + 4);
+}
+
 void qemu_edid_generate(uint8_t *edid, size_t size,
                         qemu_edid_info *info)
 {
     uint32_t desc = 54;
     uint8_t *xtra3 = NULL;
     uint8_t *dta = NULL;
+    uint8_t *did = NULL;
     uint32_t width_mm, height_mm;
     uint32_t refresh_rate = info->refresh_rate ? info->refresh_rate : 75000;
     uint32_t dpi = 100; /* if no width_mm/height_mm */
+    uint32_t large_screen = 0;
 
     /* =============== set defaults  =============== */
 
@@ -320,6 +386,9 @@  void qemu_edid_generate(uint8_t *edid, size_t size,
     if (!info->prefy) {
         info->prefy = 768;
     }
+    if (info->prefx >= 4096 || info->prefy >= 4096) {
+        large_screen = 1;
+    }
     if (info->width_mm && info->height_mm) {
         width_mm = info->width_mm;
         height_mm = info->height_mm;
@@ -401,9 +470,12 @@  void qemu_edid_generate(uint8_t *edid, size_t size,
 
     /* =============== descriptor blocks =============== */
 
-    edid_desc_timing(edid + desc, refresh_rate, info->prefx, info->prefy,
-                     width_mm, height_mm);
-    desc += 18;
+    /* The DTD section has only 12 bits to store the resolution */
+    if (!large_screen) {
+        edid_desc_timing(edid + desc, refresh_rate, info->prefx, info->prefy,
+                         width_mm, height_mm);
+        desc += 18;
+    }
 
     edid_desc_ranges(edid + desc);
     desc += 18;
@@ -429,12 +501,28 @@  void qemu_edid_generate(uint8_t *edid, size_t size,
         desc += 18;
     }
 
+    /* =============== display id extensions =============== */
+
+    if (size >= 384) {
+        did = edid + 256;
+        dummy_displayid(did);
+        edid[126]++;
+
+        if (large_screen) {
+            qemu_displayid_generate(did, refresh_rate, info->prefx, info->prefy,
+                                    width_mm, height_mm);
+        }
+    }
+
     /* =============== finish up =============== */
 
     edid_fill_modes(edid, xtra3, dta, info->maxx, info->maxy);
-    edid_checksum(edid);
+    edid_checksum(edid, 127);
     if (dta) {
-        edid_checksum(dta);
+        edid_checksum(dta, 127);
+    }
+    if (did) {
+        edid_checksum(did, 127);
     }
 }
 
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index 48d29630ab7..62fb5c38c1f 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -49,7 +49,7 @@  struct PCIVGAState {
     qemu_edid_info edid_info;
     MemoryRegion mmio;
     MemoryRegion mrs[4];
-    uint8_t edid[256];
+    uint8_t edid[384];
 };
 
 #define TYPE_PCI_VGA "pci-vga"
diff --git a/qemu-edid.c b/qemu-edid.c
index 1cd6a951723..028f2d181a1 100644
--- a/qemu-edid.c
+++ b/qemu-edid.c
@@ -41,7 +41,7 @@  static void usage(FILE *out)
 int main(int argc, char *argv[])
 {
     FILE *outfile = NULL;
-    uint8_t blob[256];
+    uint8_t blob[384];
     uint32_t dpi = 100;
     int rc;