From nobody Thu Jan 8 15:12:58 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=jablonski.xyz ARC-Seal: i=1; a=rsa-sha256; t=1767216182; cv=none; d=zohomail.com; s=zohoarc; b=CnfzBzlgJRJyYUARZri7lRNXjwx+Oa/eaOjJqqErmCagR3GUpIvmezhPYcnHwLKoiCB3FewbTYZKcIr4P13Okad48h/iDhylL8QkdxaQS7TJKXOeT9GaK7j5XrppCkidg1BYlmnE9IyFom7q2/hzI9El2tKPnEWBWFwf1VuvxdI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767216182; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=fs9rAZ/AhrU9g45k3TdURM/8EJPv83Sv6QlvOcQQhzo=; b=G9wcQ72Rpcc5WPJi78TiI24vzjGnREhrQTejm46iTIppznoIAwbMm/crdKLlK6G82EzPH+Y+EHGT9X0ZRIXkLCEyedDhSeRkS5zDI55pE1iaqBw+DSsaG9J+lyzbhAHrpD/9uAnsYR4wOqVaUgrAGdOqDDb44cB793/OgG66604= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767216182021239.1559780361174; Wed, 31 Dec 2025 13:23:02 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vb3dR-0000r2-4A; Wed, 31 Dec 2025 16:21:37 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vb3dL-0000ph-Jq for qemu-devel@nongnu.org; Wed, 31 Dec 2025 16:21:33 -0500 Received: from fout-a7-smtp.messagingengine.com ([103.168.172.150]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vb3dJ-0003iX-Cm for qemu-devel@nongnu.org; Wed, 31 Dec 2025 16:21:31 -0500 Received: from phl-compute-03.internal (phl-compute-03.internal [10.202.2.43]) by mailfout.phl.internal (Postfix) with ESMTP id E9681EC0203; Wed, 31 Dec 2025 16:21:28 -0500 (EST) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-03.internal (MEProxy); Wed, 31 Dec 2025 16:21:28 -0500 Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 31 Dec 2025 16:21:28 -0500 (EST) Received: from localhost (chomposaur [local]) by chomposaur (OpenSMTPD) with ESMTPA id 2d71946b; Wed, 31 Dec 2025 21:21:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jablonski.xyz; h=cc:cc:content-transfer-encoding:content-type:date:date:from :from:in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm1; t=1767216088; x= 1767302488; bh=fs9rAZ/AhrU9g45k3TdURM/8EJPv83Sv6QlvOcQQhzo=; b=i jxJPCt4dZwmKpKzwBmZ6Ssg3ioiIka0jWWrRicbqNSP0hizg2mkJbA2sx5mtcINv cUsdZc0pw7MRJdPzCKFCpV3wTpnb/pPZHXZ+SrLBdjCp4pF4s0P7W6z8B6XjOL1S wTwzzuMMfgKNXPL13yXz/lO/QZ7zd4IAYxo+2ixQeRx6akvItr5El1/P1/LzrKVo WIZOLF5dYps14ciOthmKVzvaMSgqjyInQ/YrpnUiLfCcIbRuWpAmwQkCJXXtWHD3 wSBoJT/EOF3i+sHN+SgkoUITjMpr+GtUyLEO4pAabiDJwooogzc7CETnJokYePcz ngCZfuNoAhjy+EdQEHYlg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm2; t=1767216088; x=1767302488; bh=f s9rAZ/AhrU9g45k3TdURM/8EJPv83Sv6QlvOcQQhzo=; b=CE51D0kvcy9LbuDBp 4rSNhpxjeG+E1RacxqMqLca6gIm6l4/7HRl1ImpUjoDJHODjDxNaL0FnH7HkaLAO yfefHq7lDeEaufkBl8hUMtxciuNKbcuH3Zwzww27HY6dVtO8rHjNaSXhsrdDS97s LCLQjvOX4284y4PZlFKFduH+x/cttq85uqVuQVv17eC/hJyk9ALckz1FtbfMO7F5 pl+pWkkd9tGYAkALQUI5J8GxYt33txbpIHdO5924SupGfVCMyJYZk2zmAXY2do95 VpYBzInzlyZ5oydQ+Er+ooHs4TCfRVqxKcshYyGR1MSXlDBcBmOxYqrafn4JnPvm EOAFw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgdekfeeljecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecufghrlhcuvffnffculdejtddmnecujfgurhephffvvefuff fkofgjfhgggfestdekredtredttdenucfhrhhomhepvehhrgguucflrggslhhonhhskhhi uceotghhrggusehjrggslhhonhhskhhirdighiiiqeenucggtffrrghtthgvrhhnpefgie etjefhleehfeeiteejgfeljeffhfeuffdvudeijefgueeuuedvvdekjefhleenucevlhhu shhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpegthhgrugesjhgrsg hlohhnshhkihdrgiihiidpnhgspghrtghpthhtohepfedpmhhouggvpehsmhhtphhouhht pdhrtghpthhtoheptghhrggusehjrggslhhonhhskhhirdighiiipdhrtghpthhtohepqh gvmhhuqdguvghvvghlsehnohhnghhnuhdrohhrghdprhgtphhtthhopegsrghlrghtohhn segvihhkrdgsmhgvrdhhuh X-ME-Proxy: Feedback-ID: ib26944c1:Fastmail From: Chad Jablonski To: qemu-devel@nongnu.org Cc: balaton@eik.bme.hu, Chad Jablonski Subject: [PATCH 6/7] ati-vga: Process Type-0/1/2 CCE packets via PIO Date: Wed, 31 Dec 2025 16:21:06 -0500 Message-ID: <20251231212107.1020964-7-chad@jablonski.xyz> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20251231212107.1020964-1-chad@jablonski.xyz> References: <20251231212107.1020964-1-chad@jablonski.xyz> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=103.168.172.150; envelope-from=chad@jablonski.xyz; helo=fout-a7-smtp.messagingengine.com X-Spam_score_int: -2 X-Spam_score: -0.3 X-Spam_bar: / X-Spam_report: (-0.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FROM_SUSPICIOUS_NTLD=0.498, PDS_OTHER_BAD_TLD=1.997, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @jablonski.xyz) X-ZM-MESSAGEID: 1767216183305158500 Content-Type: text/plain; charset="utf-8" While drivers use bus mastering modes, PIO is the simplest place to start. This implements the PM4_FIFO_DATA_EVEN/ODD registers. Writing to these registers in sequence places packets into the CCE FIFO directly without need for a ring buffer. This enables testing of the CCE packet processing itself. Ring buffer registers will follow in a future patch. Type-0 and Type-1 packets write to registers. Type-2 packets are NOPs. Type-3 packet headers are parsed but only logged as of now. Hardware testing and poking at the microcode suggests that Type-0/1/2 packets may be implemented in hardware and not the microcode. Type-3, however, definitely depends on the microcode. Signed-off-by: Chad Jablonski --- hw/display/ati.c | 10 +++ hw/display/ati_cce.c | 156 ++++++++++++++++++++++++++++++++++++++++ hw/display/ati_cce.h | 56 +++++++++++++++ hw/display/meson.build | 2 +- hw/display/trace-events | 9 +++ 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 hw/display/ati_cce.c diff --git a/hw/display/ati.c b/hw/display/ati.c index 82450c0331..e7ba202bbd 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1117,6 +1117,16 @@ void ati_reg_write(ATIVGAState *s, hwaddr addr, s->cce.freerun =3D data & PM4_MICRO_FREERUN; break; } + case PM4_FIFO_DATA_EVEN: + /* fall through */ + case PM4_FIFO_DATA_ODD: + /* + * Real hardware does seem to behave differently when the even/odd + * sequence is not strictly adhered to but it's difficult to deter= mine + * exactly what is happenning. So for now we treat them the same. + */ + ati_cce_receive_data(s, data); + break; default: break; } diff --git a/hw/display/ati_cce.c b/hw/display/ati_cce.c new file mode 100644 index 0000000000..62a88a54df --- /dev/null +++ b/hw/display/ati_cce.c @@ -0,0 +1,156 @@ +/* + * QEMU ATI SVGA emulation + * CCE engine functions + * + * Copyright (c) 2025 Chad Jablonski + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "ati_regs.h" +#include "ati_int.h" +#include "trace.h" + +static inline uint32_t +ati_cce_data_packets_remaining(const ATIPM4PacketState *p) +{ + switch (p->type) { + case ATI_CCE_TYPE0: + return p->t0.count - p->dwords_processed; + case ATI_CCE_TYPE1: + return 2 - p->dwords_processed; + case ATI_CCE_TYPE2: + return 0; + case ATI_CCE_TYPE3: + return p->t3.count - p->dwords_processed; + default: + /* This should never happen, type is 2-bits wide */ + return 0; + } +} + +static void +ati_cce_parse_packet_header(ATIPM4PacketState *p, uint32_t header) +{ + p->dwords_processed =3D 0; + p->type =3D (header & ATI_CCE_TYPE_MASK) >> ATI_CCE_TYPE_SHIFT; + switch (p->type) { + case ATI_CCE_TYPE0: { + ATIPM4Type0Header t0 =3D { + /* Packet stores base_reg as word offset, convert to byte offs= et */ + .base_reg =3D ((header & ATI_CCE_TYPE0_BASE_REG_MASK) >> + ATI_CCE_TYPE0_BASE_REG_SHIFT) << 2, + /* Packet stores count as n-1, convert to actual count */ + .count =3D ((header & ATI_CCE_TYPE0_COUNT_MASK) >> + ATI_CCE_TYPE0_COUNT_SHIFT) + 1, + .one_reg_wr =3D header & ATI_CCE_TYPE0_ONE_REG_WR, + }; + p->t0 =3D t0; + trace_ati_cce_packet_type0(t0.base_reg, t0.count, t0.one_reg_wr); + break; + } + case ATI_CCE_TYPE1: { + ATIPM4Type1Header t1 =3D { + /* Packet stores reg0 as word offset, convert to byte offset */ + .reg0 =3D ((header & ATI_CCE_TYPE1_REG0_MASK) >> + ATI_CCE_TYPE1_REG0_SHIFT) << 2, + /* Packet stores reg1 as word offset, convert to byte offset */ + .reg1 =3D ((header & ATI_CCE_TYPE1_REG1_MASK) >> + ATI_CCE_TYPE1_REG1_SHIFT) << 2, + }; + p->t1 =3D t1; + trace_ati_cce_packet_type1(t1.reg0, t1.reg1); + break; + } + case ATI_CCE_TYPE2: { + /* Type-2 is a no-op, it has no header state */ + trace_ati_cce_packet_type2(); + break; + } + case ATI_CCE_TYPE3: { + ATIPM4Type3Header t3 =3D { + .opcode =3D (header & ATI_CCE_TYPE3_OPCODE_MASK) >> + ATI_CCE_TYPE3_OPCODE_SHIFT, + /* Packet stores count as n-1, convert to actual count */ + .count =3D ((header & ATI_CCE_TYPE3_COUNT_MASK) >> + ATI_CCE_TYPE3_COUNT_SHIFT) + 1, + }; + p->t3 =3D t3; + trace_ati_cce_packet_type3(t3.opcode, t3.count); + break; + } + default: + /* This should never happen, type is 2-bits wide */ + break; + } +} + +static void +ati_cce_process_type0_data(ATIVGAState *s, uint32_t data) +{ + ATIPM4PacketState *p =3D &s->cce.cur_packet; + uint32_t offset =3D p->t0.one_reg_wr ? 0 : + (p->dwords_processed * sizeof(uint32_t)); + uint32_t reg =3D p->t0.base_reg + offset; + trace_ati_cce_packet_type0_data(p->dwords_processed, reg, data); + ati_reg_write(s, reg, data, sizeof(uint32_t)); +} + +static void +ati_cce_process_type1_data(ATIVGAState *s, uint32_t data) +{ + ATIPM4PacketState *p =3D &s->cce.cur_packet; + uint32_t reg =3D p->dwords_processed =3D=3D 0 ? p->t1.reg0 : p->t1.reg= 1; + trace_ati_cce_packet_type1_data(p->dwords_processed, reg, data); + ati_reg_write(s, reg, data, sizeof(uint32_t)); +} + +static void +ati_cce_process_type3_data(ATIVGAState *s, uint32_t data) +{ + ATIPM4PacketState *p =3D &s->cce.cur_packet; + uint32_t opcode =3D p->t3.opcode; + qemu_log_mask(LOG_UNIMP, "Type-3 CCE packets not yet implemented\n"); + trace_ati_cce_packet_type3_data(p->dwords_processed, opcode, data); +} + +static void +ati_cce_process_packet_data(ATIVGAState *s, uint32_t data) +{ + ATIPM4PacketState *p =3D &s->cce.cur_packet; + switch (p->type) { + case ATI_CCE_TYPE0: { + ati_cce_process_type0_data(s, data); + p->dwords_processed +=3D 1; + break; + } + case ATI_CCE_TYPE1: { + ati_cce_process_type1_data(s, data); + p->dwords_processed +=3D 1; + break; + } + case ATI_CCE_TYPE2: + /* Type-2 packets have no data, we should never end up here */ + break; + case ATI_CCE_TYPE3: { + ati_cce_process_type3_data(s, data); + p->dwords_processed +=3D 1; + break; + } + default: + /* This should never happen, type is 2-bits wide */ + break; + } +} + +void +ati_cce_receive_data(ATIVGAState *s, uint32_t data) +{ + uint32_t remaining =3D ati_cce_data_packets_remaining(&s->cce.cur_pack= et); + if (remaining =3D=3D 0) { + /* We're ready to start processing a new packet header */ + ati_cce_parse_packet_header(&s->cce.cur_packet, data); + return; + } + ati_cce_process_packet_data(s, data); +} diff --git a/hw/display/ati_cce.h b/hw/display/ati_cce.h index a6a9aa87c4..b6ad21f47e 100644 --- a/hw/display/ati_cce.h +++ b/hw/display/ati_cce.h @@ -13,6 +13,60 @@ #include "qemu/osdep.h" #include "qemu/log.h" =20 +typedef struct ATIVGAState ATIVGAState; + +#define ATI_CCE_TYPE_MASK 0xc0000000 +#define ATI_CCE_TYPE_SHIFT 30 + +#define ATI_CCE_TYPE0 0 +#define ATI_CCE_TYPE0_BASE_REG_MASK 0x00007fff +#define ATI_CCE_TYPE0_BASE_REG_SHIFT 0 +#define ATI_CCE_TYPE0_ONE_REG_WR 0x00008000 +#define ATI_CCE_TYPE0_COUNT_MASK 0x3fff0000 +#define ATI_CCE_TYPE0_COUNT_SHIFT 16 + +#define ATI_CCE_TYPE1 1 +#define ATI_CCE_TYPE1_REG0_MASK 0x000007ff +#define ATI_CCE_TYPE1_REG0_SHIFT 0 +#define ATI_CCE_TYPE1_REG1_MASK 0x003ff800 +#define ATI_CCE_TYPE1_REG1_SHIFT 11 + +#define ATI_CCE_TYPE2 2 + +#define ATI_CCE_TYPE3 3 +#define ATI_CCE_TYPE3_OPCODE_MASK 0x0000ff00 +#define ATI_CCE_TYPE3_OPCODE_SHIFT 8 +#define ATI_CCE_TYPE3_COUNT_MASK 0x3fff0000 +#define ATI_CCE_TYPE3_COUNT_SHIFT 16 + +typedef struct ATIPM4Type0Header { + uint32_t base_reg; + uint16_t count; + bool one_reg_wr; +} ATIPM4Type0Header; + +typedef struct ATIPM4Type1Header { + uint32_t reg0; + uint32_t reg1; +} ATIPM4Type1Header; + +/* Type-2 headers are a no-op and have no state */ + +typedef struct ATIPM4Type3Header { + uint8_t opcode; + uint16_t count; +} ATIPM4Type3Header; + +typedef struct ATIPM4PacketState { + uint8_t type; + uint16_t dwords_processed; + union { + ATIPM4Type0Header t0; + ATIPM4Type1Header t1; + ATIPM4Type3Header t3; + }; +} ATIPM4PacketState; + typedef struct ATIPM4MicrocodeState { uint8_t addr; uint8_t raddr; @@ -23,10 +77,12 @@ typedef struct ATICCEState { ATIPM4MicrocodeState microcode; /* MicroCntl */ bool freerun; + ATIPM4PacketState cur_packet; /* BufferCntl */ uint32_t buffer_size_l2qw; bool no_update; uint8_t buffer_mode; } ATICCEState; =20 +void ati_cce_receive_data(ATIVGAState *s, uint32_t data); #endif /* ATI_CCE_H */ diff --git a/hw/display/meson.build b/hw/display/meson.build index 90e6c041bd..136d014746 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -59,7 +59,7 @@ system_ss.add(when: 'CONFIG_XLNX_DISPLAYPORT', if_true: f= iles('xlnx_dp.c')) =20 system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) =20 -system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c',= 'ati_dbg.c'), pixman]) +system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c',= 'ati_dbg.c', 'ati_cce.c'), pixman]) =20 system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_PCI'], if_true: [files('appl= e-gfx.m', 'apple-gfx-pci.m')]) system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_MMIO'], if_true: [files('appl= e-gfx.m', 'apple-gfx-mmio.m')]) diff --git a/hw/display/trace-events b/hw/display/trace-events index e323a82cff..d3c7ca1467 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -147,6 +147,15 @@ sii9022_switch_mode(const char *mode) "mode: %s" ati_mm_read(unsigned int size, uint64_t addr, const char *name, uint64_t v= al) "%u 0x%"PRIx64 " %s -> 0x%"PRIx64 ati_mm_write(unsigned int size, uint64_t addr, const char *name, uint64_t = val) "%u 0x%"PRIx64 " %s <- 0x%"PRIx64 =20 +# ati_cce.c +ati_cce_packet_type0(uint32_t base_reg, uint32_t count, bool one_reg_wr) "= base_reg=3D0x%x count=3D%u one_reg_wr=3D%u" +ati_cce_packet_type0_data(uint32_t data_idx, uint32_t reg, uint32_t data) = "data_idx=3D%u reg=3D0x%x data=3D0x%x" +ati_cce_packet_type1(uint32_t reg0, uint32_t reg1) "reg0=3D0x%x reg1=3D0x%= x" +ati_cce_packet_type1_data(uint32_t data_idx, uint32_t reg, uint32_t data) = "data_idx=3D%u reg=3D0x%x data=3D0x%x" +ati_cce_packet_type2(void) "" +ati_cce_packet_type3(uint8_t opcode, uint32_t count) "opcode=3D0x%x count= =3D%u" +ati_cce_packet_type3_data(uint32_t data_idx, uint8_t opcode, uint32_t data= ) "data_idx=3D%u opcode=3D0x%x data=3D%u" + # artist.c artist_reg_read(unsigned int size, uint64_t addr, const char *name, uint64= _t val) "%u 0x%"PRIx64 "%s -> 0x%08"PRIx64 artist_reg_write(unsigned int size, uint64_t addr, const char *name, uint6= 4_t val) "%u 0x%"PRIx64 "%s <- 0x%08"PRIx64 --=20 2.51.2