Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2228882/?format=api
{ "id": 2228882, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2228882/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260427124738.966578-40-peter.maydell@linaro.org/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/1.1/projects/14/?format=api", "name": "QEMU Development", "link_name": "qemu-devel", "list_id": "qemu-devel.nongnu.org", "list_email": "qemu-devel@nongnu.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260427124738.966578-40-peter.maydell@linaro.org>", "date": "2026-04-27T12:47:13", "name": "[PULL,39/63] hw/display: Add i.MX6UL LCDIF device model", "commit_ref": null, "pull_url": null, "state": "not-applicable", "archived": false, "hash": "9503d3bdeaa7dd6a8b710aa2e4230de166efe9fa", "submitter": { "id": 5111, "url": "http://patchwork.ozlabs.org/api/1.1/people/5111/?format=api", "name": "Peter Maydell", "email": "peter.maydell@linaro.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260427124738.966578-40-peter.maydell@linaro.org/mbox/", "series": [ { "id": 501642, "url": "http://patchwork.ozlabs.org/api/1.1/series/501642/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=501642", "date": "2026-04-27T12:46:34", "name": "[PULL,01/63] docs/system: add FEAT_AA32 and FEAT_AA64 to emulation list", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/501642/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2228882/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2228882/checks/", "tags": {}, "headers": { "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=J5siPrjK;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)" ], "Received": [ "from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g43Zs2MNVz1yJX\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 27 Apr 2026 23:00:37 +1000 (AEST)", "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wHLUa-0008HR-3D; Mon, 27 Apr 2026 08:55:16 -0400", "from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <peter.maydell@linaro.org>)\n id 1wHLNw-00087G-R2\n for qemu-devel@nongnu.org; Mon, 27 Apr 2026 08:48:31 -0400", "from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)\n (Exim 4.90_1) (envelope-from <peter.maydell@linaro.org>)\n id 1wHLNq-0005hk-Nl\n for qemu-devel@nongnu.org; Mon, 27 Apr 2026 08:48:22 -0400", "by mail-wm1-x32a.google.com with SMTP id\n 5b1f17b1804b1-48896199cbaso101474605e9.1\n for <qemu-devel@nongnu.org>; Mon, 27 Apr 2026 05:48:17 -0700 (PDT)", "from lanath.. (wildly.archaic.org.uk. [81.2.115.145])\n by smtp.gmail.com with ESMTPSA id\n 5b1f17b1804b1-488ffc5e3f4sm448974115e9.2.2026.04.27.05.48.15\n for <qemu-devel@nongnu.org>\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 27 Apr 2026 05:48:15 -0700 (PDT)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=linaro.org; s=google; t=1777294096; x=1777898896; darn=nongnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:to:from:from:to:cc:subject:date:message-id\n :reply-to; bh=xLiDu/iz5AsiqDXEjMsCtuoCWzBBhHTKjCH3+u/R2jY=;\n b=J5siPrjKN43LRkWd+ubuzJ413Fl4Z2hJ6VuS1n0u6U56GUR16DOWISMCVKPPv2BkGq\n I1TOvwAl7QsSNWVLxMKM8BF4dmEAuqCl5J6RIqZVO47l8NK5nmNp2F1Ozr0pwuhovHzI\n UlwhfPIGDH9yIFFZaMh1Or5i5C8A/EjLPfKv5CGjk924nfqX58ksf3u6/spSvus41qmd\n d0m0czGXYK1XG3fOmdL0CJdbS2QOpfl7k8wGmWlp/uwo7XNoFza6uwwCGseapZGSmchM\n isPUTtr+eH6iZmvdrMMRQ01TdnsqTw3RK3jIdCzVILQIIgGFhAQ9nuXxFbC0tiL4OEai\n 2ylw==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777294096; x=1777898896;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to\n :cc:subject:date:message-id:reply-to;\n bh=xLiDu/iz5AsiqDXEjMsCtuoCWzBBhHTKjCH3+u/R2jY=;\n b=UxUtuI6RSWeA92lk17wNaVz18MaIGc8qJU2DBw/ndawT1fb/Jm3Ch7T4FLMoKRlyEz\n aqIC6Mj9lp5xPxKdFPgVdD1djP1mLSw+Fhc2yFGKm1CUENx1TMGloiPzzqWELxpmq+oV\n FK6wh7/kheHdw55W3iE4w1b6p99Wmlay1q3Qi8sO3hOcdJTI3XRQctoEGm0JKyOTGXN6\n OQObpcJiXZE8lUn3ZxBPSDiw2riT0raIa9warqhtt6Oh+K9GuVJXzoN9QOAnKCaXp7WD\n wY3N7LRuv/i4wn54A3WTCWA3Pf7KfcQElhsfo9NcNdcsEEQfBmar+aZVHR9a9VVOHf1j\n 2jVw==", "X-Gm-Message-State": "AOJu0YwvqrJdk3RlmsoL4pRkWbm335FAFOFp1O3dcYhmXUpmlYZ6v8fz\n rcGLHwFjl0E8y338cQnyeEG9bU7dZ4rzubtabyxE7wIEW98THCve2ia27HstPbDLe/mVAtiIprs\n gzwqA", "X-Gm-Gg": "AeBDieskSek9DxD2qDbIl9W8ePHTGKtM20S+YYXBDCgmqYPyrlbD+GDsJC7CzO18uXy\n Hgm85cAALNMVkKAhrC3lfaS0YcRYpLxkMPjFOgBDquYhs/ToXvMmMpEKIfxSO/1Y+MgYGln3XjJ\n LP/hneK6JdLn1rKqRG1BOMCvZd9gAVGjXwBRG1K8wOCf7Z51euCUeRRH5oGBeQK7AtqnB4M1gwR\n rZY16eaMbpl2b13kbMAEtYarvtbbhy/DewOx6kF56rCUlLMCKP0bt+69RXB84YBjFqRnEFCSsT9\n 3NHHgJFZHWzmh3bn7nNOwj6YBBE8Ykguu6fYZU/3fwHQ/BIL6xprQcrnnCpzUWgGsiv4tRL+vV6\n q3GjLYvLSHi3gkkL9tNfuvLguD27tWdikJrWHY45QAsRXQXi2dKFh/xYm64b7laleLcr2EQFqeI\n zcElSBdaqh7v/IPg8sIflAI69GtdbTSraR1loHgjomXALBcIJl+3dO3LFG2Cplf4WhFfACi+gTd\n Lzzf4Gm9BSZB4mOefU8BW71MWo1ubhZqchfqcfZPlTPq+LIqHt2", "X-Received": "by 2002:a05:600c:859a:b0:48a:53ea:13df with SMTP id\n 5b1f17b1804b1-48a53ea1500mr320879035e9.2.1777294095819;\n Mon, 27 Apr 2026 05:48:15 -0700 (PDT)", "From": "Peter Maydell <peter.maydell@linaro.org>", "To": "qemu-devel@nongnu.org", "Subject": "[PULL 39/63] hw/display: Add i.MX6UL LCDIF device model", "Date": "Mon, 27 Apr 2026 13:47:13 +0100", "Message-ID": "<20260427124738.966578-40-peter.maydell@linaro.org>", "X-Mailer": "git-send-email 2.43.0", "In-Reply-To": "<20260427124738.966578-1-peter.maydell@linaro.org>", "References": "<20260427124738.966578-1-peter.maydell@linaro.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Received-SPF": "pass client-ip=2a00:1450:4864:20::32a;\n envelope-from=peter.maydell@linaro.org; helo=mail-wm1-x32a.google.com", "X-Spam_score_int": "-20", "X-Spam_score": "-2.1", "X-Spam_bar": "--", "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,\n DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=ham autolearn_force=no", "X-Spam_action": "no action", "X-BeenThere": "qemu-devel@nongnu.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "qemu development <qemu-devel.nongnu.org>", "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>", "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>", "List-Post": "<mailto:qemu-devel@nongnu.org>", "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>", "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>", "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org", "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org" }, "content": "From: Yucai Liu <1486344514@qq.com>\n\nImplement a basic i.MX6UL LCDIF controller model with MMIO registers,\nframe-done interrupt behavior, and framebuffer-backed display updates\nfor RGB565 and XRGB8888 input formats.\n\nPlace the LCDIF device under hw/display and build it via a dedicated\nCONFIG_IMX6UL_LCDIF symbol. Model register fields with\nregisterfields.h helpers and provide migration support via vmstate.\n\nSigned-off-by: Yucai Liu <1486344514@qq.com>\nMessage-id: 20260412110240.93116-2-yangyanglan718@gmail.com\nReviewed-by: Peter Maydell <peter.maydell@linaro.org>\nSigned-off-by: Peter Maydell <peter.maydell@linaro.org>\n---\n MAINTAINERS | 2 +\n hw/display/Kconfig | 4 +\n hw/display/imx6ul_lcdif.c | 453 ++++++++++++++++++++++++++++++\n hw/display/meson.build | 1 +\n include/hw/display/imx6ul_lcdif.h | 37 +++\n 5 files changed, 497 insertions(+)\n create mode 100644 hw/display/imx6ul_lcdif.c\n create mode 100644 include/hw/display/imx6ul_lcdif.h", "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex 50a8e161c6..a23ff5279e 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -894,8 +894,10 @@ L: qemu-arm@nongnu.org\n S: Odd Fixes\n F: hw/arm/mcimx6ul-evk.c\n F: hw/arm/fsl-imx6ul.c\n+F: hw/display/imx6ul_lcdif.c\n F: hw/misc/imx6ul_ccm.c\n F: include/hw/arm/fsl-imx6ul.h\n+F: include/hw/display/imx6ul_lcdif.h\n F: include/hw/misc/imx6ul_ccm.h\n F: docs/system/arm/mcimx6ul-evk.rst\n \ndiff --git a/hw/display/Kconfig b/hw/display/Kconfig\nindex 1e95ab28ef..b3593fe981 100644\n--- a/hw/display/Kconfig\n+++ b/hw/display/Kconfig\n@@ -25,6 +25,10 @@ config PL110\n bool\n select FRAMEBUFFER\n \n+config IMX6UL_LCDIF\n+ bool\n+ select FRAMEBUFFER\n+\n config SII9022\n bool\n depends on I2C\ndiff --git a/hw/display/imx6ul_lcdif.c b/hw/display/imx6ul_lcdif.c\nnew file mode 100644\nindex 0000000000..33cd00fbe1\n--- /dev/null\n+++ b/hw/display/imx6ul_lcdif.c\n@@ -0,0 +1,453 @@\n+/*\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ *\n+ * i.MX6UL LCDIF controller\n+ *\n+ * Copyright (c) 2026 Yucai Liu <1486344514@qq.com>\n+ */\n+\n+#include \"qemu/osdep.h\"\n+#include \"hw/display/imx6ul_lcdif.h\"\n+#include \"hw/core/irq.h\"\n+#include \"hw/core/registerfields.h\"\n+#include \"hw/display/framebuffer.h\"\n+#include \"migration/vmstate.h\"\n+#include \"system/address-spaces.h\"\n+#include \"qemu/module.h\"\n+#include \"qemu/units.h\"\n+#include \"ui/pixel_ops.h\"\n+\n+#define LCDIF_MMIO_SIZE (16 * KiB)\n+#define LCDIF_RESET_CTRL1 0x000f0000\n+\n+REG32(CTRL, 0x00)\n+ FIELD(CTRL, RUN, 0, 1)\n+ FIELD(CTRL, WORD_LENGTH, 8, 2)\n+REG32(CTRL1, 0x10)\n+ FIELD(CTRL1, CUR_FRAME_DONE_IRQ, 9, 1)\n+ FIELD(CTRL1, CUR_FRAME_DONE_IRQ_EN, 13, 1)\n+ FIELD(CTRL1, BYTE_PACKING_FORMAT, 16, 4)\n+REG32(V4_TRANSFER_COUNT, 0x30)\n+ FIELD(V4_TRANSFER_COUNT, H_COUNT, 0, 16)\n+ FIELD(V4_TRANSFER_COUNT, V_COUNT, 16, 16)\n+REG32(V4_CUR_BUF, 0x40)\n+REG32(V4_NEXT_BUF, 0x50)\n+REG32(AS_NEXT_BUF, 0x230)\n+\n+#define REG_SET 0x4\n+#define REG_CLR 0x8\n+#define REG_TOG 0xc\n+\n+#define CTRL_WORD_LENGTH_16 0\n+#define CTRL_WORD_LENGTH_24 3\n+\n+#define FRAME_PERIOD_NS (16 * 1000 * 1000ULL)\n+\n+enum IMX6ULLCDIFReg {\n+ IMX6UL_LCDIF_REG_CTRL = A_CTRL >> 4,\n+ IMX6UL_LCDIF_REG_CTRL1 = A_CTRL1 >> 4,\n+ IMX6UL_LCDIF_REG_V4_TRANSFER_COUNT = A_V4_TRANSFER_COUNT >> 4,\n+ IMX6UL_LCDIF_REG_V4_CUR_BUF = A_V4_CUR_BUF >> 4,\n+ IMX6UL_LCDIF_REG_V4_NEXT_BUF = A_V4_NEXT_BUF >> 4,\n+ IMX6UL_LCDIF_REG_AS_NEXT_BUF = A_AS_NEXT_BUF >> 4,\n+};\n+\n+static inline bool imx6ul_lcdif_reg_exists(hwaddr reg)\n+{\n+ return (reg >> 4) < IMX6UL_LCDIF_REGS_NUM;\n+}\n+\n+static inline bool imx6ul_lcdif_reg_has_setclr(hwaddr reg)\n+{\n+ switch (reg) {\n+ case A_CTRL:\n+ case A_CTRL1:\n+ return true;\n+ default:\n+ return false;\n+ }\n+}\n+\n+static inline bool imx6ul_lcdif_is_running(IMX6ULLCDIFState *s)\n+{\n+ uint32_t ctrl = s->regs[IMX6UL_LCDIF_REG_CTRL];\n+\n+ return FIELD_EX32(ctrl, CTRL, RUN);\n+}\n+\n+static inline bool imx6ul_lcdif_frame_done_pending(IMX6ULLCDIFState *s)\n+{\n+ uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1];\n+\n+ return FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ);\n+}\n+\n+static void imx6ul_lcdif_schedule_frame(IMX6ULLCDIFState *s)\n+{\n+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n+\n+ timer_mod(s->frame_timer, now + FRAME_PERIOD_NS);\n+}\n+\n+static void imx6ul_lcdif_maybe_schedule_frame(IMX6ULLCDIFState *s)\n+{\n+ if (imx6ul_lcdif_is_running(s) && !imx6ul_lcdif_frame_done_pending(s)) {\n+ imx6ul_lcdif_schedule_frame(s);\n+ } else {\n+ timer_del(s->frame_timer);\n+ }\n+}\n+\n+static void imx6ul_lcdif_update_irq(IMX6ULLCDIFState *s)\n+{\n+ uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1];\n+ bool level = FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ_EN) &&\n+ FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ);\n+\n+ qemu_set_irq(s->irq, level);\n+}\n+\n+static void imx6ul_lcdif_frame_done(IMX6ULLCDIFState *s)\n+{\n+ uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1];\n+\n+ ctrl1 = FIELD_DP32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ, 1);\n+ s->regs[IMX6UL_LCDIF_REG_CTRL1] = ctrl1;\n+ imx6ul_lcdif_update_irq(s);\n+}\n+\n+static void imx6ul_lcdif_draw_line_rgb565(void *opaque, uint8_t *dst,\n+ const uint8_t *src, int width,\n+ int dststep)\n+{\n+ uint32_t *dst32 = (uint32_t *)dst;\n+ int i;\n+\n+ for (i = 0; i < width; i++) {\n+ uint16_t pixel = lduw_le_p(src);\n+ uint8_t r = ((pixel >> 11) & 0x1f) << 3;\n+ uint8_t g = ((pixel >> 5) & 0x3f) << 2;\n+ uint8_t b = (pixel & 0x1f) << 3;\n+\n+ *dst32++ = rgb_to_pixel32(r, g, b);\n+ src += 2;\n+ }\n+}\n+\n+static void imx6ul_lcdif_draw_line_xrgb8888(void *opaque, uint8_t *dst,\n+ const uint8_t *src, int width,\n+ int dststep)\n+{\n+ uint32_t *dst32 = (uint32_t *)dst;\n+ int i;\n+\n+ for (i = 0; i < width; i++) {\n+ uint32_t pixel = ldl_le_p(src);\n+ uint8_t r = (pixel >> 16) & 0xff;\n+ uint8_t g = (pixel >> 8) & 0xff;\n+ uint8_t b = pixel & 0xff;\n+\n+ *dst32++ = rgb_to_pixel32(r, g, b);\n+ src += 4;\n+ }\n+}\n+\n+static void imx6ul_lcdif_update_display(void *opaque)\n+{\n+ IMX6ULLCDIFState *s = opaque;\n+ DisplaySurface *surface = qemu_console_surface(s->con);\n+ uint32_t transfer_count = s->regs[IMX6UL_LCDIF_REG_V4_TRANSFER_COUNT];\n+ uint32_t width = FIELD_EX32(transfer_count, V4_TRANSFER_COUNT, H_COUNT);\n+ uint32_t height = FIELD_EX32(transfer_count, V4_TRANSFER_COUNT, V_COUNT);\n+ uint32_t ctrl = s->regs[IMX6UL_LCDIF_REG_CTRL];\n+ uint32_t frame_base = s->regs[IMX6UL_LCDIF_REG_V4_CUR_BUF];\n+ drawfn fn;\n+ int first = 0;\n+ int last = 0;\n+ int src_width;\n+\n+ if (!imx6ul_lcdif_is_running(s) || width == 0 || height == 0) {\n+ return;\n+ }\n+\n+ switch (FIELD_EX32(ctrl, CTRL, WORD_LENGTH)) {\n+ case CTRL_WORD_LENGTH_16:\n+ s->src_bpp = 2;\n+ fn = imx6ul_lcdif_draw_line_rgb565;\n+ break;\n+ case CTRL_WORD_LENGTH_24:\n+ s->src_bpp = 4;\n+ fn = imx6ul_lcdif_draw_line_xrgb8888;\n+ break;\n+ default:\n+ return;\n+ }\n+\n+ if (surface_width(surface) != width || surface_height(surface) != height) {\n+ qemu_console_resize(s->con, width, height);\n+ surface = qemu_console_surface(s->con);\n+ s->invalidate = true;\n+ }\n+\n+ src_width = width * s->src_bpp;\n+ if (s->invalidate || s->fb_base != frame_base ||\n+ s->src_width != src_width || s->rows != height) {\n+ framebuffer_update_memory_section(&s->fbsection, get_system_memory(),\n+ frame_base, height, src_width);\n+ s->fb_base = frame_base;\n+ s->src_width = src_width;\n+ s->rows = height;\n+ }\n+\n+ framebuffer_update_display(surface, &s->fbsection, width, height,\n+ src_width, surface_stride(surface), 0,\n+ s->invalidate, fn, s, &first, &last);\n+ if (first >= 0) {\n+ dpy_gfx_update(s->con, 0, first, width, last - first + 1);\n+ }\n+\n+ s->invalidate = false;\n+}\n+\n+static void imx6ul_lcdif_invalidate_display(void *opaque)\n+{\n+ IMX6ULLCDIFState *s = opaque;\n+\n+ s->invalidate = true;\n+}\n+\n+static const GraphicHwOps imx6ul_lcdif_graphic_ops = {\n+ .invalidate = imx6ul_lcdif_invalidate_display,\n+ .gfx_update = imx6ul_lcdif_update_display,\n+};\n+\n+static void imx6ul_lcdif_frame_timer_cb(void *opaque)\n+{\n+ IMX6ULLCDIFState *s = opaque;\n+\n+ if (!imx6ul_lcdif_is_running(s) || imx6ul_lcdif_frame_done_pending(s)) {\n+ return;\n+ }\n+\n+ imx6ul_lcdif_frame_done(s);\n+}\n+\n+static uint64_t imx6ul_lcdif_read(void *opaque, hwaddr offset, unsigned size)\n+{\n+ IMX6ULLCDIFState *s = opaque;\n+ hwaddr reg = offset & ~0xf;\n+ uint32_t idx;\n+\n+ assert(size == 4);\n+ assert(!(offset & 0x3));\n+ assert(offset < LCDIF_MMIO_SIZE);\n+\n+ idx = reg >> 4;\n+ if (idx >= ARRAY_SIZE(s->regs)) {\n+ return 0;\n+ }\n+\n+ return s->regs[idx];\n+}\n+\n+static void imx6ul_lcdif_write(void *opaque, hwaddr offset,\n+ uint64_t value, unsigned size)\n+{\n+ IMX6ULLCDIFState *s = opaque;\n+ hwaddr reg = offset & ~0xf;\n+ uint32_t idx;\n+ uint32_t oldv;\n+\n+ assert(size == 4);\n+ assert(!(offset & 0x3));\n+ assert(offset < LCDIF_MMIO_SIZE);\n+\n+ if (!imx6ul_lcdif_reg_exists(reg)) {\n+ return;\n+ }\n+\n+ idx = reg >> 4;\n+ oldv = s->regs[idx];\n+\n+ switch (offset & 0xf) {\n+ case 0:\n+ s->regs[idx] = (uint32_t)value;\n+ break;\n+ case REG_SET:\n+ if (!imx6ul_lcdif_reg_has_setclr(reg)) {\n+ return;\n+ }\n+ s->regs[idx] = oldv | (uint32_t)value;\n+ break;\n+ case REG_CLR:\n+ if (!imx6ul_lcdif_reg_has_setclr(reg)) {\n+ return;\n+ }\n+ s->regs[idx] = oldv & ~(uint32_t)value;\n+ break;\n+ case REG_TOG:\n+ if (!imx6ul_lcdif_reg_has_setclr(reg)) {\n+ return;\n+ }\n+ s->regs[idx] = oldv ^ (uint32_t)value;\n+ break;\n+ default:\n+ g_assert_not_reached();\n+ }\n+\n+ switch (reg) {\n+ case A_CTRL:\n+ if (!FIELD_EX32(oldv, CTRL, RUN) &&\n+ FIELD_EX32(s->regs[idx], CTRL, RUN)) {\n+ s->invalidate = true;\n+ graphic_hw_invalidate(s->con);\n+ imx6ul_lcdif_maybe_schedule_frame(s);\n+ break;\n+ }\n+ if (FIELD_EX32(oldv, CTRL, RUN) &&\n+ !FIELD_EX32(s->regs[idx], CTRL, RUN)) {\n+ timer_del(s->frame_timer);\n+ }\n+ break;\n+ case A_CTRL1:\n+ if (FIELD_EX32(oldv, CTRL1, CUR_FRAME_DONE_IRQ) &&\n+ !FIELD_EX32(s->regs[idx], CTRL1, CUR_FRAME_DONE_IRQ)) {\n+ imx6ul_lcdif_maybe_schedule_frame(s);\n+ }\n+ break;\n+ case A_V4_TRANSFER_COUNT:\n+ s->invalidate = true;\n+ graphic_hw_invalidate(s->con);\n+ break;\n+ case A_V4_CUR_BUF:\n+ s->invalidate = true;\n+ graphic_hw_invalidate(s->con);\n+ break;\n+ case A_V4_NEXT_BUF:\n+ s->regs[IMX6UL_LCDIF_REG_V4_CUR_BUF] = s->regs[idx];\n+ imx6ul_lcdif_frame_done(s);\n+ s->invalidate = true;\n+ graphic_hw_invalidate(s->con);\n+ imx6ul_lcdif_maybe_schedule_frame(s);\n+ return;\n+ case A_AS_NEXT_BUF:\n+ imx6ul_lcdif_frame_done(s);\n+ imx6ul_lcdif_maybe_schedule_frame(s);\n+ return;\n+ default:\n+ break;\n+ }\n+\n+ imx6ul_lcdif_update_irq(s);\n+}\n+\n+static const MemoryRegionOps imx6ul_lcdif_ops = {\n+ .read = imx6ul_lcdif_read,\n+ .write = imx6ul_lcdif_write,\n+ .endianness = DEVICE_LITTLE_ENDIAN,\n+ .valid = {\n+ .min_access_size = 4,\n+ .max_access_size = 4,\n+ .unaligned = false,\n+ },\n+};\n+\n+static void imx6ul_lcdif_reset(DeviceState *dev)\n+{\n+ IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev);\n+\n+ memset(s->regs, 0, sizeof(s->regs));\n+ s->regs[IMX6UL_LCDIF_REG_CTRL1] = LCDIF_RESET_CTRL1;\n+ s->fb_base = 0;\n+ s->src_width = 0;\n+ s->rows = 0;\n+ s->src_bpp = 0;\n+ s->invalidate = true;\n+ timer_del(s->frame_timer);\n+ imx6ul_lcdif_update_irq(s);\n+}\n+\n+static int imx6ul_lcdif_post_load(void *opaque, int version_id)\n+{\n+ IMX6ULLCDIFState *s = opaque;\n+\n+ s->fb_base = 0;\n+ s->src_width = 0;\n+ s->rows = 0;\n+ s->src_bpp = 0;\n+ s->invalidate = true;\n+\n+ imx6ul_lcdif_update_irq(s);\n+ if (imx6ul_lcdif_is_running(s) &&\n+ !imx6ul_lcdif_frame_done_pending(s) &&\n+ !timer_pending(s->frame_timer)) {\n+ imx6ul_lcdif_schedule_frame(s);\n+ }\n+\n+ return 0;\n+}\n+\n+static const VMStateDescription vmstate_imx6ul_lcdif = {\n+ .name = TYPE_IMX6UL_LCDIF,\n+ .version_id = 1,\n+ .minimum_version_id = 1,\n+ .post_load = imx6ul_lcdif_post_load,\n+ .fields = (const VMStateField[]) {\n+ VMSTATE_UINT32_ARRAY(regs, IMX6ULLCDIFState, IMX6UL_LCDIF_REGS_NUM),\n+ VMSTATE_TIMER_PTR(frame_timer, IMX6ULLCDIFState),\n+ VMSTATE_END_OF_LIST()\n+ },\n+};\n+\n+static void imx6ul_lcdif_realize(DeviceState *dev, Error **errp)\n+{\n+ IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev);\n+\n+ s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,\n+ imx6ul_lcdif_frame_timer_cb, s);\n+ s->invalidate = true;\n+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx6ul_lcdif_ops, s,\n+ TYPE_IMX6UL_LCDIF, LCDIF_MMIO_SIZE);\n+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);\n+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);\n+ s->con = graphic_console_init(dev, 0, &imx6ul_lcdif_graphic_ops, s);\n+}\n+\n+static void imx6ul_lcdif_unrealize(DeviceState *dev)\n+{\n+ IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev);\n+\n+ timer_del(s->frame_timer);\n+ timer_free(s->frame_timer);\n+ s->frame_timer = NULL;\n+\n+ if (s->con) {\n+ graphic_console_close(s->con);\n+ s->con = NULL;\n+ }\n+}\n+\n+static void imx6ul_lcdif_class_init(ObjectClass *klass, const void *data)\n+{\n+ DeviceClass *dc = DEVICE_CLASS(klass);\n+\n+ dc->realize = imx6ul_lcdif_realize;\n+ dc->unrealize = imx6ul_lcdif_unrealize;\n+ dc->vmsd = &vmstate_imx6ul_lcdif;\n+ device_class_set_legacy_reset(dc, imx6ul_lcdif_reset);\n+ dc->desc = \"i.MX6UL LCDIF\";\n+}\n+\n+static const TypeInfo imx6ul_lcdif_info = {\n+ .name = TYPE_IMX6UL_LCDIF,\n+ .parent = TYPE_SYS_BUS_DEVICE,\n+ .instance_size = sizeof(IMX6ULLCDIFState),\n+ .class_init = imx6ul_lcdif_class_init,\n+};\n+\n+static void imx6ul_lcdif_register_types(void)\n+{\n+ type_register_static(&imx6ul_lcdif_info);\n+}\n+\n+type_init(imx6ul_lcdif_register_types)\ndiff --git a/hw/display/meson.build b/hw/display/meson.build\nindex e730c289b1..ffecedbf70 100644\n--- a/hw/display/meson.build\n+++ b/hw/display/meson.build\n@@ -12,6 +12,7 @@ system_ss.add(when: ['CONFIG_VGA_CIRRUS', 'CONFIG_VGA_ISA'], if_true: files('cir\n system_ss.add(when: 'CONFIG_G364FB', if_true: files('g364fb.c'))\n system_ss.add(when: 'CONFIG_JAZZ_LED', if_true: files('jazz_led.c'))\n system_ss.add(when: 'CONFIG_PL110', if_true: files('pl110.c'))\n+system_ss.add(when: 'CONFIG_IMX6UL_LCDIF', if_true: files('imx6ul_lcdif.c'))\n system_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c'))\n system_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c'))\n system_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c'))\ndiff --git a/include/hw/display/imx6ul_lcdif.h b/include/hw/display/imx6ul_lcdif.h\nnew file mode 100644\nindex 0000000000..42fee2fd1d\n--- /dev/null\n+++ b/include/hw/display/imx6ul_lcdif.h\n@@ -0,0 +1,37 @@\n+/*\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ *\n+ * i.MX6UL LCDIF controller\n+ *\n+ * Copyright (c) 2026 Yucai Liu <1486344514@qq.com>\n+ */\n+\n+#ifndef IMX6UL_LCDIF_H\n+#define IMX6UL_LCDIF_H\n+\n+#include \"hw/core/sysbus.h\"\n+#include \"qom/object.h\"\n+#include \"qemu/timer.h\"\n+#include \"ui/console.h\"\n+\n+#define TYPE_IMX6UL_LCDIF \"imx6ul-lcdif\"\n+#define IMX6UL_LCDIF_REGS_NUM ((0x230 >> 4) + 1)\n+OBJECT_DECLARE_SIMPLE_TYPE(IMX6ULLCDIFState, IMX6UL_LCDIF)\n+\n+struct IMX6ULLCDIFState {\n+ SysBusDevice parent_obj;\n+\n+ MemoryRegion iomem;\n+ MemoryRegionSection fbsection;\n+ qemu_irq irq;\n+ QemuConsole *con;\n+ QEMUTimer *frame_timer;\n+ uint32_t fb_base;\n+ uint32_t src_width;\n+ uint32_t rows;\n+ uint8_t src_bpp;\n+ bool invalidate;\n+ uint32_t regs[IMX6UL_LCDIF_REGS_NUM];\n+};\n+\n+#endif /* IMX6UL_LCDIF_H */\n", "prefixes": [ "PULL", "39/63" ] }