qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Chad Jablonski <chad@jablonski.xyz>
To: qemu-devel@nongnu.org
Cc: balaton@eik.bme.hu, Chad Jablonski <chad@jablonski.xyz>
Subject: [PATCH v2 7/7] ati-vga: Implement HOST_DATA blit source with color expansion
Date: Sun,  2 Nov 2025 22:36:08 -0500	[thread overview]
Message-ID: <20251103033608.120908-8-chad@jablonski.xyz> (raw)
In-Reply-To: <20251103033608.120908-1-chad@jablonski.xyz>

SRCCOPY blits using 1bpp HOST_DATA as a source are expanded
to 32bpp ARGB. If pixman is enabled any additional color depth conversions
are handled. The fallback path does not yet support color conversion
and logs an error.

Unlike VRAM sourced blits, host data blits are not triggered by writing
to the dst width registers. They're only triggered on HOST_DATA_LAST
writes.

Supports MONO_FRGD_BKGD and COLOR datatypes. MONO_FRGD (transparent
background) is left for future work and logged.
GMC_SRC_SOURCE_HOST_DATA_ALIGNED is unimplemented and also logged.
Neither of these are used for xterm text rendering.

This combines clipping, host data transfers and color expansion to
enable text rendering in xterm under X.org.

Signed-off-by: Chad Jablonski <chad@jablonski.xyz>
---
 hw/display/ati.c      |  28 +++++++--
 hw/display/ati_2d.c   | 135 +++++++++++++++++++++++++++++++++++-------
 hw/display/ati_regs.h |  13 ++++
 3 files changed, 152 insertions(+), 24 deletions(-)

diff --git a/hw/display/ati.c b/hw/display/ati.c
index 0a686750ae..6ec50279ed 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -813,9 +813,14 @@ static void ati_mm_write(void *opaque, hwaddr addr,
         }
         break;
     case DST_WIDTH:
+    {
+        uint32_t src = s->regs.dp_gui_master_cntl & GMC_SRC_SOURCE_MASK;
         s->regs.dst_width = data & 0x3fff;
-        ati_2d_blt(s);
+        if (src != GMC_SRC_SOURCE_HOST_DATA) {
+            ati_2d_blt(s);
+        }
         break;
+    }
     case DST_HEIGHT:
         s->regs.dst_height = data & 0x3fff;
         break;
