diff mbox series

[2/3] ati-vga: Support unaligned access to hardware cursor registers

Message ID 1e658a7a7198a9ab10084bb85348e7d0a37a9055.1565907489.git.balaton@eik.bme.hu
State New
Headers show
Series ati-vga fixes for MacOS driver | expand

Commit Message

BALATON Zoltan Aug. 15, 2019, 10:18 p.m. UTC
This fixes horizontal mouse movement and pointer color with MacOS that
writes these registers with access size less than 4 so previously only
the last portion of access was effective overwriting previous partial
writes.

Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
---
 hw/display/ati.c | 87 +++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 58 insertions(+), 29 deletions(-)

Comments

Gerd Hoffmann Aug. 21, 2019, 9:08 a.m. UTC | #1
Hi,

> @@ -672,48 +678,71 @@ static void ati_mm_write(void *opaque, hwaddr addr,
>      case 0xf00 ... 0xfff:
>          /* read-only copy of PCI config space so ignore writes */
>          break;
> -    case CUR_OFFSET:
> -        if (s->regs.cur_offset != (data & 0x87fffff0)) {
> -            s->regs.cur_offset = data & 0x87fffff0;
> +    case CUR_OFFSET ... CUR_OFFSET + 3:
> +    {
> +        uint32_t t = s->regs.cur_offset;
> +
> +        ati_reg_write_offs(&t, addr - CUR_OFFSET, data, size);
> +        t &= 0x87fffff0;
> +        if (s->regs.cur_offset != t) {
> +            s->regs.cur_offset = t;

Repeated pattern.  I'd suggest to add a "wmask" parameter to
ati_reg_write_offs.  Maybe also make it return true/false depending
on whenever the value did change or not.

cheers,
  Gerd
BALATON Zoltan Aug. 21, 2019, 11:18 a.m. UTC | #2
Hello,

On Wed, 21 Aug 2019, Gerd Hoffmann wrote:
>> @@ -672,48 +678,71 @@ static void ati_mm_write(void *opaque, hwaddr addr,
>>      case 0xf00 ... 0xfff:
>>          /* read-only copy of PCI config space so ignore writes */
>>          break;
>> -    case CUR_OFFSET:
>> -        if (s->regs.cur_offset != (data & 0x87fffff0)) {
>> -            s->regs.cur_offset = data & 0x87fffff0;
>> +    case CUR_OFFSET ... CUR_OFFSET + 3:
>> +    {
>> +        uint32_t t = s->regs.cur_offset;
>> +
>> +        ati_reg_write_offs(&t, addr - CUR_OFFSET, data, size);
>> +        t &= 0x87fffff0;
>> +        if (s->regs.cur_offset != t) {
>> +            s->regs.cur_offset = t;
>
> Repeated pattern.  I'd suggest to add a "wmask" parameter to
> ati_reg_write_offs.  Maybe also make it return true/false depending
> on whenever the value did change or not.

This is a pattern in these HW cursor related regs but other callers of 
write_offs don't do that (currently there are one more of the CUR_* regs 
vs. others 5 to 4 but there may be other uses later as several regs 
support less than 32 bit access). It would also break symmetry between 
read_offs and write_offs so I think I'd leave this off write_offs for now 
unless new callers in the future will also need wmask. (It you insist I 
could make it a macro for CUR_* regs but not sure that would improve it 
much.)

Regards,
BALATON Zoltan
diff mbox series

Patch

diff --git a/hw/display/ati.c b/hw/display/ati.c
index 87ad18664d..5e2c4ba4aa 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -385,22 +385,28 @@  static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
     case 0xf00 ... 0xfff:
         val = pci_default_read_config(&s->dev, addr - 0xf00, size);
         break;
-    case CUR_OFFSET:
-        val = s->regs.cur_offset;
-        break;
-    case CUR_HORZ_VERT_POSN:
-        val = s->regs.cur_hv_pos;
-        val |= s->regs.cur_offset & BIT(31);
+    case CUR_OFFSET ... CUR_OFFSET + 3:
+        val = ati_reg_read_offs(s->regs.cur_offset, addr - CUR_OFFSET, size);
+        break;
+    case CUR_HORZ_VERT_POSN ... CUR_HORZ_VERT_POSN + 3:
+        val = ati_reg_read_offs(s->regs.cur_hv_pos,
+                                addr - CUR_HORZ_VERT_POSN, size);
+        if (addr + size > CUR_HORZ_VERT_POSN + 3) {
+            val |= (s->regs.cur_offset & BIT(31)) >> (4 - size);
+        }
         break;
-    case CUR_HORZ_VERT_OFF:
-        val = s->regs.cur_hv_offs;
-        val |= s->regs.cur_offset & BIT(31);
+    case CUR_HORZ_VERT_OFF ... CUR_HORZ_VERT_OFF + 3:
+        val = ati_reg_read_offs(s->regs.cur_hv_offs,
+                                addr - CUR_HORZ_VERT_OFF, size);
+        if (addr + size > CUR_HORZ_VERT_OFF + 3) {
+            val |= (s->regs.cur_offset & BIT(31)) >> (4 - size);
+        }
         break;
-    case CUR_CLR0:
-        val = s->regs.cur_color0;
+    case CUR_CLR0 ... CUR_CLR0 + 3:
+        val = ati_reg_read_offs(s->regs.cur_color0, addr - CUR_CLR0, size);
         break;
-    case CUR_CLR1:
-        val = s->regs.cur_color1;
+    case CUR_CLR1 ... CUR_CLR1 + 3:
+        val = ati_reg_read_offs(s->regs.cur_color1, addr - CUR_CLR1, size);
         break;
     case DST_OFFSET:
         val = s->regs.dst_offset;
@@ -672,48 +678,71 @@  static void ati_mm_write(void *opaque, hwaddr addr,
     case 0xf00 ... 0xfff:
         /* read-only copy of PCI config space so ignore writes */
         break;
-    case CUR_OFFSET:
-        if (s->regs.cur_offset != (data & 0x87fffff0)) {
-            s->regs.cur_offset = data & 0x87fffff0;
+    case CUR_OFFSET ... CUR_OFFSET + 3:
+    {
+        uint32_t t = s->regs.cur_offset;
+
+        ati_reg_write_offs(&t, addr - CUR_OFFSET, data, size);
+        t &= 0x87fffff0;
+        if (s->regs.cur_offset != t) {
+            s->regs.cur_offset = t;
             ati_cursor_define(s);
         }
         break;
-    case CUR_HORZ_VERT_POSN:
-        s->regs.cur_hv_pos = data & 0x3fff0fff;
-        if (data & BIT(31)) {
-            s->regs.cur_offset |= data & BIT(31);
+    }
+    case CUR_HORZ_VERT_POSN ... CUR_HORZ_VERT_POSN + 3:
+    {
+        uint32_t t = s->regs.cur_hv_pos | (s->regs.cur_offset & BIT(31));
+
+        ati_reg_write_offs(&t, addr - CUR_HORZ_VERT_POSN, data, size);
+        s->regs.cur_hv_pos = t & 0x3fff0fff;
+        if (t & BIT(31)) {
+            s->regs.cur_offset |= t & BIT(31);
         } else if (s->regs.cur_offset & BIT(31)) {
             s->regs.cur_offset &= ~BIT(31);
             ati_cursor_define(s);
         }
         if (!s->cursor_guest_mode &&
-            (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(data & BIT(31))) {
+            (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(t & BIT(31))) {
             dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16,
                           s->regs.cur_hv_pos & 0xffff, 1);
         }
         break;
+    }
     case CUR_HORZ_VERT_OFF:
-        s->regs.cur_hv_offs = data & 0x3f003f;
-        if (data & BIT(31)) {
-            s->regs.cur_offset |= data & BIT(31);
+    {
+        uint32_t t = s->regs.cur_hv_offs | (s->regs.cur_offset & BIT(31));
+
+        ati_reg_write_offs(&t, addr - CUR_HORZ_VERT_OFF, data, size);
+        s->regs.cur_hv_offs = t & 0x3f003f;
+        if (t & BIT(31)) {
+            s->regs.cur_offset |= t & BIT(31);
         } else if (s->regs.cur_offset & BIT(31)) {
             s->regs.cur_offset &= ~BIT(31);
             ati_cursor_define(s);
         }
         break;
-    case CUR_CLR0:
-        if (s->regs.cur_color0 != (data & 0xffffff)) {
-            s->regs.cur_color0 = data & 0xffffff;
+    }
+    case CUR_CLR0 ... CUR_CLR0 + 3:
+    {
+        uint32_t t = s->regs.cur_color0;
+
+        ati_reg_write_offs(&t, addr - CUR_CLR0, data, size);
+        t &= 0xffffff;
+        if (s->regs.cur_color0 != t) {
+            s->regs.cur_color0 = t;
             ati_cursor_define(s);
         }
         break;
-    case CUR_CLR1:
+    }
+    case CUR_CLR1 ... CUR_CLR1 + 3:
         /*
          * Update cursor unconditionally here because some clients set up
          * other registers before actually writing cursor data to memory at
          * offset so we would miss cursor change unless always updating here
          */
-        s->regs.cur_color1 = data & 0xffffff;
+        ati_reg_write_offs(&s->regs.cur_color1, addr - CUR_CLR1, data, size);
+        s->regs.cur_color1 &= 0xffffff;
         ati_cursor_define(s);
         break;
     case DST_OFFSET: