[PATCH v2 7/7] ati-vga: Implement PM4_STAT register and common flags

Chad Jablonski posted 7 patches 6 days, 5 hours ago
[PATCH v2 7/7] ati-vga: Implement PM4_STAT register and common flags
Posted by Chad Jablonski 6 days, 5 hours ago
The PM4_STAT register largely mirrors the flags of the GUI_STAT
register. The exception is that the low bits of PM4_STAT reflect the
remaining slots in the CCE FIFO instead of the GUI FIFO.

This implements a very incomplete set of common flags
(MICRO_BUSY and GUI_ACTIVE) and the PM4_STAT register.

Signed-off-by: Chad Jablonski <chad@jablonski.xyz>
---
 hw/display/ati.c      | 16 +++++++++++++++-
 hw/display/ati_cce.c  | 38 ++++++++++++++++++++++++++++++++++++++
 hw/display/ati_cce.h  |  3 +++
 hw/display/ati_regs.h |  1 +
 4 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/hw/display/ati.c b/hw/display/ati.c
index 9edde2b0bc..3f31a76b72 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -276,6 +276,16 @@ static inline uint64_t ati_reg_read_offs(uint32_t reg, int offs,
     }
 }
 
+static uint32_t ati_common_stat(ATIVGAState *s)
+{
+    /* TODO: This is _very_ naive. It will evolve. */
+    uint32_t micro_busy = ati_cce_micro_busy(&s->cce.cur_packet) ?
+                          MICRO_BUSY : 0;
+    /* GUI_ACTIVE is the OR of all other status flags */
+    uint32_t gui_active = micro_busy ? GUI_ACTIVE : 0;
+    return gui_active | micro_busy;
+}
+
 static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
 {
     ATIVGAState *s = opaque;
@@ -383,7 +393,7 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
         break;
     case RBBM_STATUS:
     case GUI_STAT:
-        val = 64; /* free CMDFIFO entries */
+        val = ati_common_stat(s) | 64; /* free CMDFIFO entries */
         break;
     case CRTC_H_TOTAL_DISP:
         val = s->regs.crtc_h_total_disp;
@@ -543,6 +553,10 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
     case PM4_MICRO_CNTL:
         val = s->cce.freerun ? PM4_MICRO_FREERUN : 0;
         break;
+    case PM4_STAT: {
+        val = ati_common_stat(s) | ati_cce_fifo_cnt(&s->cce);
+        break;
+    }
     default:
         break;
     }
diff --git a/hw/display/ati_cce.c b/hw/display/ati_cce.c
index 62a88a54df..3d35b0e857 100644
--- a/hw/display/ati_cce.c
+++ b/hw/display/ati_cce.c
@@ -11,6 +11,24 @@
 #include "ati_int.h"
 #include "trace.h"
 
+static uint32_t
+ati_cce_fifo_max(uint8_t mode)
+{
+    switch (mode) {
+    case PM4_BUFFER_CNTL_NONPM4...PM4_BUFFER_CNTL_192BM:
+        return 192;
+    case PM4_BUFFER_CNTL_128PIO_64INDBM...PM4_BUFFER_CNTL_128BM_64INDBM:
+        return 128;
+    case PM4_BUFFER_CNTL_64PIO_128INDBM...PM4_BUFFER_CNTL_64PIO_64VCBM_64INDBM:
+        /* fall through */
+    case PM4_BUFFER_CNTL_64PIO_64VCPIO_64INPIO:
+        return 64;
+    default:
+        /* Undocumented but testing shows this returns 192 otherwise */
+        return 192;
+    }
+}
+
 static inline uint32_t
 ati_cce_data_packets_remaining(const ATIPM4PacketState *p)
 {
@@ -154,3 +172,23 @@ ati_cce_receive_data(ATIVGAState *s, uint32_t data)
     }
     ati_cce_process_packet_data(s, data);
 }
