>> +        if ((data & GMC_SRC_CLIPPING_MASK) == GMC_SRC_CLIP_DEFAULT) {
>> +            s->regs.src_sc_bottom_right = s->regs.default_sc_bottom_right;
>> +        }
>> +        if ((data & GMC_DST_CLIPPING_MASK) == GMC_DST_CLIP_DEFAULT) {
>> +            s->regs.sc_top_left = 0;
>> +            s->regs.sc_bottom_right = s->regs.default_sc_bottom_right;
>> +        }
>
> Or is this what you meant by style? Now I get that. I think the bits 
> should not reset the regs just cause the operation to use the default 
> values instead but if you can verify what actual hardware does that would 
> be best.

Hi BALATON,

I've tested this on the real 'Rage 128 Pro Ultra TF' and it shows that this is
the correct behavior (copying to the registers). I was definitely a bit
surprised!

I've run this test both with X running (idle over ssh) and prior to
starting X with the same results. I would like to write a bare-metal test
to make sure that the environment is a bit more controlled. But I struggled
with modesetting and it was looking like it was going to become a bit of a
rabbit hole. So I settled on testing under Linux for now and left that for
another day.

Here is the output:

Test SRC clipping
====================================

** Initializing DEFAULT_SC_BOTTOM_RIGHT to 0x0 **
** Initializing SRC_SC_BOTTOM to 0x0 **
** Initializing SRC_SC_RIGHT to 0x0 **

Initial State
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de00f
DEFAULT_SC_BOTTOM_RIGHT: 0x00000000
SRC_SC_BOTTOM:           0x00000000
SRC_SC_RIGHT:            0x00000000

** Setting DEFAULT_SC_BOTTOM_RIGHT to 0x0aaa0bbb **
** Setting SRC_SC_BOTTOM to 0x111 **
** Setting SRC_SC_RIGHT to 0x222 **

State After Setting
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de00f
DEFAULT_SC_BOTTOM_RIGHT: 0x0aaa0bbb
SRC_SC_BOTTOM:           0x00000111
SRC_SC_RIGHT:            0x00000222

** Setting GMC_SRC_CLIPPING to default **

State After Setting Default
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de00b
DEFAULT_SC_BOTTOM_RIGHT: 0x0aaa0bbb
SRC_SC_BOTTOM:           0x00000aaa  <======= Set to default
SRC_SC_RIGHT:            0x00000bbb  <======= Set to default

** Setting GMC_SRC_CLIPPING to leave alone **

State After Setting Leave Alone
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de00f
DEFAULT_SC_BOTTOM_RIGHT: 0x0aaa0bbb
SRC_SC_BOTTOM:           0x00000aaa  <======= STILL default
SRC_SC_RIGHT:            0x00000bbb  <======= STILL default

Test DST clipping
====================================

** Initializing DEFAULT_SC_BOTTOM_RIGHT to 0x0 **
** Initializing SC_BOTTOM to 0x0 **
** Initializing SC_RIGHT to 0x0 **
** Initializing SC_TOP to 0x0 **
** Initializing SC_LEFT to 0x0 **

Initial State
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de00f
DEFAULT_SC_BOTTOM_RIGHT: 0x00000000
SC_BOTTOM:               0x00000000
SC_RIGHT:                0x00000000
SC_TOP:                  0x00000000
SC_LEFT:                 0x00000000

** Setting DEFAULT_SC_BOTTOM_RIGHT to 0x0aaa0bbb **
** Setting SC_BOTTOM to 0x111 **
** Setting SC_RIGHT to 0x222 **
** SETTING SC_TOP to 0x333 **
** SETTING SC_LEFT to 0x444 **

State After Setting
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de00f
DEFAULT_SC_BOTTOM_RIGHT: 0x0aaa0bbb
SC_BOTTOM:               0x00000111
SC_RIGHT:                0x00000222
SC_TOP:                  0x00000333
SC_LEFT:                 0x00000444

** Setting GMC_DST_CLIPPING to default **

State After Setting Default
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de007
DEFAULT_SC_BOTTOM_RIGHT: 0x0aaa0bbb
SC_BOTTOM:               0x00000aaa  <======= Set to default
SC_RIGHT:                0x00000bbb  <======= Set to default
SC_TOP:                  0x00000000  <======= Set to default
SC_LEFT:                 0x00000000  <======= Set to default

** Setting GMC_DST_CLIPPING to leave alone **