@@ -862,10 +867,15 @@ static void ati_mm_write(void *opaque, hwaddr addr,
         s->regs.dst_y = (data >> 16) & 0x3fff;
         break;
     case DST_HEIGHT_WIDTH:
+    {
+        uint32_t src = s->regs.dp_gui_master_cntl & GMC_SRC_SOURCE_MASK;
         s->regs.dst_width = data & 0x3fff;
         s->regs.dst_height = (data >> 16) & 0x3fff;
-        ati_2d_blt(s);
+        if (src != GMC_SRC_SOURCE_HOST_DATA) {
+            ati_2d_blt(s);
+        }
         break;
+    }
     case DP_GUI_MASTER_CNTL:
         s->regs.dp_gui_master_cntl = data & 0xff00000f;
         s->regs.dp_datatype = (data & 0x0f00) >> 8 | (data & 0x30f0) << 4 |
@@ -881,10 +891,15 @@ static void ati_mm_write(void *opaque, hwaddr addr,
         }
         break;
     case DST_WIDTH_X:
+    {
+        uint32_t src = s->regs.dp_gui_master_cntl & GMC_SRC_SOURCE_MASK;
         s->regs.dst_x = data & 0x3fff;
         s->regs.dst_width = (data >> 16) & 0x3fff;
-        ati_2d_blt(s);
+        if (src != GMC_SRC_SOURCE_HOST_DATA) {
+            ati_2d_blt(s);
+        }
         break;
+    }
     case SRC_X_Y:
         s->regs.src_y = data & 0x3fff;
         s->regs.src_x = (data >> 16) & 0x3fff;
@@ -894,10 +909,15 @@ static void ati_mm_write(void *opaque, hwaddr addr,
         s->regs.dst_x = (data >> 16) & 0x3fff;
         break;
     case DST_WIDTH_HEIGHT:
+    {
+        uint32_t src = s->regs.dp_gui_master_cntl & GMC_SRC_SOURCE_MASK;
         s->regs.dst_height = data & 0x3fff;
         s->regs.dst_width = (data >> 16) & 0x3fff;
-        ati_2d_blt(s);
+        if (src != GMC_SRC_SOURCE_HOST_DATA) {
+            ati_2d_blt(s);
+        }
         break;
+    }
     case DST_HEIGHT_Y:
         s->regs.dst_y = data & 0x3fff;
         s->regs.dst_height = (data >> 16) & 0x3fff;
diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c
index 181bf634f0..c177686338 100644
--- a/hw/display/ati_2d.c
+++ b/hw/display/ati_2d.c
@@ -25,6 +25,9 @@
  * possible.
  */
 
+#define DEFAULT_CNTL (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL)
+#define EXPANDED_SRC_BPP 32
+
 static int ati_bpp_from_datatype(ATIVGAState *s)
 {
     switch (s->regs.dp_datatype & 0xf) {
@@ -44,7 +47,6 @@ static int ati_bpp_from_datatype(ATIVGAState *s)
     }
 }
 
-#define DEFAULT_CNTL (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL)
 /* Convert 1bpp monochrome data to 32bpp ARGB using color expansion */
 static void expand_colors(uint8_t *color_dst, const uint8_t *mono_src,
                           uint32_t width, uint32_t height,
@@ -139,30 +141,112 @@ void ati_2d_blt(ATIVGAState *s)
     switch (s->regs.dp_mix & GMC_ROP3_MASK) {
     case ROP3_SRCCOPY:
     {
+        uint32_t src = s->regs.dp_gui_master_cntl & GMC_SRC_SOURCE_MASK;
+        uint8_t *color_data = NULL;
         bool fallback = false;
-        unsigned src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
-                         s->regs.src_x + clip_left :
-                         s->regs.src_x + 1 - dst.width + clip_left);
-        unsigned src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
-                         s->regs.src_y + clip_top :
-                         s->regs.src_y + 1 - dst.height + clip_top);
-        int src_stride = DEFAULT_CNTL ?
+        unsigned src_x, src_y, src_stride, src_bpp;
+        uint8_t *src_bits;
+
+        switch (src) {
+        case GMC_SRC_SOURCE_HOST_DATA:
+        {
+            unsigned src_datatype = s->regs.dp_gui_master_cntl &
+                                    GMC_SRC_DATATYPE_MASK;
+            switch (src_datatype) {
+            case GMC_SRC_DATATYPE_MONO_FRGD_BKGD:
+            {
+                bool lsb_to_msb = s->regs.dp_gui_master_cntl &
+                                  GMC_BYTE_ORDER_LSB_TO_MSB;
+                /* Monochrome source is 1 bpp aligned to 32-bit rows */
+                uint32_t mono_size = ((dst.width + 31) / 32) * 4 * dst.height;
+                if (s->host_data_pos < mono_size) {
+                    qemu_log_mask(LOG_UNIMP,
+                                  "HOST_DATA blit requires %u bytes, buffer holds %u "
+                                  "(increase buffer size)\n",
+                                  mono_size, s->host_data_pos);
+                    return;
+                }
+
+                /* Expand all of the source, clipping will be applied later */
+                color_data = g_malloc(dst.width * dst.height *
+                                      sizeof(uint32_t));
+                src_bpp = EXPANDED_SRC_BPP;
+                expand_colors(color_data, s->host_data_buffer,
+                              dst.width, dst.height, s->regs.dp_src_frgd_clr,
+                              s->regs.dp_src_bkgd_clr, lsb_to_msb);
+                break;
+            }
+            case GMC_SRC_DATATYPE_COLOR:
+            {
+                uint32_t color_size = dst.width * dst.height * (bpp / 8);
+                if (s->host_data_pos < color_size) {
+                    qemu_log_mask(LOG_UNIMP,
+                                  "HOST_DATA blit requires %u bytes, buffer holds %u "
+                                  "(increase buffer size)\n",
+                                  color_size, s->host_data_pos);
+                    return;
+                }
+                /*
+                 * The rage128 register guide states that the bit depth in this
+                 * case matches the bit depth of the dst. There is no
+                 * independent bit depth register for the src.
+                 */
+                src_bpp = bpp;
+                color_data = s->host_data_buffer;
+                break;
+            }
+            case GMC_SRC_DATATYPE_MONO_FRGD:
+                qemu_log_mask(LOG_UNIMP, "ati_2d blt source datatype "
+                              "MONO_FRGD (leave-alone) not yet supported\n");
+                return;
+            default:
+                qemu_log_mask(LOG_UNIMP, "ati_2d blt source datatype %x is "
+                              "not yet supported\n", src_datatype);
+                return;
+            }
+
+            src_x = clip_left;
+            src_y = clip_top;
+            src_stride = dst.width * (src_bpp / 8);
+            src_bits = color_data;
+            s->host_data_pos = 0;
+            break;
+        }
+        case GMC_SRC_SOURCE_MEMORY:
+        {
+            src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
+                     s->regs.src_x + clip_left :
+                     s->regs.src_x + 1 - dst.width + clip_left);
+            src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
+                     s->regs.src_y + clip_top :
+                     s->regs.src_y + 1 - dst.height + clip_top);
+            src_stride = DEFAULT_CNTL ?
                          s->regs.src_pitch : s->regs.default_pitch;
-        if (!src_stride) {
-            qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n");
+            src_bpp = bpp;
+            src_bits = s->vga.vram_ptr + (DEFAULT_CNTL ?
+                       s->regs.src_offset : s->regs.default_offset);
+            if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
+                src_bits += s->regs.crtc_offset & 0x07ffffff;
+                src_stride *= bpp;
+            }
+            if (src_x > 0x3fff || src_y > 0x3fff || src_bits >= end
+                || src_bits + src_x
+                 + (src_y + clipped.height) * src_stride >= end) {
+                qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
+                return;
+            }
+            break;
+        }
+        case GMC_SRC_SOURCE_HOST_DATA_ALIGNED:
+        default:
+            qemu_log_mask(LOG_UNIMP, "ati_2d blt source %x is not "
+                          "yet supported\n", src);
             return;
         }
-        uint8_t *src_bits = s->vga.vram_ptr + (DEFAULT_CNTL ?
-                            s->regs.src_offset : s->regs.default_offset);
 
-        if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
-            src_bits += s->regs.crtc_offset & 0x07ffffff;
-            src_stride *= bpp;
-        }
-        if (src_x > 0x3fff || src_y > 0x3fff || src_bits >= end
-            || src_bits + src_x
-             + (src_y + clipped.height) * src_stride >= end) {
-            qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
+        if (!src_stride) {
+            qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n");
+            g_free(color_data);
             return;
         }
 
@@ -207,6 +291,15 @@ void ati_2d_blt(ATIVGAState *s)
             unsigned int src_pitch = src_stride * sizeof(uint32_t);
             unsigned int dst_pitch = dst_stride * sizeof(uint32_t);
 
+            if (src_bpp != bpp) {
+                qemu_log_mask(LOG_UNIMP,
+                              "Mismatched bit depths not yet supported "
+                              "in the fallback (non-pixman) implementation. "
+                              "src: %d != dst: %d\n", src_bpp, bpp);
+                g_free(color_data);
+                return;
+            }
+
             for (y = 0; y < clipped.height; y++) {
                 i = clipped.x * bypp;
                 j = src_x * bypp;
@@ -232,6 +325,8 @@ void ati_2d_blt(ATIVGAState *s)
                          clipped.x + clipped.width : clipped.x);
         s->regs.dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
                          clipped.y + clipped.height : clipped.y);