+
+bool
+ati_cce_micro_busy(const ATIPM4PacketState *p)
+{
+    uint32_t remaining = ati_cce_data_packets_remaining(p);
+    if (remaining > 0) {
+        return true;
+    }
+    return false;
+}
+
+uint32_t
+ati_cce_fifo_cnt(const ATICCEState *c)
+{
+    /*
+     * This should return the available slots. Given that commands are
+     * processed immediately this returns the fifo max for now.
+     */
+    return ati_cce_fifo_max(c->buffer_mode);
+}
diff --git a/hw/display/ati_cce.h b/hw/display/ati_cce.h
index b6ad21f47e..12aede6ecc 100644
--- a/hw/display/ati_cce.h
+++ b/hw/display/ati_cce.h
@@ -85,4 +85,7 @@ typedef struct ATICCEState {
 } ATICCEState;
 
 void ati_cce_receive_data(ATIVGAState *s, uint32_t data);
+bool ati_cce_micro_busy(const ATIPM4PacketState *p);
+uint32_t ati_cce_fifo_cnt(const ATICCEState *c);
+
 #endif /* ATI_CCE_H */
diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h
index d7118449f5..841019ac7b 100644
--- a/hw/display/ati_regs.h
+++ b/hw/display/ati_regs.h
@@ -280,6 +280,7 @@
 
 /* CONSTANTS */
 #define GUI_ACTIVE                              0x80000000
+#define MICRO_BUSY                              0x00020000
 #define ENGINE_IDLE                             0x0
 
 #define PLL_WR_EN                               0x00000080