State After Setting Leave Alone
------------------------------------
DEFAULT_GUI_MASTER_CNTL: 0x4a1de00f
DEFAULT_SC_BOTTOM_RIGHT: 0x0aaa0bbb
SC_BOTTOM:               0x00000aaa  <======= STILL default
SC_RIGHT:                0x00000bbb  <======= STILL default
SC_TOP:                  0x00000000  <======= STILL default
SC_LEFT:                 0x00000000  <======= STILL default


And the source:

===============================================================================

/*
 * ATI Rage 128 Pro Clipping Mode Hardware Test
 *
 * Tests whether clipping mode bits exhibit latching or dynamic behavior
 *
 * Build: gcc -std=c99 -o test test.c -lpci
 * Requirements: libpci-dev, root privileges, ATI Rage 128 Pro hardware
 * Note: Run with X.org idle (SSH session recommended)
 */
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <pci/pci.h>

#define ATI_VENDOR_ID            0x1002
#define MAX_ATI_DEVICES          10

#define DP_GUI_MASTER_CNTL       0x146c

#define SRC_SC_BOTTOM_RIGHT      0x16f4
#define SRC_SC_BOTTOM            0x165C
#define SRC_SC_RIGHT             0x1654
#define DEFAULT_SC_BOTTOM_RIGHT  0x16e8

#define SC_TOP_LEFT              0x1640
#define SC_LEFT                  0x1640
#define SC_TOP                   0x1648

#define SC_BOTTOM_RIGHT          0x1644
#define SC_RIGHT                 0x1644
#define SC_BOTTOM                0x164C

#define DST_OFFSET               0x1404
#define DST_PITCH                0x1408
#define DP_BRUSH_FRGD_CLR        0x147c
#define DP_WRITE_MASK            0x16cc
#define DST_HEIGHT               0x1410
#define DST_X_Y                  0x1594
#define DST_WIDTH_X              0x1588

#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
  __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)

void run_tests(void *bar2);
struct pci_dev *find_device(struct pci_access *pacc,
                            char *name_out, int name_len);
void print_devices(struct pci_access *pacc);
void *map_bar(struct pci_dev *dev, int bar_idx);
static inline uint32_t reg_read(void *base, uint32_t offset);
static inline uint32_t reg_write(void *base, uint32_t offset, uint32_t value);

int main(int argc, char **argv) {
  struct pci_access *pacc = pci_alloc();
  char name[256];
  struct pci_dev *dev = find_device(pacc, name, sizeof(name));
  void *bar2 = map_bar(dev, 2);

  run_tests(bar2);

  return 0;
}