+
+        g_free(color_data);
         break;
     }
     case ROP3_PATCOPY:
diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h
index 9b52b61dcb..b741c3c6b8 100644
--- a/hw/display/ati_regs.h
+++ b/hw/display/ati_regs.h
@@ -404,6 +404,7 @@
 #define GMC_BRUSH_SOLIDCOLOR                    0x000000d0
 #define GMC_SRC_DSTCOLOR                        0x00003000
 #define GMC_BYTE_ORDER_MSB_TO_LSB               0x00000000
+#define GMC_BYTE_ORDER_LSB_TO_MSB               0x00004000
 #define GMC_DP_SRC_RECT                         0x02000000
 #define GMC_3D_FCN_EN_CLR                       0x00000000
 #define GMC_AUX_CLIP_CLEAR                      0x20000000
@@ -421,6 +422,18 @@
 #define GMC_DST_CLIP_DEFAULT                    0x00000000
 #define GMC_DST_CLIP_LEAVE_ALONE                0x00000008
 
+/* DP_GUI_MASTER_CNTL DP_SRC_DATATYPE named constants */
+#define GMC_SRC_DATATYPE_MASK                   0x00003000
+#define GMC_SRC_DATATYPE_MONO_FRGD_BKGD         0x00000000
+#define GMC_SRC_DATATYPE_MONO_FRGD              0x00001000
+#define GMC_SRC_DATATYPE_COLOR                  0x00003000
+
+/* DP_GUI_MASTER_CNTL DP_SRC_SOURCE named constants */
+#define GMC_SRC_SOURCE_MASK                     0x07000000
+#define GMC_SRC_SOURCE_MEMORY                   0x02000000
+#define GMC_SRC_SOURCE_HOST_DATA                0x03000000
+#define GMC_SRC_SOURCE_HOST_DATA_ALIGNED        0x04000000
+
 /* DP_GUI_MASTER_CNTL ROP3 named constants */
 #define GMC_ROP3_MASK                           0x00ff0000
 #define ROP3_BLACKNESS                          0x00000000