-- 
2.51.2
Re: [PATCH v2 7/7] ati-vga: Implement PM4_STAT register and common flags
Posted by BALATON Zoltan 5 days, 18 hours ago
On Wed, 31 Dec 2025, Chad Jablonski wrote:
> The PM4_STAT register largely mirrors the flags of the GUI_STAT
> register. The exception is that the low bits of PM4_STAT reflect the
> remaining slots in the CCE FIFO instead of the GUI FIFO.
>
> This implements a very incomplete set of common flags
> (MICRO_BUSY and GUI_ACTIVE) and the PM4_STAT register.
>
> Signed-off-by: Chad Jablonski <chad@jablonski.xyz>
> ---
> hw/display/ati.c      | 16 +++++++++++++++-
> hw/display/ati_cce.c  | 38 ++++++++++++++++++++++++++++++++++++++
> hw/display/ati_cce.h  |  3 +++
> hw/display/ati_regs.h |  1 +
> 4 files changed, 57 insertions(+), 1 deletion(-)
>
> diff --git a/hw/display/ati.c b/hw/display/ati.c
> index 9edde2b0bc..3f31a76b72 100644
> --- a/hw/display/ati.c
> +++ b/hw/display/ati.c
> @@ -276,6 +276,16 @@ static inline uint64_t ati_reg_read_offs(uint32_t reg, int offs,
>     }
> }
>
> +static uint32_t ati_common_stat(ATIVGAState *s)
> +{
> +    /* TODO: This is _very_ naive. It will evolve. */
> +    uint32_t micro_busy = ati_cce_micro_busy(&s->cce.cur_packet) ?
> +                          MICRO_BUSY : 0;
> +    /* GUI_ACTIVE is the OR of all other status flags */
> +    uint32_t gui_active = micro_busy ? GUI_ACTIVE : 0;
> +    return gui_active | micro_busy;
> +}
> +
> static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
> {
>     ATIVGAState *s = opaque;
> @@ -383,7 +393,7 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
>         break;
>     case RBBM_STATUS:
>     case GUI_STAT:
> -        val = 64; /* free CMDFIFO entries */
> +        val = ati_common_stat(s) | 64; /* free CMDFIFO entries */
>         break;
>     case CRTC_H_TOTAL_DISP:
>         val = s->regs.crtc_h_total_disp;
> @@ -543,6 +553,10 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
>     case PM4_MICRO_CNTL:
>         val = s->cce.freerun ? PM4_MICRO_FREERUN : 0;
>         break;
> +    case PM4_STAT: {
> +        val = ati_common_stat(s) | ati_cce_fifo_cnt(&s->cce);
> +        break;
> +    }
>     default:
>         break;
>     }
> diff --git a/hw/display/ati_cce.c b/hw/display/ati_cce.c
> index 62a88a54df..3d35b0e857 100644
> --- a/hw/display/ati_cce.c
> +++ b/hw/display/ati_cce.c
> @@ -11,6 +11,24 @@
> #include "ati_int.h"
> #include "trace.h"
>
> +static uint32_t
> +ati_cce_fifo_max(uint8_t mode)
> +{
> +    switch (mode) {
> +    case PM4_BUFFER_CNTL_NONPM4...PM4_BUFFER_CNTL_192BM:

You could move this down before default: then no separate case is needed 
for it as this seems to be the default and other cases are real cases.

> +        return 192;
> +    case PM4_BUFFER_CNTL_128PIO_64INDBM...PM4_BUFFER_CNTL_128BM_64INDBM:
> +        return 128;
> +    case PM4_BUFFER_CNTL_64PIO_128INDBM...PM4_BUFFER_CNTL_64PIO_64VCBM_64INDBM:
> +        /* fall through */

Comment not needed here.

Sorry that I cannot give more advice than such small style comments but I 
don't know these GPUs that well and have no time to dig into it so I can 
only give generic ideas and try to review it. I still need to check the 
host data series. Thanks a lot for this great contribution, keep up the 
good work.

Regards,
BALATON Zoltan

> +    case PM4_BUFFER_CNTL_64PIO_64VCPIO_64INPIO:
> +        return 64;
> +    default:
> +        /* Undocumented but testing shows this returns 192 otherwise */
> +        return 192;
> +    }
> +}
> +
> static inline uint32_t
> ati_cce_data_packets_remaining(const ATIPM4PacketState *p)
> {
> @@ -154,3 +172,23 @@ ati_cce_receive_data(ATIVGAState *s, uint32_t data)
>     }
>     ati_cce_process_packet_data(s, data);
> }
> +
> +bool
> +ati_cce_micro_busy(const ATIPM4PacketState *p)
> +{
> +    uint32_t remaining = ati_cce_data_packets_remaining(p);
> +    if (remaining > 0) {
> +        return true;
> +    }
> +    return false;
> +}
> +
> +uint32_t
> +ati_cce_fifo_cnt(const ATICCEState *c)
> +{
> +    /*
> +     * This should return the available slots. Given that commands are
> +     * processed immediately this returns the fifo max for now.
> +     */
> +    return ati_cce_fifo_max(c->buffer_mode);
> +}
> diff --git a/hw/display/ati_cce.h b/hw/display/ati_cce.h
> index b6ad21f47e..12aede6ecc 100644
> --- a/hw/display/ati_cce.h
> +++ b/hw/display/ati_cce.h
> @@ -85,4 +85,7 @@ typedef struct ATICCEState {
> } ATICCEState;
>
> void ati_cce_receive_data(ATIVGAState *s, uint32_t data);
> +bool ati_cce_micro_busy(const ATIPM4PacketState *p);
> +uint32_t ati_cce_fifo_cnt(const ATICCEState *c);
> +
> #endif /* ATI_CCE_H */
> diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h
> index d7118449f5..841019ac7b 100644
> --- a/hw/display/ati_regs.h
> +++ b/hw/display/ati_regs.h
> @@ -280,6 +280,7 @@
>
> /* CONSTANTS */
> #define GUI_ACTIVE                              0x80000000
> +#define MICRO_BUSY                              0x00020000
> #define ENGINE_IDLE                             0x0
>
> #define PLL_WR_EN                               0x00000080
>