get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2222486/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2222486,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2222486/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260412110240.93116-2-yangyanglan718@gmail.com/",
    "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": "<20260412110240.93116-2-yangyanglan718@gmail.com>",
    "date": "2026-04-12T11:02:39",
    "name": "[v3,1/2] hw/display: Add i.MX6UL LCDIF device model",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "9503d3bdeaa7dd6a8b710aa2e4230de166efe9fa",
    "submitter": {
        "id": 92856,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/92856/?format=api",
        "name": "Yucai Liu",
        "email": "yangyanglan718@gmail.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260412110240.93116-2-yangyanglan718@gmail.com/mbox/",
    "series": [
        {
            "id": 499594,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/499594/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=499594",
            "date": "2026-04-12T11:02:39",
            "name": "i.MX6UL LCDIF device model and SoC wiring",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/499594/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2222486/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2222486/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=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=HhZ4ZoV9;\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 4ftnjC4ZN9z1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 12 Apr 2026 21:03:58 +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 1wBsal-00083w-Om; Sun, 12 Apr 2026 07:03:03 -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 <yangyanglan718@gmail.com>)\n id 1wBsae-00081X-M6\n for qemu-devel@nongnu.org; Sun, 12 Apr 2026 07:02:56 -0400",
            "from mail-pf1-x42f.google.com ([2607:f8b0:4864:20::42f])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)\n (Exim 4.90_1) (envelope-from <yangyanglan718@gmail.com>)\n id 1wBsab-0005he-DP\n for qemu-devel@nongnu.org; Sun, 12 Apr 2026 07:02:56 -0400",
            "by mail-pf1-x42f.google.com with SMTP id\n d2e1a72fcca58-82f1dcd5886so794446b3a.2\n for <qemu-devel@nongnu.org>; Sun, 12 Apr 2026 04:02:49 -0700 (PDT)",
            "from archlinux ([58.38.119.35]) by smtp.gmail.com with ESMTPSA id\n d2e1a72fcca58-82f0c20aed6sm7996263b3a.0.2026.04.12.04.02.45\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Sun, 12 Apr 2026 04:02:47 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1775991769; x=1776596569; darn=nongnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=lJrRuxRkxNCdIM+GF/qPPDZigwpgDO+6NfgivjmF46Q=;\n b=HhZ4ZoV9RXQHKPuZTAcG0hOQ2Q7ON+X5jlNul8lJExi9hDCGjDqTYgw+GOI+027FtF\n K+KQsr3vQH8ntEPYg0m9kAPUrvpYqUEaOmJ0ZlLjvyA2pLfQpZmYF69OgYJJj2fpVeO8\n 7JHWOz4b8Zh91l91lPj+ajf5kWrNhB8W8gq1Rsc4O+TA1KWmT2wWfUrbw51hFSWq5zWn\n PeskWKW9n8C1/G65cKbq8RRFRBr7DKQVZEhF2EyCXt9BhoL0z7mMr9iTagNLNajoWU+l\n Y2nFY+fB9XrWidU4S9/1dWgNw/HZ4FotP9uCbEiqSzGvjqOg/mg0XMYkI77nhA7OeEhg\n zG4A==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1775991769; x=1776596569;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=lJrRuxRkxNCdIM+GF/qPPDZigwpgDO+6NfgivjmF46Q=;\n b=W4LQ0iXb7DOxHJ0yA+xhyp482iD7eDudbHqnCt5CM/NG8oBpW8MtTIKdbq6CwWgku5\n tRwqsU/GIrGk5ElZpxFOnyfTvo7I3kmslfWAbt4LbTZy0lvgObpNkiUHG2mVPi4od+Zb\n mNBhDlvdvzB1EHT1HWN2Zd7lJEOvgN7f9o95yZs8racjK3c7bOHfHJFlOnm8fd3b9a88\n +OyeXeW0bBR4fcThYJOufa0F/bxTAoYegCr9r4poB+A4/Ri360cI2LLCT7GJVBUoCO9t\n unktmpTwZvLsPOrLXhTU8Fti8bDlWpLuvckoEc7IxnkBjqsDMgXE+XCEgFuDNmbwWz4+\n F1Sw==",
        "X-Forwarded-Encrypted": "i=1;\n AFNElJ9S8AegddtbLQF0bCBPKweOrWpAti+VYJp/8Q9bkivOH5zcI7rYOdi1QUMa2v7U3VEeMjFTrAPa8rC9@nongnu.org",
        "X-Gm-Message-State": "AOJu0Yw+nvTB3+B0RVuZKTP0reP6QuFG1Dun1HzmOwxz2ePA1a7Ho5ep\n j63+Kgsy9GccDM+Mke9QfiBiBQCWy8lw3C+mJCIHqvAEr7KDa48McgOa",
        "X-Gm-Gg": "AeBDietBDWpUVCMyCp0VwzulEDUTOsaedwS2bjSPha5tzrIEzykTQs0p4LdZowdmW2U\n /oYEOurFhf59RwACm899cRbT9YXMBfS6h0v7TGwlbQWNAWiTVY11Vg3uHsm8rn4RapWmUoV+cz0\n g/hhagajJyp9UcwFoor1OT1ECX4VtU+8kb+rAwQA8zya2vGWHavmT846vZoE3QjxPVOY+y2w841\n il88JHnvrDRTr8px7CXTJFSPmc5Bc5ekQo5UawI75m/Y2oRFau+PtXKdalumbtH06LAXHRPSrai\n /e8mtBCPBP5JiJnQOs0F4m7fxp65qXyPQYqMMmSvIvclRq61rEGMSddjUNcBTXgy94ZTqqrgrzU\n //YUIoo5fqr935jiqHjG+qTQyEeg1WzUCKOoKocAwYYFFPdU6KvuEnqZbeFAuN18Q6nHMn+MsOM\n 70e2HjB8wWjVN1oZn9bKf+uSQ7",
        "X-Received": "by 2002:a05:6a00:228c:b0:82a:7893:e14f with SMTP id\n d2e1a72fcca58-82f0c351427mr11086560b3a.41.1775991768535;\n Sun, 12 Apr 2026 04:02:48 -0700 (PDT)",
        "From": "Yucai Liu <yangyanglan718@gmail.com>",
        "To": "pbonzini@redhat.com,\n\tpeter.maydell@linaro.org",
        "Cc": "jcd@tribudubois.net,\n\tqemu-devel@nongnu.org,\n\tqemu-arm@nongnu.org",
        "Subject": "[PATCH v3 1/2] hw/display: Add i.MX6UL LCDIF device model",
        "Date": "Sun, 12 Apr 2026 19:02:39 +0800",
        "Message-ID": "<20260412110240.93116-2-yangyanglan718@gmail.com>",
        "X-Mailer": "git-send-email 2.53.0",
        "In-Reply-To": "<20260412110240.93116-1-yangyanglan718@gmail.com>",
        "References": "<20260412110240.93116-1-yangyanglan718@gmail.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Received-SPF": "pass client-ip=2607:f8b0:4864:20::42f;\n envelope-from=yangyanglan718@gmail.com; helo=mail-pf1-x42f.google.com",
        "X-Spam_score_int": "-17",
        "X-Spam_score": "-1.8",
        "X-Spam_bar": "-",
        "X-Spam_report": "(-1.8 / 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 FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001,\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>\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 4918f41ec4..b58022eb28 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -895,8 +895,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 90e6c041bd..9b0b1ddf63 100644\n--- a/hw/display/meson.build\n+++ b/hw/display/meson.build\n@@ -11,6 +11,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": [
        "v3",
        "1/2"
    ]
}