-- 
2.51.0



      parent reply	other threads:[~2025-11-03  3:38 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-03  3:36 [PATCH v2 0/7] ati-vga: Implement HOST_DATA transfers to enable X.org text rendering Chad Jablonski
2025-11-03  3:36 ` [PATCH v2 1/7] ati-vga: Add scissor clipping register support Chad Jablonski
2025-11-03 13:16   ` BALATON Zoltan
2025-11-04 18:22     ` Chad Jablonski
2025-11-03  3:36 ` [PATCH v2 2/7] ati-vga: Implement scissor rectangle clipping for 2D operations Chad Jablonski
2025-11-03 13:29   ` BALATON Zoltan
2025-11-05 19:29     ` Chad Jablonski
2025-11-03  3:36 ` [PATCH v2 3/7] ati-vga: Implement foreground and background color register writes Chad Jablonski
2025-11-03 13:33   ` BALATON Zoltan
2025-11-03  3:36 ` [PATCH v2 4/7] ati-vga: Fix DP_GUI_MASTER_CNTL register mask Chad Jablonski
2025-11-03 13:46   ` BALATON Zoltan
2025-11-03  3:36 ` [PATCH v2 5/7] ati-vga: Implement HOST_DATA register writes Chad Jablonski
2025-11-03 13:58   ` BALATON Zoltan
2025-11-04 11:26     ` BALATON Zoltan
2025-11-10 19:53       ` Chad Jablonski
2025-11-03  3:36 ` [PATCH v2 6/7] ati-vga: Add expand_colors() helper for monochrome color expansion Chad Jablonski
2025-11-03 14:03   ` BALATON Zoltan
2025-11-10 20:20     ` Chad Jablonski
2025-11-03  3:36 ` Chad Jablonski [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251103033608.120908-8-chad@jablonski.xyz \
    --to=chad@jablonski.xyz \
    --cc=balaton@eik.bme.hu \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).