void test_dst_clipping(void *bar2) {
  uint32_t dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  uint32_t default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  uint32_t sc_bottom = reg_read(bar2, SC_BOTTOM);
  uint32_t sc_right = reg_read(bar2, SC_RIGHT);
  uint32_t sc_top = reg_read(bar2, SC_TOP);
  uint32_t sc_left = reg_read(bar2, SC_LEFT);

  printf("Test DST clipping\n");
  printf("====================================\n\n");

  printf("** Initializing DEFAULT_SC_BOTTOM_RIGHT to 0x0 **\n");
  printf("** Initializing SC_BOTTOM to 0x0 **\n");
  printf("** Initializing SC_RIGHT to 0x0 **\n");
  printf("** Initializing SC_TOP to 0x0 **\n");
  printf("** Initializing SC_LEFT to 0x0 **\n");

  reg_write(bar2, DEFAULT_SC_BOTTOM_RIGHT, 0x0);
  reg_write(bar2, SC_BOTTOM, 0x0);
  reg_write(bar2, SC_RIGHT, 0x0);
  reg_write(bar2, SC_TOP, 0x0);
  reg_write(bar2, SC_LEFT, 0x0);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  sc_bottom = reg_read(bar2, SC_BOTTOM);
  sc_right = reg_read(bar2, SC_RIGHT);
  sc_top = reg_read(bar2, SC_TOP);
  sc_left = reg_read(bar2, SC_LEFT);

  printf("\n");
  printf("Initial State\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SC_BOTTOM:               0x%08x\n", sc_bottom);
  printf("SC_RIGHT:                0x%08x\n", sc_right);
  printf("SC_TOP:                  0x%08x\n", sc_top);
  printf("SC_LEFT:                 0x%08x\n", sc_left);
  printf("\n");

  printf("** Setting DEFAULT_SC_BOTTOM_RIGHT to 0x0aaa0bbb **\n");
  printf("** Setting SC_BOTTOM to 0x111 **\n");
  printf("** Setting SC_RIGHT to 0x222 **\n");
  printf("** SETTING SC_TOP to 0x333 **\n");
  printf("** SETTING SC_LEFT to 0x444 **\n");
  reg_write(bar2, DEFAULT_SC_BOTTOM_RIGHT, 0x0aaa0bbb);
  reg_write(bar2, SC_BOTTOM, 0x00000111);
  reg_write(bar2, SC_RIGHT, 0x00000222);
  reg_write(bar2, SC_TOP, 0x00000333);
  reg_write(bar2, SC_LEFT, 0x00000444);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  sc_bottom = reg_read(bar2, SC_BOTTOM);
  sc_right = reg_read(bar2, SC_RIGHT);
  sc_top = reg_read(bar2, SC_TOP);
  sc_left = reg_read(bar2, SC_LEFT);

  printf("\n");
  printf("State After Setting\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SC_BOTTOM:               0x%08x\n", sc_bottom);
  printf("SC_RIGHT:                0x%08x\n", sc_right);
  printf("SC_TOP:                  0x%08x\n", sc_top);
  printf("SC_LEFT:                 0x%08x\n", sc_left);
  printf("\n");

  printf("** Setting GMC_DST_CLIPPING to default **\n");
  reg_write(bar2, DP_GUI_MASTER_CNTL, dp_gui_master_cntl & ~0x8);
  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  sc_bottom = reg_read(bar2, SC_BOTTOM);
  sc_right = reg_read(bar2, SC_RIGHT);
  sc_top = reg_read(bar2, SC_TOP);
  sc_left = reg_read(bar2, SC_LEFT);

  printf("\n");
  printf("State After Setting Default\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SC_BOTTOM:               0x%08x\n", sc_bottom);
  printf("SC_RIGHT:                0x%08x\n", sc_right);
  printf("SC_TOP:                  0x%08x\n", sc_top);
  printf("SC_LEFT:                 0x%08x\n", sc_left);
  printf("\n");

  printf("** Setting GMC_DST_CLIPPING to leave alone **\n");
  reg_write(bar2, DP_GUI_MASTER_CNTL, dp_gui_master_cntl | 0x8);
  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  sc_bottom = reg_read(bar2, SC_BOTTOM);
  sc_right = reg_read(bar2, SC_RIGHT);
  sc_top = reg_read(bar2, SC_TOP);
  sc_left = reg_read(bar2, SC_LEFT);

  printf("\n");
  printf("State After Setting Leave Alone\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SC_BOTTOM:               0x%08x\n", sc_bottom);
  printf("SC_RIGHT:                0x%08x\n", sc_right);
  printf("SC_TOP:                  0x%08x\n", sc_top);
  printf("SC_LEFT:                 0x%08x\n", sc_left);
  printf("\n");
}

void test_src_clipping(void *bar2) {
  uint32_t dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  uint32_t default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  uint32_t src_sc_bottom = reg_read(bar2, SRC_SC_BOTTOM);
  uint32_t src_sc_right = reg_read(bar2, SRC_SC_RIGHT);

  printf("Test SRC clipping\n");
  printf("====================================\n\n");

  printf("** Initializing DEFAULT_SC_BOTTOM_RIGHT to 0x0 **\n");
  printf("** Initializing SRC_SC_BOTTOM to 0x0 **\n");
  printf("** Initializing SRC_SC_RIGHT to 0x0 **\n");

  reg_write(bar2, DEFAULT_SC_BOTTOM_RIGHT, 0x0);
  reg_write(bar2, SRC_SC_BOTTOM, 0x0);
  reg_write(bar2, SRC_SC_RIGHT, 0x0);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  src_sc_bottom = reg_read(bar2, SRC_SC_BOTTOM);
  src_sc_right = reg_read(bar2, SRC_SC_RIGHT);

  printf("\n");
  printf("Initial State\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SRC_SC_BOTTOM:           0x%08x\n", src_sc_bottom);
  printf("SRC_SC_RIGHT:            0x%08x\n", src_sc_right);
  printf("\n");

  printf("** Setting DEFAULT_SC_BOTTOM_RIGHT to 0x0aaa0bbb **\n");
  printf("** Setting SRC_SC_BOTTOM to 0x111 **\n");
  printf("** Setting SRC_SC_RIGHT to 0x222 **\n");
  reg_write(bar2, DEFAULT_SC_BOTTOM_RIGHT, 0x0aaa0bbb);
  reg_write(bar2, SRC_SC_BOTTOM, 0x00000111);
  reg_write(bar2, SRC_SC_RIGHT, 0x00000222);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  src_sc_bottom = reg_read(bar2, SRC_SC_BOTTOM);
  src_sc_right = reg_read(bar2, SRC_SC_RIGHT);

  printf("\n");
  printf("State After Setting\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SRC_SC_BOTTOM:           0x%08x\n", src_sc_bottom);
  printf("SRC_SC_RIGHT:            0x%08x\n", src_sc_right);
  printf("\n");

  printf("** Setting GMC_SRC_CLIPPING to default **\n");
  reg_write(bar2, DP_GUI_MASTER_CNTL, dp_gui_master_cntl & ~0x4);
  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  src_sc_bottom = reg_read(bar2, SRC_SC_BOTTOM);
  src_sc_right = reg_read(bar2, SRC_SC_RIGHT);

  printf("\n");
  printf("State After Setting Default\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SRC_SC_BOTTOM:           0x%08x\n", src_sc_bottom);
  printf("SRC_SC_RIGHT:            0x%08x\n", src_sc_right);
  printf("\n");

  printf("** Setting GMC_SRC_CLIPPING to leave alone **\n");
  reg_write(bar2, DP_GUI_MASTER_CNTL, dp_gui_master_cntl | 0x4);
  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);

  dp_gui_master_cntl = reg_read(bar2, DP_GUI_MASTER_CNTL);
  default_sc_bottom_right = reg_read(bar2, DEFAULT_SC_BOTTOM_RIGHT);
  src_sc_bottom = reg_read(bar2, SRC_SC_BOTTOM);
  src_sc_right = reg_read(bar2, SRC_SC_RIGHT);

  printf("\n");
  printf("State After Setting Leave Alone\n");
  printf("------------------------------------\n");
  printf("DEFAULT_GUI_MASTER_CNTL: 0x%08x\n", dp_gui_master_cntl);
  printf("DEFAULT_SC_BOTTOM_RIGHT: 0x%08x\n", default_sc_bottom_right);
  printf("SRC_SC_BOTTOM:           0x%08x\n", src_sc_bottom);
  printf("SRC_SC_RIGHT:            0x%08x\n", src_sc_right);
  printf("\n");
}

