@samitouri / QOSamiQemu / commits / 2735701ec9

ati-vga: Implement HOST_DATA flush to VRAM

Implement flushing the 128-bit HOST_DATA accumulator to VRAM to enable text rendering in X. Supports all datatypes (monochrome frgd/bkgd, monochrome frgd, and color), however monochrome frgd support is partial and does not properly handle transparency/leave-alone. The flush is broken up into two steps. First, if necessary, expansion of the monochrome bits to the destination color depth. Then the expanded pixels are sent to the ati_2d_do_blt one scanline at a time. ati_2d_do_blt then clips and performs the blit. Signed-off-by: Chad Jablonski <chad@jablonski.xyz> Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu> [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Message-ID: <8d0d4b52a16f5564e37bbc88496d7ab8d41477ac.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>

Chad Jablonski committed Mar 9, 2026 at 02:47 UTC 2735701ec957c7893b7bb82590dcf80cdd817bca
4 files changed +139 -6
hw/display/ati.c
+2 -4
@@ -1037,11 +1037,9 @@ static void ati_mm_write(void *opaque, hwaddr addr,
1037 }
1038 s->host_data.acc[s->host_data.next++] = data;
1039 if (addr == HOST_DATA_LAST) {
1040 - qemu_log_mask(LOG_UNIMP, "HOST_DATA finish not yet implemented\n");
1041 - s->host_data.next = 0;
1040 + ati_host_data_finish(s);
1041 } else if (s->host_data.next >= 4) {
1043 - qemu_log_mask(LOG_UNIMP, "HOST_DATA flush not yet implemented\n");
1044 - s->host_data.next = 0;
1042 + ati_host_data_flush(s);
1043 }
1044 break;
1045 default:
hw/display/ati_2d.c
+130 -2
@@ -47,6 +47,7 @@ static int ati_bpp_from_datatype(const ATIVGAState *s)
47 typedef struct {
48 int bpp;
49 uint32_t rop3;
50 + bool host_data_active;
51 bool left_to_right;
52 bool top_to_bottom;
53 uint32_t frgd_clr;
@@ -85,6 +86,7 @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx)
86 {
87 ctx->bpp = ati_bpp_from_datatype(s);
88 ctx->rop3 = s->regs.dp_mix & GMC_ROP3_MASK;
89 + ctx->host_data_active = s->host_data.active;
90 ctx->left_to_right = s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT;
91 ctx->top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM;
92 ctx->frgd_clr = s->regs.dp_brush_frgd_clr;
@@ -178,9 +180,10 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman)
180 qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n");
181 return false;
182 }
181 - if (vis_src.x > 0x3fff || vis_src.y > 0x3fff ||
183 + if (!ctx->host_data_active &&
184 + (vis_src.x > 0x3fff || vis_src.y > 0x3fff ||
185 ctx->src_bits >= ctx->vram_end || ctx->src_bits + vis_src.x +
183 - (vis_src.y + vis_dst.height) * ctx->src_stride >= ctx->vram_end) {
186 + (vis_src.y + vis_dst.height) * ctx->src_stride >= ctx->vram_end)) {
187 qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
188 return false;
189 }
@@ -300,8 +303,133 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman)
303 void ati_2d_blt(ATIVGAState *s)
304 {
305 ATI2DCtx ctx;
306 + uint32_t src_source = s->regs.dp_mix & DP_SRC_SOURCE;
307 +
308 + /* Finish any active HOST_DATA blits before starting a new blit */
309 + ati_host_data_finish(s);
310 +
311 + if (src_source == DP_SRC_HOST || src_source == DP_SRC_HOST_BYTEALIGN) {
312 + /* Begin a HOST_DATA blit */
313 + s->host_data.active = true;
314 + s->host_data.next = 0;
315 + s->host_data.col = 0;
316 + s->host_data.row = 0;
317 + return;
318 + }
319 setup_2d_blt_ctx(s, &ctx);
320 if (ati_2d_do_blt(&ctx, s->use_pixman)) {
321 ati_set_dirty(&s->vga, &ctx);
322 }
323 }
324 +
325 +bool ati_host_data_flush(ATIVGAState *s)
326 +{
327 + ATI2DCtx ctx, chunk;
328 + uint32_t fg = s->regs.dp_src_frgd_clr;
329 + uint32_t bg = s->regs.dp_src_bkgd_clr;
330 + unsigned bypp, pix_count, row, col, idx;
331 + uint8_t pix_buf[ATI_HOST_DATA_ACC_BITS * sizeof(uint32_t)];
332 + uint32_t byte_pix_order = s->regs.dp_datatype & DP_BYTE_PIX_ORDER;
333 + uint32_t src_source = s->regs.dp_mix & DP_SRC_SOURCE;
334 + uint32_t src_datatype = s->regs.dp_datatype & DP_SRC_DATATYPE;
335 +
336 + if (!s->host_data.active) {
337 + return false;
338 + }
339 + if (src_source != DP_SRC_HOST) {
340 + qemu_log_mask(LOG_GUEST_ERROR,
341 + "host_data_blt: unsupported src_source %x\n", src_source);
342 + return false;
343 + }
344 + if (src_datatype != SRC_MONO_FRGD_BKGD && src_datatype != SRC_MONO_FRGD &&
345 + src_datatype != SRC_COLOR) {
346 + qemu_log_mask(LOG_GUEST_ERROR,
347 + "host_data_blt: undefined src_datatype %x\n",
348 + src_datatype);
349 + return false;
350 + }
351 +
352 + setup_2d_blt_ctx(s, &ctx);
353 +
354 + if (!ctx.left_to_right || !ctx.top_to_bottom) {
355 + qemu_log_mask(LOG_UNIMP,
356 + "host_data_blt: unsupported blit direction %c%c\n",
357 + ctx.left_to_right ? '>' : '<',
358 + ctx.top_to_bottom ? 'v' : '^');
359 + return false;
360 + }
361 +
362 + bypp = ctx.bpp / 8;
363 +
364 + if (src_datatype == SRC_COLOR) {
365 + pix_count = ATI_HOST_DATA_ACC_BITS / ctx.bpp;
366 + memcpy(pix_buf, &s->host_data.acc[0], sizeof(s->host_data.acc));
367 + } else {
368 + pix_count = ATI_HOST_DATA_ACC_BITS;
369 + /* Expand monochrome bits to color pixels */
370 + idx = 0;
371 + for (int word = 0; word < 4; word++) {
372 + for (int byte = 0; byte < 4; byte++) {
373 + uint8_t byte_val = s->host_data.acc[word] >> (byte * 8);
374 + for (int i = 0; i < 8; i++) {
375 + bool is_fg = byte_val & BIT(byte_pix_order ? i : 7 - i);
376 + uint32_t color = is_fg ? fg : bg;
377 + stn_he_p(&pix_buf[idx], bypp, color);
378 + idx += bypp;
379 + }
380 + }
381 + }
382 + }
383 +
384 + /* Copy and then modify blit ctx for use in a chunked blit */
385 + chunk = ctx;
386 + chunk.src_bits = pix_buf;
387 + chunk.src.y = 0;
388 + chunk.src_stride = ATI_HOST_DATA_ACC_BITS * bypp;
389 +
390 + /* Blit one scanline chunk at a time */
391 + row = s->host_data.row;
392 + col = s->host_data.col;
393 + idx = 0;
394 + DPRINTF("blt %dpx @ row: %d, col: %d\n", pix_count, row, col);
395 + while (idx < pix_count && row < ctx.dst.height) {
396 + unsigned pix_in_scanline = MIN(pix_count - idx,
397 + ctx.dst.width - col);
398 + chunk.src.x = idx;
399 + /* Build a rect for this scanline chunk */
400 + chunk.dst.x = ctx.dst.x + col;
401 + chunk.dst.y = ctx.dst.y + row;
402 + chunk.dst.width = pix_in_scanline;
403 + chunk.dst.height = 1;
404 + DPRINTF("blt %dpx span @ row: %d, col: %d to dst (%d,%d)\n",
405 + pix_in_scanline, row, col, chunk.dst.x, chunk.dst.y);
406 + if (ati_2d_do_blt(&chunk, s->use_pixman)) {
407 + ati_set_dirty(&s->vga, &chunk);
408 + }
409 + idx += pix_in_scanline;
410 + col += pix_in_scanline;
411 + if (col >= ctx.dst.width) {
412 + col = 0;
413 + row += 1;
414 + }
415 + }
416 +
417 + /* Track state of the overall blit for use by the next flush */
418 + s->host_data.next = 0;
419 + s->host_data.row = row;
420 + s->host_data.col = col;
421 + if (s->host_data.row >= ctx.dst.height) {
422 + s->host_data.active = false;
423 + }
424 +
425 + return s->host_data.active;
426 +}
427 +
428 +void ati_host_data_finish(ATIVGAState *s)
429 +{
430 + if (ati_host_data_flush(s)) {
431 + qemu_log_mask(LOG_GUEST_ERROR,
432 + "HOST_DATA blit ended before all data was written\n");
433 + }
434 + s->host_data.active = false;
435 +}
hw/display/ati_int.h
+3
@@ -33,6 +33,7 @@
33
34 #define ATI_RAGE128_LINEAR_APER_SIZE (64 * MiB)
35 #define ATI_R100_LINEAR_APER_SIZE (128 * MiB)
36 +#define ATI_HOST_DATA_ACC_BITS 128
37
38 #define TYPE_ATI_VGA "ati-vga"
39 OBJECT_DECLARE_SIMPLE_TYPE(ATIVGAState, ATI_VGA)
@@ -128,5 +129,7 @@ struct ATIVGAState {
129 const char *ati_reg_name(int num);
130
131 void ati_2d_blt(ATIVGAState *s);
132 +bool ati_host_data_flush(ATIVGAState *s);
133 +void ati_host_data_finish(ATIVGAState *s);
134
135 #endif /* ATI_INT_H */
hw/display/ati_regs.h
+4
@@ -397,7 +397,11 @@
397 #define DST_32BPP 0x00000006
398 #define DP_DST_DATATYPE 0x0000000f
399 #define DP_BRUSH_DATATYPE 0x00000f00
400 +#define SRC_MONO_FRGD_BKGD 0x00000000
401 +#define SRC_MONO_FRGD 0x00010000
402 +#define SRC_COLOR 0x00030000
403 #define DP_SRC_DATATYPE 0x00030000
404 +#define DP_BYTE_PIX_ORDER 0x40000000
405
406 #define BRUSH_SOLIDCOLOR 0x00000d00
407