void run_tests(void *bar2) {
  test_src_clipping(bar2);
  test_dst_clipping(bar2);
}

struct pci_dev *find_device(struct pci_access *pacc,
                            char *name_out, int name_len) {
  struct pci_dev *dev, *it;
  int device_count = 0;

  pci_init(pacc);
  pci_scan_bus(pacc);

  for (it = pacc->devices; it; it = it->next) {
    if (it->vendor_id == ATI_VENDOR_ID) {
      if (device_count == 0) {
        dev = it;
      }
      device_count += 1;
    }
  }

  if (device_count == 0) {
    printf("No ATI devices found\n");
    exit(1);
  }

  if (device_count > 1) {
    printf("Found multiple ATI devices:\n");
    print_devices(pacc);
  }

  pci_lookup_name(pacc, name_out, name_len,
                  PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
                  dev->vendor_id, dev->device_id);

  printf("# %s\n\n", name_out);

  return dev;
}

void print_devices(struct pci_access *pacc) {
  struct pci_dev *dev;
  char name[256];

  for (dev = pacc->devices; dev; dev = dev->next) {
    if (dev->vendor_id != ATI_VENDOR_ID) continue;
    pci_lookup_name(pacc, name, sizeof(name),
                    PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
                    dev->vendor_id, dev->device_id);
    printf("\t- %s\n", name);
  }
}

void *map_bar(struct pci_dev *dev, int bar_idx) {
  char pci_loc[32];
  sprintf(pci_loc, "%04x:%02x:%02x.%d", dev->domain, dev->bus,
          dev->dev, dev->func);

  char base_path[256];
  sprintf(base_path, "/sys/bus/pci/devices/%s", pci_loc);

  char bar_path[256];
  sprintf(bar_path, "%s/resource%d", base_path, bar_idx);

  int bar_fd = open(bar_path, O_RDWR | O_SYNC);
  if (bar_fd == -1) FATAL;

  void *bar = mmap(NULL, dev->size[bar_idx], PROT_READ | PROT_WRITE,
                   MAP_SHARED, bar_fd, 0);
  if (bar == (void *) -1) FATAL;

  return bar;
}

static inline uint32_t reg_read(void *base, uint32_t offset) {
  volatile uint32_t *reg = (volatile uint32_t *)(base + offset);
  return *reg;
}

static inline uint32_t reg_write(void *base, uint32_t offset, uint32_t value) {
  volatile uint32_t *reg = (volatile uint32_t *)(base + offset);
  *reg = value;
}

===============================================================================

I haven't tested them yet, but I'll also run this test for
GMC_SRC_PITCH_OFFSET_CNTL and GMC_DST_PITCH_OFFSET_CNTL to confirm that
they have the same behavior.

Reply via email to