* Re: [PATCH v2 8/8] arm64: dts: qcom: eliza: Add support for MM clock controllers
From: Taniya Das @ 2026-05-12 8:52 UTC (permalink / raw)
To: Bryan O'Donoghue, Bjorn Andersson, Michael Turquette,
Stephen Boyd, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Konrad Dybcio, Maxime Coquelin, Alexandre Torgue
Cc: Ajit Pandey, Imran Shaik, Jagadeesh Kona, linux-arm-msm,
linux-clk, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel
In-Reply-To: <b280ad04-d4ae-4904-9e99-3d057e3d221b@kernel.org>
On 4/10/2026 1:56 PM, Bryan O'Donoghue wrote:
> On 10/04/2026 04:55, Taniya Das wrote:
>>> Why do these two controllers have no power-domains ?
>> Bryan, on Eliza the videocc and camcc are connected on CX and MXA.
>
> Shouldn't you at least have:
>
> power-domains = <&rpmhpd RPMHPD_CX> ?
>
> And even
>
> power-domains = <&rpmhpd RPMHPD_MX>,
> <&rpmhpd RPMHPD_CX>;
> power-domain-names = "mx",
> "cx";
>
> Konrad's suggestion to me was that MXA should have a vote in my CSIPHY
> series I think he and Jagadeesh discussed it but I'm not sure if they
> _concluded_ what was the right thing to do.
>
> Right now I'm representing the dependency. MXA is always on ... and
> there's nothing to do voting for it @ MX ?
>
The clock controller drivers only request the minimum operating level
for the power domains. Since the cx and mx rails are already at the
minimum operating level when APPS is active, explicit voting for these
power domains is not required from camcc.
--
Thanks,
Taniya Das
^ permalink raw reply
* Re: [PATCH 0/7] soc: aspeed: Add AST2600 eSPI controller support
From: Shulzhenko, Oleksandr @ 2026-05-12 8:45 UTC (permalink / raw)
To: YH Chung, Arnd Bergmann, Andrew Jeffery, Conor Dooley
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joel Stanley,
Ryan Chen, Philipp Zabel, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
openbmc@lists.ozlabs.org, maciej.lawniczak@intel.com, Mark Brown
In-Reply-To: <KL1PR0601MB4276AB799EC03BB00C4C0E5490392@KL1PR0601MB4276.apcprd06.prod.outlook.com>
On 5/12/2026 9:08 AM, YH Chung wrote:
> Hi Shulzhenko,
>
> Thanks for the follow-up.
>
>> Integrating this driver into the SPI subsystem may allow reusing some existing
>> definitions, e.g.|spi_controller|,|spi_message|, and perhaps parts related to
>> single/dual/quad I/O handling. At the same time, parts such as the Flash channel
>> (included in the current series), and OOB / Virtual Wire support (I would expect
>> to come later), appear to be specific to the Intel eSPI protocol. Modeling all of
>> that as just another SPI IP driver may introduce some awkward layering and
>> overhead.
> Agreed. eSPI introduces two additional pins, RESET# and ALERT#, beyond the
> standard SPI signals. More importantly, eSPI functionality is described
> primarily in terms of four logical channels, rather than generic low-level
> bus signaling or pure data transfers.
>
>> Also, the current series already seems to separate common eSPI logic from
>> AST2600-specific pieces, assuming that 2700 driver is also coming at some point.
>>
>> This makes me wonder whether a dedicated eSPI layer/subsystem could be a
>> better fit — either under the SPI or as something separate (but not SoC driver).
>>
>> Given my limited experience with SPI/eSPI, could you help clarify a few points for
>> me (and probably others as well)?
>>
>> * How much of the SPI subsystem can be reused for this implementation,
>> both for the current patchset and for likely future extensions?
> I believe only a limited portion of the SPI subsystem can be reused. Some
> generic framework elements, such as controller registration and basic
> scaffolding, may be useful initially. But this reuse appears to be mostly
> mechanical rather than semantic. Once eSPI-specific features like Flash
> channels, OOB messaging, and Virtual Wire semantics are involved, the SPI
> transaction model does not seem to map very naturally.
>
>> * Are there any pitfalls or abstraction mismatches in trying to reuse
>> the SPI core here?
> Our main concern is an abstraction mismatch. SPI is designed as a generic
> peripheral bus, while eSPI is more of a system-management interface with
> explicit host-BMC-specific semantics. Reusing the SPI core would likely
> require treating eSPI packets as generic bus-level transfers in the kernel.
>
> However, some eSPI transactions and protocol handling, such as LPC bridge
> accesses, are performed autonomously by the hardware rather than being fully
> driven as low-level bus operations by the driver. This makes the eSPI driver
> somewhat different from a conventional serial bus controller driver
> maintained under the SPI core.
>
Hi YH,
My main concern is trying to understand whether it is completely
impossible (or introduces too much effort that we'd better not to take)
integrating this to SPI subsystem.
From your reply I understand there are two potential blockers:
a) Treating eSPI transfers as bus-level transfers (meaning that it will
be necessary probably making separate driver for OOB/VW/Flash channels
as they essentially use eSPI as a transport);
b) Some logic being done by the hardware (i.e. LPC bridge).
Please confirm my understanding:
(a) is feasible, but requires many effort to re-define architecture
(b) If something is done by the hardware - what is the driver impact? I
recall eDAF use case when the driver wasn't involved at all - and flash
access was fully done by the hardware (unless the controller is
configured to handle it in SW mode).
P.S. I guess we can talk about host-BMC communication only when talking
about hardware-dependent stuff (i.e. ast2600-espi files). eSPI core
should be (it seems to be already is) at least BMC agnostic and this is
the reason not having it under SOC/aspeed (ast2600-espi.* may stay here
though).
^ permalink raw reply
* Re: [PATCH v4 03/15] clk: scmi: Use new determine_rate clock operation
From: Sudeep Holla @ 2026-05-12 8:43 UTC (permalink / raw)
To: Cristian Marussi, Michael Turquette, Stephen Boyd
Cc: linux-kernel, linux-arm-kernel, arm-scmi, linux-clk,
linux-renesas-soc, philip.radford, james.quinlan, f.fainelli,
vincent.guittot, etienne.carriere, peng.fan, michal.simek,
geert+renesas, kuninori.morimoto.gx, marek.vasut+renesas,
Brian Masney
In-Reply-To: <20260508153300.2224715-4-cristian.marussi@arm.com>
On Fri, May 08, 2026 at 04:32:48PM +0100, Cristian Marussi wrote:
> Use the Clock protocol layer determine_rate logic to calculate the closest
> rate that can be supported by a specific clock.
>
> No functional change.
>
Hi Stephen/Mike,
I have potentially queued this and 5/15 to take it via Arm SoC unless you
have any objections. It is mainly simplifying the SCMI clock driver.
--
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH 2/2] drm/verisilicon: add support for Nuvoton MA35D1 DCUltra Lite display controller
From: Thomas Zimmermann @ 2026-05-12 8:24 UTC (permalink / raw)
To: Joey Lu, zhengxingda, maarten.lankhorst, mripard, airlied, simona,
robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260511075142.54752-3-a0987203069@gmail.com>
Hi,
instead of this if-else branchery, I strongly recommend to add a new
file with a new mode-setting pipeline for the new chipset. You can
share existing helpers where possible, and implement variants where
chips differ. It's a bit more code, but will be a lot easier to
maintain in the future.
See ast and mgag200 for example. Both drivers support various revisions
of their chipset, where each rev has its oddities.
Best regards
Thomas
Am 11.05.26 um 09:51 schrieb Joey Lu:
> The Nuvoton MA35D1 SoC integrates a Verisilicon DCUltra Lite display
> controller, which is a previous generation of the DC8000 series. While
> the general register layout is similar to the DC8000, there are several
> key differences that require per-variant handling in the driver.
>
> Add a vs_dc_info platform data structure (in vs_hwdb.h) to describe
> per-IP-variant capabilities, and use it throughout the driver to select
> the correct code paths at runtime.
>
> Key differences between DC8000 and DCUltra Lite handled:
>
> 1. No chip identity registers (0x0020-0x0030): DCUltra Lite uses static
> platform data instead of reading model/revision/customer_id from HW.
>
> 2. No CONFIG_EX commit mechanism: DC8000 uses registers at 0x1CC0
> (FB_CONFIG_EX), 0x24D8 (FB_TOP_LEFT), 0x24E0 (FB_BOTTOM_RIGHT),
> 0x2510 (FB_BLEND_CONFIG), 0x2518 (PANEL_CONFIG_EX). DCUltra Lite
> omits all of these and instead uses enable/reset bits in FB_CONFIG
> (bit 0 = enable, bit 4 = reset) for direct framebuffer updates.
>
> 3. No PANEL_START register (0x1CCC): DCUltra Lite panel output starts
> when PANEL_CONFIG.RUNNING is set; no separate multi-display sync
> start register is needed.
>
> 4. Different IRQ registers: DCUltra Lite uses 0x147C (IRQ_STA) /
> 0x1480 (IRQ_EN); DC8000 uses 0x0010 (IRQ_ACK) / 0x0014 (IRQ_EN).
>
> 5. Different clock/reset topology: DCUltra Lite requires only "core"
> (bus gate) and "pix0" (pixel divider) clocks with no reset lines
> managed by the driver. DC8000 needs core/axi/ahb clocks and three
> resets.
>
> 6. Single output only: DCUltra Lite has one display output; per-output
> index logic is still in place but display_count is fixed at 1.
>
> 7. Reduced register space: max_register is 0x2000 vs DC8000's 0x2544.
>
> Add the "nuvoton,ma35d1-dcu" compatible string to the OF match table,
> extend Kconfig to allow building on ARCH_MA35 platforms, and expose
> vs_formats_no_yuv444 as the default format table for DCUltra Lite
> (YUV444 blending is a DC8000-only feature).
>
> All changes have been tested on Nuvoton MA35D1 hardware and are
> functioning correctly.
>
> Signed-off-by: Joey Lu <a0987203069@gmail.com>
> ---
> drivers/gpu/drm/verisilicon/Kconfig | 2 +-
> drivers/gpu/drm/verisilicon/vs_bridge.c | 28 ++--
> drivers/gpu/drm/verisilicon/vs_crtc.c | 13 +-
> drivers/gpu/drm/verisilicon/vs_dc.c | 129 ++++++++++++------
> drivers/gpu/drm/verisilicon/vs_dc.h | 1 +
> drivers/gpu/drm/verisilicon/vs_drm.c | 16 ++-
> drivers/gpu/drm/verisilicon/vs_hwdb.c | 2 +-
> drivers/gpu/drm/verisilicon/vs_hwdb.h | 25 ++++
> .../gpu/drm/verisilicon/vs_primary_plane.c | 43 +++---
> .../drm/verisilicon/vs_primary_plane_regs.h | 2 +
> 10 files changed, 187 insertions(+), 74 deletions(-)
>
> diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
> index 7cce86ec8603..295d246eb4b4 100644
> --- a/drivers/gpu/drm/verisilicon/Kconfig
> +++ b/drivers/gpu/drm/verisilicon/Kconfig
> @@ -2,7 +2,7 @@
> config DRM_VERISILICON_DC
> tristate "DRM Support for Verisilicon DC-series display controllers"
> depends on DRM && COMMON_CLK
> - depends on RISCV || COMPILE_TEST
> + depends on RISCV || ARCH_MA35 || COMPILE_TEST
> select DRM_BRIDGE_CONNECTOR
> select DRM_CLIENT_SELECTION
> select DRM_DISPLAY_HELPER
> diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c b/drivers/gpu/drm/verisilicon/vs_bridge.c
> index 7a93049368db..225af322de32 100644
> --- a/drivers/gpu/drm/verisilicon/vs_bridge.c
> +++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
> @@ -164,13 +164,16 @@ static void vs_bridge_enable_common(struct vs_crtc *crtc,
> VSDC_DISP_PANEL_CONFIG_CLK_EN);
> regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
> VSDC_DISP_PANEL_CONFIG_RUNNING);
> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
> - VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
> - VSDC_DISP_PANEL_START_RUNNING(output));
>
> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
> - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
> + if (dc->info->has_config_ex) {
> + regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
> + VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
> + VSDC_DISP_PANEL_START_RUNNING(output));
> +
> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
> + VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
> + }
> }
>
> static void vs_bridge_atomic_enable_dpi(struct drm_bridge *bridge,
> @@ -228,14 +231,17 @@ static void vs_bridge_atomic_disable(struct drm_bridge *bridge,
> struct vs_dc *dc = crtc->dc;
> unsigned int output = crtc->id;
>
> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
> - VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
> - VSDC_DISP_PANEL_START_RUNNING(output));
> regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
> VSDC_DISP_PANEL_CONFIG_RUNNING);
>
> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
> - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
> + if (dc->info->has_config_ex) {
> + regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
> + VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
> + VSDC_DISP_PANEL_START_RUNNING(output));
> +
> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
> + VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
> + }
> }
>
> static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
> index 9080344398ca..2f3e6d41c657 100644
> --- a/drivers/gpu/drm/verisilicon/vs_crtc.c
> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
> @@ -18,6 +18,7 @@
> #include "vs_dc.h"
> #include "vs_dc_top_regs.h"
> #include "vs_drm.h"
> +#include "vs_hwdb.h"
> #include "vs_plane.h"
>
> static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
> @@ -132,7 +133,11 @@ static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
> struct vs_dc *dc = vcrtc->dc;
>
> - regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN, VSDC_TOP_IRQ_VSYNC(vcrtc->id));
> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE)
> + regmap_write(dc->regs, VSDC_DISP_IRQ_EN, BIT(0));
> + else
> + regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
> + VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>
> return 0;
> }
> @@ -142,7 +147,11 @@ static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
> struct vs_dc *dc = vcrtc->dc;
>
> - regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN, VSDC_TOP_IRQ_VSYNC(vcrtc->id));
> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE)
> + regmap_write(dc->regs, VSDC_DISP_IRQ_EN, 0);
> + else
> + regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
> + VSDC_TOP_IRQ_VSYNC(vcrtc->id));
> }
>
> static const struct drm_crtc_funcs vs_crtc_funcs = {
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
> index dad9967bc10b..82a6a26f6d81 100644
> --- a/drivers/gpu/drm/verisilicon/vs_dc.c
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
> @@ -9,21 +9,45 @@
> #include <linux/of_graph.h>
>
> #include "vs_crtc.h"
> +#include "vs_crtc_regs.h"
> #include "vs_dc.h"
> #include "vs_dc_top_regs.h"
> #include "vs_drm.h"
> #include "vs_hwdb.h"
>
> -static const struct regmap_config vs_dc_regmap_cfg = {
> +static const struct regmap_config vs_dc8000_regmap_cfg = {
> .reg_bits = 32,
> .val_bits = 32,
> .reg_stride = sizeof(u32),
> - /* VSDC_OVL_CONFIG_EX(1) */
> .max_register = 0x2544,
> };
>
> +static const struct regmap_config vs_dcultra_lite_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = sizeof(u32),
> + .max_register = 0x2000,
> +};
> +
> +static const struct vs_dc_info vs_dc8000_info = {
> + .family = VS_DC_FAMILY_DC8000,
> + .has_chip_id = true,
> + .has_config_ex = true,
> + .regmap_cfg = &vs_dc8000_regmap_cfg,
> +};
> +
> +static const struct vs_dc_info vs_dcultra_lite_info = {
> + .family = VS_DC_FAMILY_DCULTRA_LITE,
> + .display_count = 1,
> + .has_chip_id = false,
> + .has_config_ex = false,
> + .regmap_cfg = &vs_dcultra_lite_regmap_cfg,
> + .formats = &vs_formats_no_yuv444,
> +};
> +
> static const struct of_device_id vs_dc_driver_dt_match[] = {
> - { .compatible = "verisilicon,dc" },
> + { .compatible = "verisilicon,dc", .data = &vs_dc8000_info },
> + { .compatible = "nuvoton,ma35d1-dcu", .data = &vs_dcultra_lite_info },
> {},
> };
> MODULE_DEVICE_TABLE(of, vs_dc_driver_dt_match);
> @@ -33,6 +57,13 @@ static irqreturn_t vs_dc_irq_handler(int irq, void *private)
> struct vs_dc *dc = private;
> u32 irqs;
>
> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE) {
> + regmap_read(dc->regs, VSDC_DISP_IRQ_STA, &irqs);
> + if (irqs & BIT(0))
> + vs_drm_handle_irq(dc, VSDC_TOP_IRQ_VSYNC(0));
> + return IRQ_HANDLED;
> + }
> +
> regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
>
> vs_drm_handle_irq(dc, irqs);
> @@ -43,6 +74,7 @@ static irqreturn_t vs_dc_irq_handler(int irq, void *private)
> static int vs_dc_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> + const struct vs_dc_info *info;
> struct vs_dc *dc;
> void __iomem *regs;
> unsigned int port_count, i;
> @@ -55,6 +87,10 @@ static int vs_dc_probe(struct platform_device *pdev)
> return -ENODEV;
> }
>
> + info = of_device_get_match_data(dev);
> + if (!info)
> + return -ENODEV;
> +
> port_count = of_graph_get_port_count(dev->of_node);
> if (!port_count) {
> dev_err(dev, "can't find DC downstream ports\n");
> @@ -75,15 +111,31 @@ static int vs_dc_probe(struct platform_device *pdev)
> if (!dc)
> return -ENOMEM;
>
> - dc->rsts[0].id = "core";
> - dc->rsts[1].id = "axi";
> - dc->rsts[2].id = "ahb";
> + dc->info = info;
>
> - ret = devm_reset_control_bulk_get_optional_shared(dev, VSDC_RESET_COUNT,
> - dc->rsts);
> - if (ret) {
> - dev_err(dev, "can't get reset lines\n");
> - return ret;
> + if (info->family == VS_DC_FAMILY_DC8000) {
> + dc->rsts[0].id = "core";
> + dc->rsts[1].id = "axi";
> + dc->rsts[2].id = "ahb";
> +
> + ret = devm_reset_control_bulk_get_optional_shared(dev,
> + VSDC_RESET_COUNT, dc->rsts);
> + if (ret) {
> + dev_err(dev, "can't get reset lines\n");
> + return ret;
> + }
> +
> + dc->axi_clk = devm_clk_get_enabled(dev, "axi");
> + if (IS_ERR(dc->axi_clk)) {
> + dev_err(dev, "can't get axi clock\n");
> + return PTR_ERR(dc->axi_clk);
> + }
> +
> + dc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
> + if (IS_ERR(dc->ahb_clk)) {
> + dev_err(dev, "can't get ahb clock\n");
> + return PTR_ERR(dc->ahb_clk);
> + }
> }
>
> dc->core_clk = devm_clk_get_enabled(dev, "core");
> @@ -92,28 +144,18 @@ static int vs_dc_probe(struct platform_device *pdev)
> return PTR_ERR(dc->core_clk);
> }
>
> - dc->axi_clk = devm_clk_get_enabled(dev, "axi");
> - if (IS_ERR(dc->axi_clk)) {
> - dev_err(dev, "can't get axi clock\n");
> - return PTR_ERR(dc->axi_clk);
> - }
> -
> - dc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
> - if (IS_ERR(dc->ahb_clk)) {
> - dev_err(dev, "can't get ahb clock\n");
> - return PTR_ERR(dc->ahb_clk);
> - }
> -
> irq = platform_get_irq(pdev, 0);
> if (irq < 0) {
> dev_err(dev, "can't get irq\n");
> return irq;
> }
>
> - ret = reset_control_bulk_deassert(VSDC_RESET_COUNT, dc->rsts);
> - if (ret) {
> - dev_err(dev, "can't deassert reset lines\n");
> - return ret;
> + if (info->family == VS_DC_FAMILY_DC8000) {
> + ret = reset_control_bulk_deassert(VSDC_RESET_COUNT, dc->rsts);
> + if (ret) {
> + dev_err(dev, "can't deassert reset lines\n");
> + return ret;
> + }
> }
>
> regs = devm_platform_ioremap_resource(pdev, 0);
> @@ -123,23 +165,30 @@ static int vs_dc_probe(struct platform_device *pdev)
> goto err_rst_assert;
> }
>
> - dc->regs = devm_regmap_init_mmio(dev, regs, &vs_dc_regmap_cfg);
> + dc->regs = devm_regmap_init_mmio(dev, regs, info->regmap_cfg);
> if (IS_ERR(dc->regs)) {
> ret = PTR_ERR(dc->regs);
> goto err_rst_assert;
> }
>
> - ret = vs_fill_chip_identity(dc->regs, &dc->identity);
> - if (ret)
> - goto err_rst_assert;
> + if (info->has_chip_id) {
> + ret = vs_fill_chip_identity(dc->regs, &dc->identity);
> + if (ret)
> + goto err_rst_assert;
>
> - dev_info(dev, "Found DC%x rev %x customer %x\n", dc->identity.model,
> - dc->identity.revision, dc->identity.customer_id);
> + dev_info(dev, "Found DC%x rev %x customer %x\n",
> + dc->identity.model, dc->identity.revision,
> + dc->identity.customer_id);
>
> - if (port_count > dc->identity.display_count) {
> - dev_err(dev, "too many downstream ports than HW capability\n");
> - ret = -EINVAL;
> - goto err_rst_assert;
> + if (port_count > dc->identity.display_count) {
> + dev_err(dev, "too many downstream ports than HW capability\n");
> + ret = -EINVAL;
> + goto err_rst_assert;
> + }
> + } else {
> + /* Fill identity from platform data */
> + dc->identity.display_count = info->display_count;
> + dc->identity.formats = info->formats;
> }
>
> for (i = 0; i < dc->identity.display_count; i++) {
> @@ -168,7 +217,8 @@ static int vs_dc_probe(struct platform_device *pdev)
> return 0;
>
> err_rst_assert:
> - reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts);
> + if (info->family == VS_DC_FAMILY_DC8000)
> + reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts);
> return ret;
> }
>
> @@ -180,7 +230,8 @@ static void vs_dc_remove(struct platform_device *pdev)
>
> dev_set_drvdata(&pdev->dev, NULL);
>
> - reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts);
> + if (dc->info->family == VS_DC_FAMILY_DC8000)
> + reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts);
> }
>
> static void vs_dc_shutdown(struct platform_device *pdev)
> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
> index ed1016f18758..f0613519af37 100644
> --- a/drivers/gpu/drm/verisilicon/vs_dc.h
> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
> @@ -31,6 +31,7 @@ struct vs_dc {
> struct clk *pix_clk[VSDC_MAX_OUTPUTS];
> struct reset_control_bulk_data rsts[VSDC_RESET_COUNT];
>
> + const struct vs_dc_info *info;
> struct vs_drm_dev *drm_dev;
> struct vs_chip_identity identity;
> };
> diff --git a/drivers/gpu/drm/verisilicon/vs_drm.c b/drivers/gpu/drm/verisilicon/vs_drm.c
> index fd259d53f49f..ff0fc6673006 100644
> --- a/drivers/gpu/drm/verisilicon/vs_drm.c
> +++ b/drivers/gpu/drm/verisilicon/vs_drm.c
> @@ -27,6 +27,7 @@
> #include "vs_dc.h"
> #include "vs_dc_top_regs.h"
> #include "vs_drm.h"
> +#include "vs_hwdb.h"
>
> #define DRIVER_NAME "verisilicon"
> #define DRIVER_DESC "Verisilicon DC-series display controller driver"
> @@ -72,12 +73,19 @@ static struct drm_mode_config_helper_funcs vs_mode_config_helper_funcs = {
> .atomic_commit_tail = drm_atomic_helper_commit_tail,
> };
>
> -static void vs_mode_config_init(struct drm_device *drm)
> +static void vs_mode_config_init(struct drm_device *drm, struct vs_dc *dc)
> {
> drm->mode_config.min_width = 0;
> drm->mode_config.min_height = 0;
> - drm->mode_config.max_width = 8192;
> - drm->mode_config.max_height = 8192;
> +
> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE) {
> + drm->mode_config.max_width = 1920;
> + drm->mode_config.max_height = 1080;
> + } else {
> + drm->mode_config.max_width = 8192;
> + drm->mode_config.max_height = 8192;
> + }
> +
> drm->mode_config.funcs = &vs_mode_config_funcs;
> drm->mode_config.helper_private = &vs_mode_config_helper_funcs;
> }
> @@ -125,7 +133,7 @@ int vs_drm_initialize(struct vs_dc *dc, struct platform_device *pdev)
> if (ret)
> return ret;
>
> - vs_mode_config_init(drm);
> + vs_mode_config_init(drm, dc);
>
> /* Enable connectors polling */
> drm_kms_helper_poll_init(drm);
> diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c b/drivers/gpu/drm/verisilicon/vs_hwdb.c
> index 09336af0900a..39402d75d841 100644
> --- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
> +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
> @@ -78,7 +78,7 @@ static const u32 vs_formats_array_with_yuv444[] = {
> /* TODO: non-RGB formats */
> };
>
> -static const struct vs_formats vs_formats_no_yuv444 = {
> +const struct vs_formats vs_formats_no_yuv444 = {
> .array = vs_formats_array_no_yuv444,
> .num = ARRAY_SIZE(vs_formats_array_no_yuv444)
> };
> diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h b/drivers/gpu/drm/verisilicon/vs_hwdb.h
> index 92192e4fa086..655cf93ca3aa 100644
> --- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
> +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
> @@ -14,6 +14,29 @@ struct vs_formats {
> unsigned int num;
> };
>
> +enum vs_dc_family {
> + VS_DC_FAMILY_DC8000,
> + VS_DC_FAMILY_DCULTRA_LITE,
> +};
> +
> +/**
> + * struct vs_dc_info - per-SoC DC platform data
> + * @family: DC IP family (DC8000, DCUltra Lite, etc.)
> + * @display_count: number of display outputs (0 = auto-detect from DT/HW)
> + * @has_chip_id: whether chip identity registers exist
> + * @has_config_ex: whether CONFIG_EX commit mechanism exists
> + * @regmap_cfg: regmap configuration for this variant
> + * @formats: supported pixel formats (NULL = auto-detect from chip ID)
> + */
> +struct vs_dc_info {
> + enum vs_dc_family family;
> + u32 display_count;
> + bool has_chip_id;
> + bool has_config_ex;
> + const struct regmap_config *regmap_cfg;
> + const struct vs_formats *formats;
> +};
> +
> struct vs_chip_identity {
> u32 model;
> u32 revision;
> @@ -23,6 +46,8 @@ struct vs_chip_identity {
> const struct vs_formats *formats;
> };
>
> +extern const struct vs_formats vs_formats_no_yuv444;
> +
> int vs_fill_chip_identity(struct regmap *regs,
> struct vs_chip_identity *ident);
>
> diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
> index 1f2be41ae496..197d5d683e22 100644
> --- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
> +++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
> @@ -55,8 +55,9 @@ static int vs_primary_plane_atomic_check(struct drm_plane *plane,
>
> static void vs_primary_plane_commit(struct vs_dc *dc, unsigned int output)
> {
> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> - VSDC_FB_CONFIG_EX_COMMIT);
> + if (dc->info->has_config_ex)
> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> + VSDC_FB_CONFIG_EX_COMMIT);
> }
>
> static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
> @@ -69,11 +70,13 @@ static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
> unsigned int output = vcrtc->id;
> struct vs_dc *dc = vcrtc->dc;
>
> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> - VSDC_FB_CONFIG_EX_FB_EN);
> - regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> - VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
> - VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
> + if (dc->info->has_config_ex) {
> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> + VSDC_FB_CONFIG_EX_FB_EN);
> + regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> + VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
> + VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
> + }
>
> vs_primary_plane_commit(dc, output);
> }
> @@ -88,8 +91,9 @@ static void vs_primary_plane_atomic_disable(struct drm_plane *plane,
> unsigned int output = vcrtc->id;
> struct vs_dc *dc = vcrtc->dc;
>
> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> - VSDC_FB_CONFIG_EX_FB_EN);
> + if (dc->info->has_config_ex)
> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
> + VSDC_FB_CONFIG_EX_FB_EN);
>
> vs_primary_plane_commit(dc, output);
> }
> @@ -126,6 +130,11 @@ static void vs_primary_plane_atomic_update(struct drm_plane *plane,
> VSDC_FB_CONFIG_UV_SWIZZLE_EN,
> vs_state->format.uv_swizzle);
>
> + /* DCUltra Lite requires explicit enable/reset bits in FB_CONFIG */
> + if (!dc->info->has_config_ex)
> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
> + VSDC_FB_CONFIG_ENABLE | VSDC_FB_CONFIG_RESET);
> +
> dma_addr = vs_fb_get_dma_addr(fb, &state->src);
>
> regmap_write(dc->regs, VSDC_FB_ADDRESS(output),
> @@ -133,16 +142,18 @@ static void vs_primary_plane_atomic_update(struct drm_plane *plane,
> regmap_write(dc->regs, VSDC_FB_STRIDE(output),
> fb->pitches[0]);
>
> - regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
> - VSDC_MAKE_PLANE_POS(state->crtc_x, state->crtc_y));
> - regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
> - VSDC_MAKE_PLANE_POS(state->crtc_x + state->crtc_w,
> - state->crtc_y + state->crtc_h));
> regmap_write(dc->regs, VSDC_FB_SIZE(output),
> VSDC_MAKE_PLANE_SIZE(state->crtc_w, state->crtc_h));
>
> - regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
> - VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
> + if (dc->info->has_config_ex) {
> + regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
> + VSDC_MAKE_PLANE_POS(state->crtc_x, state->crtc_y));
> + regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
> + VSDC_MAKE_PLANE_POS(state->crtc_x + state->crtc_w,
> + state->crtc_y + state->crtc_h));
> + regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
> + VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
> + }
>
> vs_primary_plane_commit(dc, output);
> }
> diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h b/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
> index cbb125c46b39..288064760b48 100644
> --- a/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
> +++ b/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
> @@ -16,6 +16,8 @@
> #define VSDC_FB_STRIDE(n) (0x1408 + 0x4 * (n))
>
> #define VSDC_FB_CONFIG(n) (0x1518 + 0x4 * (n))
> +#define VSDC_FB_CONFIG_ENABLE BIT(0)
> +#define VSDC_FB_CONFIG_RESET BIT(4)
> #define VSDC_FB_CONFIG_CLEAR_EN BIT(8)
> #define VSDC_FB_CONFIG_ROT_MASK GENMASK(13, 11)
> #define VSDC_FB_CONFIG_ROT(v) ((v) << 11)
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply
* Re: [PATCH 2/2] drm/verisilicon: add support for Nuvoton MA35D1 DCUltra Lite display controller
From: Icenowy Zheng @ 2026-05-12 8:11 UTC (permalink / raw)
To: Joey Lu, maarten.lankhorst, mripard, tzimmermann, airlied, simona,
robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <de35406e-874d-4bdd-be7f-3d74dc37b13f@gmail.com>
在 2026-05-12二的 15:45 +0800,Joey Lu写道:
>
> On 5/11/2026 5:47 PM, Icenowy Zheng wrote:
> > 在 2026-05-11一的 15:51 +0800,Joey Lu写道:
> > > The Nuvoton MA35D1 SoC integrates a Verisilicon DCUltra Lite
> > > display
> > > controller, which is a previous generation of the DC8000 series.
> > > While
> > > the general register layout is similar to the DC8000, there are
> > > several
> > > key differences that require per-variant handling in the driver.
> > >
> > > Add a vs_dc_info platform data structure (in vs_hwdb.h) to
> > > describe
> > > per-IP-variant capabilities, and use it throughout the driver to
> > > select
> > > the correct code paths at runtime.
> > >
> > > Key differences between DC8000 and DCUltra Lite handled:
> > What the driver supports now is DC8200, DC8000 have the following
> > point
> > 1~4 the same with DCUltraLite (different to DC8200).
> Understood. I'll rename all `vs_dc8000_*` identifiers to
> `vs_dc8200_*` in v2. The commit message will also be corrected to say
> that points 1~4 are differences from DC8200, not DC8000.
> > > 1. No chip identity registers (0x0020-0x0030): DCUltra Lite uses
> > > static
> > > platform data instead of reading model/revision/customer_id
> > > from
> > > HW.
> > My test shows that revision and customer_id is correctly present,
> > only
> > model is 0 -- I think this can be also considered as a valid model
> > value because the IP name has also no model number.
> >
> > The revision number is 0x5560 and customer id is 0x305 .
> Thank you for testing. I'll drop the `has_chip_id` flag
> entirely. In v2, `vs_fill_chip_identity()` will be called for all
> variants. A new entry will be added to `vs_chip_identities[]` in
> vs_hwdb.c with model=0x0, revision=0x5560, customer_id=0x305,
> display_count=1, and `vs_formats_no_yuv444`.
> e.g. verisilicon-dc 40260000.display: Found DC0 rev 5560 customer 305
> >
> > > 2. No CONFIG_EX commit mechanism: DC8000 uses registers at 0x1CC0
> > > (FB_CONFIG_EX), 0x24D8 (FB_TOP_LEFT), 0x24E0
> > > (FB_BOTTOM_RIGHT),
> > > 0x2510 (FB_BLEND_CONFIG), 0x2518 (PANEL_CONFIG_EX). DCUltra
> > > Lite
> > > omits all of these and instead uses enable/reset bits in
> > > FB_CONFIG
> > > (bit 0 = enable, bit 4 = reset) for direct framebuffer
> > > updates.
> > >
> > > 3. No PANEL_START register (0x1CCC): DCUltra Lite panel output
> > > starts
> > > when PANEL_CONFIG.RUNNING is set; no separate multi-display
> > > sync
> > > start register is needed.
> > >
> > > 4. Different IRQ registers: DCUltra Lite uses 0x147C (IRQ_STA) /
> > > 0x1480 (IRQ_EN); DC8000 uses 0x0010 (IRQ_ACK) / 0x0014
> > > (IRQ_EN).
> > >
> > > 5. Different clock/reset topology: DCUltra Lite requires only
> > > "core"
> > > (bus gate) and "pix0" (pixel divider) clocks with no reset
> > > lines
> > > managed by the driver. DC8000 needs core/axi/ahb clocks and
> > > three
> > > resets.
> > It's possible that your SoC integration combines core clock gate
> > with
> > bus clock gate instead of bus clock gate not existing.
> Agreed. In v2 I'll remove the family-gated clock handling and
> instead use `devm_clk_get_optional_enabled()` for the axi and ahb
> clocks, so they are simply skipped if not present in DT. Resets are
> already optional. This keeps the probe path uniform and handles any
> SoC-specific clock combinations naturally.
Maybe this could be splitted as another patch, to make single commits
smaller.
> > > 6. Single output only: DCUltra Lite has one display output; per-
> > > output
> > > index logic is still in place but display_count is fixed at
> > > 1.
> > >
> > > 7. Reduced register space: max_register is 0x2000 vs DC8000's
> > > 0x2544.
> > >
> > > Add the "nuvoton,ma35d1-dcu" compatible string to the OF match
> > > table,
> > > extend Kconfig to allow building on ARCH_MA35 platforms, and
> > > expose
> > > vs_formats_no_yuv444 as the default format table for DCUltra Lite
> > > (YUV444 blending is a DC8000-only feature).
> > >
> > > All changes have been tested on Nuvoton MA35D1 hardware and are
> > > functioning correctly.
> > >
> > > Signed-off-by: Joey Lu <a0987203069@gmail.com>
> > > ---
> > > drivers/gpu/drm/verisilicon/Kconfig | 2 +-
> > > drivers/gpu/drm/verisilicon/vs_bridge.c | 28 ++--
> > > drivers/gpu/drm/verisilicon/vs_crtc.c | 13 +-
> > > drivers/gpu/drm/verisilicon/vs_dc.c | 129
> > > ++++++++++++----
> > > --
> > > drivers/gpu/drm/verisilicon/vs_dc.h | 1 +
> > > drivers/gpu/drm/verisilicon/vs_drm.c | 16 ++-
> > > drivers/gpu/drm/verisilicon/vs_hwdb.c | 2 +-
> > > drivers/gpu/drm/verisilicon/vs_hwdb.h | 25 ++++
> > > .../gpu/drm/verisilicon/vs_primary_plane.c | 43 +++---
> > > .../drm/verisilicon/vs_primary_plane_regs.h | 2 +
> > > 10 files changed, 187 insertions(+), 74 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/verisilicon/Kconfig
> > > b/drivers/gpu/drm/verisilicon/Kconfig
> > > index 7cce86ec8603..295d246eb4b4 100644
> > > --- a/drivers/gpu/drm/verisilicon/Kconfig
> > > +++ b/drivers/gpu/drm/verisilicon/Kconfig
> > > @@ -2,7 +2,7 @@
> > > config DRM_VERISILICON_DC
> > > tristate "DRM Support for Verisilicon DC-series display
> > > controllers"
> > > depends on DRM && COMMON_CLK
> > > - depends on RISCV || COMPILE_TEST
> > > + depends on RISCV || ARCH_MA35 || COMPILE_TEST
> > > select DRM_BRIDGE_CONNECTOR
> > > select DRM_CLIENT_SELECTION
> > > select DRM_DISPLAY_HELPER
> > > diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
> > > b/drivers/gpu/drm/verisilicon/vs_bridge.c
> > > index 7a93049368db..225af322de32 100644
> > > --- a/drivers/gpu/drm/verisilicon/vs_bridge.c
> > > +++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
> > > @@ -164,13 +164,16 @@ static void vs_bridge_enable_common(struct
> > > vs_crtc *crtc,
> > > VSDC_DISP_PANEL_CONFIG_CLK_EN);
> > > regmap_set_bits(dc->regs,
> > > VSDC_DISP_PANEL_CONFIG(output),
> > > VSDC_DISP_PANEL_CONFIG_RUNNING);
> > > - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
> > > -
> > > VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
> > > - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
> > > - VSDC_DISP_PANEL_START_RUNNING(output));
> > >
> > > - regmap_set_bits(dc->regs,
> > > VSDC_DISP_PANEL_CONFIG_EX(crtc-
> > > > id),
> > > - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
> > > + if (dc->info->has_config_ex) {
> > > + regmap_clear_bits(dc->regs,
> > > VSDC_DISP_PANEL_START,
> > > +
> > > VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
> > > + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
> > > + VSDC_DISP_PANEL_START_RUNNING(ou
> > > tput
> > > ));
> > > +
> > > + regmap_set_bits(dc->regs,
> > > VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
> > > + VSDC_DISP_PANEL_CONFIG_EX_COMMIT
> > > );
> > Should the commit operation happen on DC8000/DCUltraLite too? (By
> > writing to DcregFrameBufferConfig0.VALID).
> >
> > Many registers written has "Note: This field is double buffered" in
> > the
> > DCUltraLite documentation.
> >
> > I suggest create a static function for commit -- write to the
> > corresponding commit bit on DC8200, and write to
> > DcregFrameBufferConfig0.VALID on DC8000/DCUltraLite.
> [a] There is no commit operation for DCUltra Lite.
> I'll not add a `VSDC_FB_CONFIG_VALID` macro. VALID (BIT(3)) is a
> hardware-managed double-buffer status bit: hardware writes 1=PENDING
> when a new register set is ready and clears to 0=WORKING after the
> VBLANK copy. Software must never write it, and there is no polling
> use
It seems to be writable and controls whether register buffering is
enabled, see [1].
The description of this bit in MA35D1 TRM says "This ensures a frame
will always start with a valid working set if this register is
programmed last, which reduces the need for SW to wait for the start of
a VBLANK signal in order to ensure all states are loaded before the
next VBLANK", which indicates some kind of "committing write", although
the code at [1] seems to indicate that double buffering is only enabled
when bit is cleared.
Anyway this bit should be programmable, and "Software must never write
it" contradicts with the MA35D1 TRM.
Thanks,
Icenowy
[1]
https://github.com/rockos-riscv/rockos-kernel/blob/rockos-v6.6.y/drivers/gpu/drm/eswin/es_dc_hw.c#L993
> case in the driver that requires a named constant. For non-config_ex
> variants, `vs_primary_plane_commit()` performs no commit operation —
> `VSDC_FB_CONFIG_ENABLE` (OUTPUT, BIT(0)) is set in
> `vs_crtc_atomic_enable()` and `VSDC_FB_CONFIG_RESET` (BIT(4)) is
> set/cleared in the bridge enable/disable paths.
========= 8< ==========
^ permalink raw reply
* Re: [PATCH] ARM: OMAP2+: Make OMAP4 finish_suspend callback CFI-safe
From: Andreas Kemnade @ 2026-05-12 8:02 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Mithil Bavishi, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, Russell King, Sami Tolvanen, Kees Cook,
linux-arm-kernel, linux-omap, llvm, linux-kernel
In-Reply-To: <20260512073442.GA570003@ax162>
On Tue, 12 May 2026 16:34:42 +0900
Nathan Chancellor <nathan@kernel.org> wrote:
> On Tue, May 12, 2026 at 12:23:41AM -0400, Mithil Bavishi wrote:
> > With CONFIG_CFI enabled, OMAP4 can trap in omap4_enter_lowpower()
> > because omap_pm_ops.finish_suspend points directly to the assembly
> > routine omap4_finish_suspend, which lacks the expected KCFI type
> > metadata.
>
> It sounds like omap4_finish_suspend() should be defined with
> SYM_TYPED_FUNC_START then? Is that the case for all of the other
> functions that are added to omap_pm_ops?
>
omap_cpu_resume: the address is written to some cpu register and
on that way casted to u32. So therefore does not trigger CFI.
Same for secondary_startup which is also assembler code.
scu_prepare is C.
DO you have a pointer to any documentation:
:~/linux$ grep -R SYM_TYPED_FUNC_START Documentation/
Regards,
Andreas
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: display: verisilicon,dc: generalize for DCUltra Lite variant
From: Joey Lu @ 2026-05-12 8:02 UTC (permalink / raw)
To: Icenowy Zheng, maarten.lankhorst, mripard, tzimmermann, airlied,
simona, robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <cb69490652409eb4f07b3ee642d478e1b2e28c5b.camel@iscas.ac.cn>
On 5/11/2026 5:59 PM, Icenowy Zheng wrote:
> 在 2026-05-11一的 15:51 +0800,Joey Lu写道:
>> Extend the verisilicon,dc base schema to accommodate the Nuvoton
>> MA35D1
>> DCUltra Lite (a previous generation of the DC8000 series) which has a
>> different clock topology, no reset control, and a single output.
>>
>> - Replace the fixed clock/reset item lists with minItems/maxItems
>> ranges
>> so sub-schemas can enforce variant-specific constraints
>> - Add a 'port' property (single-port alias) alongside the existing
>> 'ports'
>> for single-output variants
>> - Remove the mandatory 'ports' requirement from the base schema; sub-
>> schemas
>> shall enforce their own port topology
>> - Add a 'select' stanza so the validator matches any node whose
>> compatible
>> contains a known Verisilicon DC string, including SoC-specific glue
>> - Relax additionalProperties to allow unevaluatedProperties
>> enforcement in
>> sub-schemas
>> - Fix a minor whitespace issue in the port@0 description
>>
>> Add nuvoton,ma35d1-dcu.yaml as a sub-schema for the Nuvoton MA35D1
>> DCUltra
>> Lite display controller:
>>
>> The Nuvoton MA35D1 integrates the Verisilicon DCUltra Lite display
>> controller. It is a single-output display controller with a 32-bit
>> RGB (DPI) interface. Unlike the DC8000, it does not have discoverable
>> chip identity registers, does not support the CONFIG_EX commit path,
>> and uses dedicated IRQ status/enable registers at offsets
>> 0x147C/0x1480.
>> The clock topology uses two clocks (bus gate and pixel divider) and
>> does not require explicit reset control from the driver.
>>
>> Signed-off-by: Joey Lu <a0987203069@gmail.com>
>> ---
>> .../bindings/display/nuvoton,ma35d1-dcu.yaml | 94
>> +++++++++++++++++++
>> .../bindings/display/verisilicon,dc.yaml | 64 +++++++------
>> 2 files changed, 131 insertions(+), 27 deletions(-)
>> create mode 100644
>> Documentation/devicetree/bindings/display/nuvoton,ma35d1-dcu.yaml
>>
>> diff --git
>> a/Documentation/devicetree/bindings/display/nuvoton,ma35d1-dcu.yaml
>> b/Documentation/devicetree/bindings/display/nuvoton,ma35d1-dcu.yaml
>> new file mode 100644
>> index 000000000000..9279004ae27c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/nuvoton,ma35d1-
>> dcu.yaml
>> @@ -0,0 +1,94 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/nuvoton,ma35d1-dcu.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton MA35D1 DCUltra Lite display controller
>> +
>> +maintainers:
>> + - Joey Lu <yclu4@nuvoton.com>
>> +
>> +description:
>> + The Nuvoton MA35D1 integrates the Verisilicon DCUltra Lite display
>> + controller. It is a single-output display controller with a 32-bit
>> + RGB (DPI) interface.
> You'd better write this in verisilicon,dc.yaml with if clauses. See
> Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml for an
> example for a generic IP with different integrations, and how it
> constraints different SoC's integration.
Understood. I'll drop both the separate nuvoton,ma35d1-dcu.yaml
file and the `nuvoton,ma35d1-dcu` compatible string entirely. Since the
chip identity registers are present on the DCUltra Lite (model=0x0,
rev=0x5560, customer=0x305), the MA35D1 DT node will use
`compatible = "verisilicon,dc"` directly. The YAML will use a single
`if/then/else` block in `allOf:` keyed on whether `thead,th1520-dc8200`
is present in the compatible list: the `then` branch enforces DC8200
constraints (5 clocks, 3 resets, `ports` required), and the `else`
branch enforces DCUltra Lite / generic constraints (2 clocks, 1 reset,
`port` required, `ports` forbidden). The Nuvoton MA35D1 example will use
`compatible = "verisilicon,dc"` and fall into the `else` branch.
>> +
>> +select:
>> + properties:
>> + compatible:
>> + contains:
>> + enum:
>> + - nuvoton,ma35d1-dcu
>> + required:
>> + - compatible
>> +
>> +allOf:
>> + - $ref: http://devicetree.org/schemas/display/verisilicon,dc.yaml#
>> +
>> +properties:
>> + compatible:
>> + const: nuvoton,ma35d1-dcu
>> +
>> + reg:
>> + maxItems: 1
>> + description:
>> + Register range of the DCUltra Lite controller. The address
>> space
>> + is 0x2000 bytes.
> Is it really 0x2000 bytes? The next peripherals in the address space,
> the GC520L 2D GPU, is 0x20000 bytes away from the start of DCU
> registers space.
Good point. The 0x2000 figure is the driver's `max_register`,
not the full hardware block size. I'll remove the description text
claiming "0x2000 bytes" and update the example to use `0x20000` to
match the actual hardware address space.
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + clocks:
>> + items:
>> + - description: Bus clock that gates register access (DCU_GATE)
>> + - description: Pixel clock divider for display timing
>> (DCUP_DIV)
>> +
>> + clock-names:
>> + items:
>> + - const: core
>> + - const: pix0
>> +
>> + resets:
>> + maxItems: 1
>> + description:
>> + Optional reset for the display controller. The driver does not
>> + assert or deassert this reset; it may be used by firmware or
>> + boot loaders to bring the hardware to a clean state.
> Why is there a reset in hardware but not toggled in the software?
The driver will handle the reset in v2. The probe path will call
`devm_reset_control_bulk_get_optional_shared()` unconditionally with all
three names ("core", "axi", "ahb"). Since the MA35D1 DT only provides a
single reset named "core", the "axi" and "ahb" entries will be no-op
handles — optional bulk reset lookup skips absent entries silently.
>> +
>> + port:
>> + $ref: /schemas/graph.yaml#/properties/port
>> + description:
>> + Output port to the downstream display device (e.g. RGB panel).
>> + The DCUltra Lite supports a single parallel RGB output.
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - interrupts
>> + - clocks
>> + - clock-names
>> + - port
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>> + #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
>> + #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
>> +
>> + display@40260000 {
>> + compatible = "nuvoton,ma35d1-dcu";
>> + reg = <0x40260000 0x2000>;
>> + interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&clk DCU_GATE>, <&clk DCUP_DIV>;
>> + clock-names = "core", "pix0";
>> + resets = <&sys MA35D1_RESET_DISP>;
>> +
>> + port {
>> + dpi_out: endpoint {
>> + remote-endpoint = <&panel_in>;
>> + };
>> + };
>> + };
>> diff --git
>> a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> index 9dc35ab973f2..00884529f8c1 100644
>> --- a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> +++ b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> @@ -9,15 +9,34 @@ title: Verisilicon DC-series display controllers
>> maintainers:
>> - Icenowy Zheng <uwu@icenowy.me>
>>
>> +description:
>> + Verisilicon DC-series display controllers.
>> +
>> +# Select any node whose compatible contains one of the known
>> Verisilicon DC
>> +# or DC-derived compatible strings, including SoC-specific glue
>> variants.
>> +select:
>> + properties:
>> + compatible:
>> + contains:
>> + enum:
>> + - verisilicon,dc
>> + - thead,th1520-dc8200
>> + - nuvoton,ma35d1-dcu
>> + required:
>> + - compatible
>> +
>> properties:
>> $nodename:
>> pattern: "^display@[0-9a-f]+$"
>>
>> compatible:
>> - items:
>> - - enum:
>> - - thead,th1520-dc8200
>> - - const: verisilicon,dc # DC IPs have discoverable ID/revision
>> registers
>> + # Enumerated in full so the schema validator can verify any
>> compatible
>> + # string against this list, including those from child schemas.
>> + contains:
>> + enum:
>> + - verisilicon,dc
>> + - thead,th1520-dc8200
>> + - nuvoton,ma35d1-dcu
>>
>> reg:
>> maxItems: 1
>> @@ -26,32 +45,24 @@ properties:
>> maxItems: 1
>>
>> clocks:
>> - items:
>> - - description: DC Core clock
>> - - description: DMA AXI bus clock
>> - - description: Configuration AHB bus clock
>> - - description: Pixel clock of output 0
>> - - description: Pixel clock of output 1
>> + minItems: 2
>> + maxItems: 5
>>
>> clock-names:
>> - items:
>> - - const: core
>> - - const: axi
>> - - const: ahb
>> - - const: pix0
>> - - const: pix1
>> + minItems: 2
>> + maxItems: 5
>>
>> resets:
>> - items:
>> - - description: DC Core reset
>> - - description: DMA AXI bus reset
>> - - description: Configuration AHB bus reset
>> + minItems: 1
>> + maxItems: 3
>>
>> reset-names:
>> - items:
>> - - const: core
>> - - const: axi
>> - - const: ahb
>> + minItems: 1
>> + maxItems: 3
>> +
>> + port:
>> + $ref: /schemas/graph.yaml#/properties/port
>> + description: Single video output port for single-output
>> variants.
>>
>> ports:
>> $ref: /schemas/graph.yaml#/properties/ports
>> @@ -59,7 +70,7 @@ properties:
>> properties:
>> port@0:
>> $ref: /schemas/graph.yaml#/properties/port
>> - description: The first output channel , endpoint 0 should be
>> + description: The first output channel, endpoint 0 should be
>> used for DPI format output and endpoint 1 should be used
>> for DP format output.
>>
>> @@ -75,9 +86,8 @@ required:
>> - interrupts
>> - clocks
>> - clock-names
>> - - ports
>>
>> -additionalProperties: false
>> +additionalProperties: true
>>
>> examples:
>> - |
^ permalink raw reply
* Re: [PATCH v4] media: verisilicon: Create AV1 helper library
From: Benjamin Gaignard @ 2026-05-12 8:01 UTC (permalink / raw)
To: Hans Verkuil, nicolas.dufresne, p.zabel, mchehab, heiko
Cc: linux-kernel, linux-media, linux-rockchip, linux-arm-kernel,
kernel
In-Reply-To: <1f48d0fd-729e-4694-bb8a-4ce6c6f3675f@kernel.org>
Le 12/05/2026 à 08:33, Hans Verkuil a écrit :
> Hi Benjamin,
>
> It looks much better now that it uses a struct.
>
> I have some other comments, though:
You are right I will more carefully check arrays bond and
the returned value of hantro_av1_get_frame_index() everywhere.
Regards,
Benjamin
>
> On 05/05/2026 10:19, Benjamin Gaignard wrote:
>> Regroup all none hardware related AV1 functions into a helper library.
>> The goal is to avoid code duplication for future AV1 codecs.
>>
>> Tested on rock 5b board Fluster score remains the same 204/241.
>>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
>> ---
>> version 4:
>> - change functions prototypes to use a structure.
>> - rebased on v7.1-rc2 tag.
>>
>> drivers/media/platform/verisilicon/Makefile | 7 +-
>> .../media/platform/verisilicon/hantro_av1.c | 782 ++++++++++++++
>> .../media/platform/verisilicon/hantro_av1.h | 62 ++
>> ...entropymode.c => hantro_av1_entropymode.c} | 18 +-
>> ...entropymode.h => hantro_av1_entropymode.h} | 18 +-
>> ...av1_filmgrain.c => hantro_av1_filmgrain.c} | 125 ++-
>> .../verisilicon/hantro_av1_filmgrain.h | 48 +
>> .../media/platform/verisilicon/hantro_hw.h | 7 +-
>> .../verisilicon/rockchip_av1_filmgrain.h | 36 -
>> .../verisilicon/rockchip_vpu981_hw_av1_dec.c | 977 ++----------------
>> .../platform/verisilicon/rockchip_vpu_hw.c | 7 +-
>> 11 files changed, 1076 insertions(+), 1011 deletions(-)
>> create mode 100644 drivers/media/platform/verisilicon/hantro_av1.c
>> create mode 100644 drivers/media/platform/verisilicon/hantro_av1.h
>> rename drivers/media/platform/verisilicon/{rockchip_av1_entropymode.c => hantro_av1_entropymode.c} (99%)
>> rename drivers/media/platform/verisilicon/{rockchip_av1_entropymode.h => hantro_av1_entropymode.h} (95%)
>> rename drivers/media/platform/verisilicon/{rockchip_av1_filmgrain.c => hantro_av1_filmgrain.c} (85%)
>> create mode 100644 drivers/media/platform/verisilicon/hantro_av1_filmgrain.h
>> delete mode 100644 drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h
>>
> <snip>
>
>> diff --git a/drivers/media/platform/verisilicon/hantro_av1.c b/drivers/media/platform/verisilicon/hantro_av1.c
>> new file mode 100644
>> index 000000000000..7258a749f7b5
>> --- /dev/null
>> +++ b/drivers/media/platform/verisilicon/hantro_av1.c
>> @@ -0,0 +1,782 @@
> <snip>
>
>> +int hantro_av1_get_frame_index(struct hantro_ctx *ctx, int ref)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
>> + u64 timestamp;
>> + int i, idx = frame->ref_frame_idx[ref];
> It's probably wise to check that 'ref' isn't out of bounds (< 0 or
>> = V4L2_AV1_REFS_PER_FRAME).
>> +
>> + if (idx >= V4L2_AV1_TOTAL_REFS_PER_FRAME || idx < 0)
>> + return AV1_INVALID_IDX;
>> +
>> + timestamp = frame->reference_frame_ts[idx];
>> + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
>> + if (!av1_dec->frame_refs[i].used)
>> + continue;
>> + if (av1_dec->frame_refs[i].timestamp == timestamp)
>> + return i;
>> + }
>> +
>> + return AV1_INVALID_IDX;
>> +}
>> +
>> +int hantro_av1_get_order_hint(struct hantro_ctx *ctx, int ref)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + int idx = hantro_av1_get_frame_index(ctx, ref);
>> +
>> + if (idx != AV1_INVALID_IDX)
>> + return av1_dec->frame_refs[idx].order_hint;
>> +
>> + return 0;
>> +}
>> +
>> +int hantro_av1_frame_ref(struct hantro_ctx *ctx, u64 timestamp)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
>> + int i;
>> +
>> + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
>> + int j;
>> +
>> + if (av1_dec->frame_refs[i].used)
>> + continue;
>> +
>> + av1_dec->frame_refs[i].width = frame->frame_width_minus_1 + 1;
>> + av1_dec->frame_refs[i].height = frame->frame_height_minus_1 + 1;
>> + av1_dec->frame_refs[i].mi_cols = DIV_ROUND_UP(frame->frame_width_minus_1 + 1, 8);
>> + av1_dec->frame_refs[i].mi_rows = DIV_ROUND_UP(frame->frame_height_minus_1 + 1, 8);
>> + av1_dec->frame_refs[i].timestamp = timestamp;
>> + av1_dec->frame_refs[i].frame_type = frame->frame_type;
>> + av1_dec->frame_refs[i].order_hint = frame->order_hint;
>> + av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx);
>> +
>> + for (j = 0; j < V4L2_AV1_TOTAL_REFS_PER_FRAME; j++)
>> + av1_dec->frame_refs[i].order_hints[j] = frame->order_hints[j];
>> + av1_dec->frame_refs[i].used = true;
>> + av1_dec->current_frame_index = i;
>> +
>> + return i;
>> + }
>> +
>> + return AV1_INVALID_IDX;
>> +}
>> +
>> +static void hantro_av1_frame_unref(struct hantro_ctx *ctx, int idx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> +
>> + if (idx >= 0)
>> + av1_dec->frame_refs[idx].used = false;
>> +}
>> +
>> +void hantro_av1_clean_refs(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> +
>> + int ref, idx;
>> +
>> + for (idx = 0; idx < AV1_MAX_FRAME_BUF_COUNT; idx++) {
>> + u64 timestamp = av1_dec->frame_refs[idx].timestamp;
>> + bool used = false;
>> +
>> + if (!av1_dec->frame_refs[idx].used)
>> + continue;
>> +
>> + for (ref = 0; ref < V4L2_AV1_TOTAL_REFS_PER_FRAME; ref++) {
>> + if (ctrls->frame->reference_frame_ts[ref] == timestamp)
>> + used = true;
>> + }
>> +
>> + if (!used)
>> + hantro_av1_frame_unref(ctx, idx);
>> + }
>> +}
>> +
>> +size_t hantro_av1_luma_size(struct hantro_ctx *ctx)
>> +{
>> + return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height;
>> +}
>> +
>> +size_t hantro_av1_chroma_size(struct hantro_ctx *ctx)
>> +{
>> + size_t cr_offset = hantro_av1_luma_size(ctx);
>> +
>> + return ALIGN((cr_offset * 3) / 2, 64);
>> +}
>> +
>> +static void hantro_av1_tiles_free(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_dev *vpu = ctx->dev;
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> +
>> + if (av1_dec->db_data_col.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->db_data_col.size,
>> + av1_dec->db_data_col.cpu,
>> + av1_dec->db_data_col.dma);
>> + av1_dec->db_data_col.cpu = NULL;
>> +
>> + if (av1_dec->db_ctrl_col.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->db_ctrl_col.size,
>> + av1_dec->db_ctrl_col.cpu,
>> + av1_dec->db_ctrl_col.dma);
>> + av1_dec->db_ctrl_col.cpu = NULL;
>> +
>> + if (av1_dec->cdef_col.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->cdef_col.size,
>> + av1_dec->cdef_col.cpu, av1_dec->cdef_col.dma);
>> + av1_dec->cdef_col.cpu = NULL;
>> +
>> + if (av1_dec->sr_col.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->sr_col.size,
>> + av1_dec->sr_col.cpu, av1_dec->sr_col.dma);
>> + av1_dec->sr_col.cpu = NULL;
>> +
>> + if (av1_dec->lr_col.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->lr_col.size,
>> + av1_dec->lr_col.cpu, av1_dec->lr_col.dma);
>> + av1_dec->lr_col.cpu = NULL;
>> +}
>> +
>> +static int hantro_av1_tiles_reallocate(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_dev *vpu = ctx->dev;
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
>> + unsigned int num_tile_cols = tile_info->tile_cols;
>> + unsigned int height = ALIGN(ctrls->frame->frame_height_minus_1 + 1, 64);
>> + unsigned int height_in_sb = height / 64;
>> + unsigned int stripe_num = ((height + 8) + 63) / 64;
>> + size_t size;
>> +
>> + if (av1_dec->db_data_col.size >=
>> + ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols)
>> + return 0;
>> +
>> + hantro_av1_tiles_free(ctx);
>> +
>> + size = ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols;
>> + av1_dec->db_data_col.cpu = dma_alloc_coherent(vpu->dev, size,
>> + &av1_dec->db_data_col.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->db_data_col.cpu)
>> + goto buffer_allocation_error;
>> + av1_dec->db_data_col.size = size;
>> +
>> + size = ALIGN(height * 2 * 16 / 4, 128) * num_tile_cols;
>> + av1_dec->db_ctrl_col.cpu = dma_alloc_coherent(vpu->dev, size,
>> + &av1_dec->db_ctrl_col.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->db_ctrl_col.cpu)
>> + goto buffer_allocation_error;
>> + av1_dec->db_ctrl_col.size = size;
>> +
>> + size = ALIGN(height_in_sb * 44 * ctx->bit_depth * 16 / 8, 128) * num_tile_cols;
>> + av1_dec->cdef_col.cpu = dma_alloc_coherent(vpu->dev, size,
>> + &av1_dec->cdef_col.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->cdef_col.cpu)
>> + goto buffer_allocation_error;
>> + av1_dec->cdef_col.size = size;
>> +
>> + size = ALIGN(height_in_sb * (3040 + 1280), 128) * num_tile_cols;
>> + av1_dec->sr_col.cpu = dma_alloc_coherent(vpu->dev, size,
>> + &av1_dec->sr_col.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->sr_col.cpu)
>> + goto buffer_allocation_error;
>> + av1_dec->sr_col.size = size;
>> +
>> + size = ALIGN(stripe_num * 1536 * ctx->bit_depth / 8, 128) * num_tile_cols;
>> + av1_dec->lr_col.cpu = dma_alloc_coherent(vpu->dev, size,
>> + &av1_dec->lr_col.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->lr_col.cpu)
>> + goto buffer_allocation_error;
>> + av1_dec->lr_col.size = size;
>> +
>> + av1_dec->num_tile_cols_allocated = num_tile_cols;
>> + return 0;
>> +
>> +buffer_allocation_error:
>> + hantro_av1_tiles_free(ctx);
>> + return -ENOMEM;
>> +}
>> +
>> +void hantro_av1_exit(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_dev *vpu = ctx->dev;
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> +
>> + if (av1_dec->global_model.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->global_model.size,
>> + av1_dec->global_model.cpu,
>> + av1_dec->global_model.dma);
>> + av1_dec->global_model.cpu = NULL;
>> +
>> + if (av1_dec->tile_info.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->tile_info.size,
>> + av1_dec->tile_info.cpu,
>> + av1_dec->tile_info.dma);
>> + av1_dec->tile_info.cpu = NULL;
>> +
>> + if (av1_dec->film_grain.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->film_grain.size,
>> + av1_dec->film_grain.cpu,
>> + av1_dec->film_grain.dma);
>> + av1_dec->film_grain.cpu = NULL;
>> +
>> + if (av1_dec->prob_tbl.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->prob_tbl.size,
>> + av1_dec->prob_tbl.cpu, av1_dec->prob_tbl.dma);
>> + av1_dec->prob_tbl.cpu = NULL;
>> +
>> + if (av1_dec->prob_tbl_out.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->prob_tbl_out.size,
>> + av1_dec->prob_tbl_out.cpu,
>> + av1_dec->prob_tbl_out.dma);
>> + av1_dec->prob_tbl_out.cpu = NULL;
>> +
>> + if (av1_dec->tile_buf.cpu)
>> + dma_free_coherent(vpu->dev, av1_dec->tile_buf.size,
>> + av1_dec->tile_buf.cpu, av1_dec->tile_buf.dma);
>> + av1_dec->tile_buf.cpu = NULL;
>> +
>> + hantro_av1_tiles_free(ctx);
>> +}
>> +
>> +int hantro_av1_init(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_dev *vpu = ctx->dev;
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> +
>> + memset(av1_dec, 0, sizeof(*av1_dec));
>> +
>> + av1_dec->global_model.cpu = dma_alloc_coherent(vpu->dev, GLOBAL_MODEL_SIZE,
>> + &av1_dec->global_model.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->global_model.cpu)
>> + return -ENOMEM;
>> + av1_dec->global_model.size = GLOBAL_MODEL_SIZE;
>> +
>> + av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_TILE_INFO_SIZE,
>> + &av1_dec->tile_info.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->tile_info.cpu)
>> + return -ENOMEM;
>> + av1_dec->tile_info.size = AV1_TILE_INFO_SIZE;
>> +
>> + av1_dec->film_grain.cpu = dma_alloc_coherent(vpu->dev,
>> + ALIGN(sizeof(struct hantro_av1_film_grain),
>> + 2048),
>> + &av1_dec->film_grain.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->film_grain.cpu)
>> + return -ENOMEM;
>> + av1_dec->film_grain.size = ALIGN(sizeof(struct hantro_av1_film_grain), 2048);
>> +
>> + av1_dec->prob_tbl.cpu = dma_alloc_coherent(vpu->dev,
>> + ALIGN(sizeof(struct av1cdfs), 2048),
>> + &av1_dec->prob_tbl.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->prob_tbl.cpu)
>> + return -ENOMEM;
>> + av1_dec->prob_tbl.size = ALIGN(sizeof(struct av1cdfs), 2048);
>> +
>> + av1_dec->prob_tbl_out.cpu = dma_alloc_coherent(vpu->dev,
>> + ALIGN(sizeof(struct av1cdfs), 2048),
>> + &av1_dec->prob_tbl_out.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->prob_tbl_out.cpu)
>> + return -ENOMEM;
>> + av1_dec->prob_tbl_out.size = ALIGN(sizeof(struct av1cdfs), 2048);
>> + av1_dec->cdfs = &av1_dec->default_cdfs;
>> + av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
>> +
>> + hantro_av1_set_default_cdfs(av1_dec->cdfs, av1_dec->cdfs_ndvc);
>> +
>> + av1_dec->tile_buf.cpu = dma_alloc_coherent(vpu->dev,
>> + AV1_TILE_SIZE,
>> + &av1_dec->tile_buf.dma,
>> + GFP_KERNEL);
>> + if (!av1_dec->tile_buf.cpu)
>> + return -ENOMEM;
>> + av1_dec->tile_buf.size = AV1_TILE_SIZE;
>> +
>> + return 0;
>> +}
>> +
>> +int hantro_av1_prepare_run(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> +
>> + ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
>> + if (WARN_ON(!ctrls->sequence))
>> + return -EINVAL;
>> +
>> + ctrls->tile_group_entry =
>> + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
>> + if (WARN_ON(!ctrls->tile_group_entry))
>> + return -EINVAL;
>> +
>> + ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME);
>> + if (WARN_ON(!ctrls->frame))
>> + return -EINVAL;
>> +
>> + ctrls->film_grain =
>> + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN);
>> +
>> + return hantro_av1_tiles_reallocate(ctx);
>> +}
>> +
>> +static int hantro_av1_get_msb(u32 n)
>> +{
>> + if (n == 0)
>> + return 0;
>> + return 31 ^ __builtin_clz(n);
>> +}
>> +
>> +static short hantro_av1_resolve_divisor_32(u32 d, short *shift)
>> +{
>> + int f;
>> + u64 e;
>> +
>> + *shift = hantro_av1_get_msb(d);
>> + /* e is obtained from D after resetting the most significant 1 bit. */
>> + e = d - ((u32)1 << *shift);
>> + /* Get the most significant DIV_LUT_BITS (8) bits of e into f */
>> + if (*shift > DIV_LUT_BITS)
>> + f = AV1_DIV_ROUND_UP_POW2(e, *shift - DIV_LUT_BITS);
>> + else
>> + f = e << (DIV_LUT_BITS - *shift);
>> + if (f > DIV_LUT_NUM)
>> + return -1;
>> + *shift += DIV_LUT_PREC_BITS;
>> + /* Use f as lookup into the precomputed table of multipliers */
>> + return div_lut[f];
>> +}
>> +
>> +static void hantro_av1_get_shear_params(const u32 *params, s64 *alpha,
>> + s64 *beta, s64 *gamma, s64 *delta)
>> +{
>> + const int *mat = params;
>> + short shift;
>> + short y;
>> + long long gv, dv;
>> +
>> + if (mat[2] <= 0)
>> + return;
>> +
>> + *alpha = clamp_val(mat[2] - (1 << WARPEDMODEL_PREC_BITS), S16_MIN, S16_MAX);
>> + *beta = clamp_val(mat[3], S16_MIN, S16_MAX);
>> +
>> + y = hantro_av1_resolve_divisor_32(abs(mat[2]), &shift) * (mat[2] < 0 ? -1 : 1);
>> +
>> + gv = ((long long)mat[4] * (1 << WARPEDMODEL_PREC_BITS)) * y;
>> +
>> + *gamma = clamp_val((int)AV1_DIV_ROUND_UP_POW2_SIGNED(gv, shift), S16_MIN, S16_MAX);
>> +
>> + dv = ((long long)mat[3] * mat[4]) * y;
>> + *delta = clamp_val(mat[5] -
>> + (int)AV1_DIV_ROUND_UP_POW2_SIGNED(dv, shift) - (1 << WARPEDMODEL_PREC_BITS),
>> + S16_MIN, S16_MAX);
>> +
>> + *alpha = AV1_DIV_ROUND_UP_POW2_SIGNED(*alpha, WARP_PARAM_REDUCE_BITS)
>> + * (1 << WARP_PARAM_REDUCE_BITS);
>> + *beta = AV1_DIV_ROUND_UP_POW2_SIGNED(*beta, WARP_PARAM_REDUCE_BITS)
>> + * (1 << WARP_PARAM_REDUCE_BITS);
>> + *gamma = AV1_DIV_ROUND_UP_POW2_SIGNED(*gamma, WARP_PARAM_REDUCE_BITS)
>> + * (1 << WARP_PARAM_REDUCE_BITS);
>> + *delta = AV1_DIV_ROUND_UP_POW2_SIGNED(*delta, WARP_PARAM_REDUCE_BITS)
>> + * (1 << WARP_PARAM_REDUCE_BITS);
>> +}
>> +
>> +void hantro_av1_set_global_model(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
>> + const struct v4l2_av1_global_motion *gm = &frame->global_motion;
>> + u8 *dst = av1_dec->global_model.cpu;
>> + int ref_frame, i;
>> +
>> + memset(dst, 0, GLOBAL_MODEL_SIZE);
>> + for (ref_frame = 0; ref_frame < V4L2_AV1_REFS_PER_FRAME; ++ref_frame) {
>> + s64 alpha = 0, beta = 0, gamma = 0, delta = 0;
>> +
>> + for (i = 0; i < 6; ++i) {
>> + if (i == 2)
>> + *(s32 *)dst =
>> + gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][3];
>> + else if (i == 3)
>> + *(s32 *)dst =
>> + gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][2];
>> + else
>> + *(s32 *)dst =
>> + gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][i];
>> + dst += 4;
>> + }
>> +
>> + if (gm->type[V4L2_AV1_REF_LAST_FRAME + ref_frame] <= V4L2_AV1_WARP_MODEL_AFFINE)
>> + hantro_av1_get_shear_params(&gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][0],
>> + &alpha, &beta, &gamma, &delta);
>> +
>> + *(s16 *)dst = alpha;
>> + dst += 2;
>> + *(s16 *)dst = beta;
>> + dst += 2;
>> + *(s16 *)dst = gamma;
>> + dst += 2;
>> + *(s16 *)dst = delta;
>> + dst += 2;
>> + }
>> +}
>> +
>> +int hantro_av1_tile_log2(int target)
>> +{
>> + int k;
>> +
>> + /*
>> + * returns the smallest value for k such that 1 << k is greater
>> + * than or equal to target
>> + */
>> + for (k = 0; (1 << k) < target; k++)
>> + ;
>> +
>> + return k;
>> +}
>> +
>> +int hantro_av1_get_dist(struct hantro_ctx *ctx, int a, int b)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + int bits = ctrls->sequence->order_hint_bits - 1;
>> + int diff, m;
>> +
>> + if (!ctrls->sequence->order_hint_bits)
>> + return 0;
>> +
>> + diff = a - b;
>> + m = 1 << bits;
>> + diff = (diff & (m - 1)) - (diff & m);
>> +
>> + return diff;
>> +}
>> +
>> +void hantro_av1_set_frame_sign_bias(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
>> + const struct v4l2_ctrl_av1_sequence *sequence = ctrls->sequence;
>> + int i;
>> +
>> + if (!sequence->order_hint_bits || IS_INTRA(frame->frame_type)) {
>> + for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++)
>> + av1_dec->ref_frame_sign_bias[i] = 0;
>> +
>> + return;
>> + }
>> + // Identify the nearest forward and backward references.
>> + for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME - 1; i++) {
>> + if (hantro_av1_get_frame_index(ctx, i) >= 0) {
>> + int rel_off =
>> + hantro_av1_get_dist(ctx,
>> + hantro_av1_get_order_hint(ctx, i),
>> + frame->order_hint);
>> + av1_dec->ref_frame_sign_bias[i + 1] = (rel_off <= 0) ? 0 : 1;
>> + }
>> + }
>> +}
>> +
>> +void hantro_av1_init_scaling_function(const u8 *values, const u8 *scaling,
>> + u8 num_points, u8 *scaling_lut)
>> +{
>> + int i, point;
>> +
>> + if (num_points == 0) {
>> + memset(scaling_lut, 0, 256);
>> + return;
>> + }
>> +
>> + for (point = 0; point < num_points - 1; point++) {
>> + int x;
>> + s32 delta_y = scaling[point + 1] - scaling[point];
>> + s32 delta_x = values[point + 1] - values[point];
>> + s64 delta =
>> + delta_x ? delta_y * ((65536 + (delta_x >> 1)) /
>> + delta_x) : 0;
>> +
>> + for (x = 0; x < delta_x; x++) {
>> + scaling_lut[values[point] + x] =
>> + scaling[point] +
>> + (s32)((x * delta + 32768) >> 16);
>> + }
>> + }
>> +
>> + for (i = values[num_points - 1]; i < 256; i++)
>> + scaling_lut[i] = scaling[num_points - 1];
>> +}
>> +
>> +void hantro_av1_set_tile_info(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
>> + const struct v4l2_ctrl_av1_tile_group_entry *group_entry =
>> + ctrls->tile_group_entry;
>> + u8 *dst = av1_dec->tile_info.cpu;
>> + int tile0, tile1;
>> +
>> + memset(dst, 0, av1_dec->tile_info.size);
>> +
>> + for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) {
>> + for (tile1 = 0; tile1 < tile_info->tile_rows; tile1++) {
>> + int tile_id = tile1 * tile_info->tile_cols + tile0;
>> + u32 start, end;
>> + u32 y0 =
>> + tile_info->height_in_sbs_minus_1[tile1] + 1;
>> + u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1;
>> +
>> + /* tile size in SB units (width,height) */
>> + *dst++ = x0;
>> + *dst++ = 0;
>> + *dst++ = 0;
>> + *dst++ = 0;
>> + *dst++ = y0;
>> + *dst++ = 0;
>> + *dst++ = 0;
>> + *dst++ = 0;
>> +
>> + /* tile start position */
>> + start = group_entry[tile_id].tile_offset - group_entry[0].tile_offset;
>> + *dst++ = start & 255;
>> + *dst++ = (start >> 8) & 255;
>> + *dst++ = (start >> 16) & 255;
>> + *dst++ = (start >> 24) & 255;
>> +
>> + /* number of bytes in tile data */
>> + end = start + group_entry[tile_id].tile_size;
>> + *dst++ = end & 255;
>> + *dst++ = (end >> 8) & 255;
>> + *dst++ = (end >> 16) & 255;
>> + *dst++ = (end >> 24) & 255;
>> + }
>> + }
>> +}
>> +
>> +bool hantro_av1_is_lossless(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
>> + const struct v4l2_av1_segmentation *segmentation = &frame->segmentation;
>> + const struct v4l2_av1_quantization *quantization = &frame->quantization;
>> + int i;
>> +
>> + for (i = 0; i < V4L2_AV1_MAX_SEGMENTS; i++) {
>> + int qindex = quantization->base_q_idx;
>> +
>> + if (segmentation->feature_enabled[i] &
>> + V4L2_AV1_SEGMENT_FEATURE_ENABLED(V4L2_AV1_SEG_LVL_ALT_Q)) {
>> + qindex += segmentation->feature_data[i][V4L2_AV1_SEG_LVL_ALT_Q];
>> + }
>> + qindex = clamp(qindex, 0, 255);
>> +
>> + if (qindex ||
>> + quantization->delta_q_y_dc ||
>> + quantization->delta_q_u_dc ||
>> + quantization->delta_q_u_ac ||
>> + quantization->delta_q_v_dc ||
>> + quantization->delta_q_v_ac)
>> + return false;
>> + }
>> +
>> + return true;
>> +}
>> +
>> +void hantro_av1_update_prob(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
>> + bool frame_is_intra = IS_INTRA(frame->frame_type);
>> + struct av1cdfs *out_cdfs = (struct av1cdfs *)av1_dec->prob_tbl_out.cpu;
>> + int i;
>> +
>> + if (frame->flags & V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF)
>> + return;
>> +
>> + for (i = 0; i < NUM_REF_FRAMES; i++) {
>> + if (frame->refresh_frame_flags & BIT(i)) {
>> + struct mvcdfs stored_mv_cdf;
>> +
>> + hantro_av1_get_cdfs(ctx, i);
>> + stored_mv_cdf = av1_dec->cdfs->mv_cdf;
>> + *av1_dec->cdfs = *out_cdfs;
>> + if (frame_is_intra) {
>> + av1_dec->cdfs->mv_cdf = stored_mv_cdf;
>> + *av1_dec->cdfs_ndvc = out_cdfs->mv_cdf;
>> + }
>> + hantro_av1_store_cdfs(ctx, frame->refresh_frame_flags);
>> + break;
>> + }
>> + }
>> +}
>> +
>> +void hantro_av1_set_prob(struct hantro_ctx *ctx)
>> +{
>> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
>> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
>> + const struct v4l2_av1_quantization *quantization = &frame->quantization;
>> + bool error_resilient_mode =
>> + !!(frame->flags & V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE);
>> + bool frame_is_intra = IS_INTRA(frame->frame_type);
>> +
>> + if (error_resilient_mode || frame_is_intra ||
>> + frame->primary_ref_frame == AV1_PRIMARY_REF_NONE) {
>> + av1_dec->cdfs = &av1_dec->default_cdfs;
>> + av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
>> + hantro_av1_default_coeff_probs(quantization->base_q_idx,
>> + av1_dec->cdfs);
>> + } else {
>> + hantro_av1_get_cdfs(ctx, frame->ref_frame_idx[frame->primary_ref_frame]);
> There is no sanity check for frame->primary_ref_frame to prevent out of bounds
> array access. But perhaps this has to be checked elsewhere?
>
>> + }
>> + hantro_av1_store_cdfs(ctx, frame->refresh_frame_flags);
>> +
>> + memcpy(av1_dec->prob_tbl.cpu, av1_dec->cdfs, sizeof(struct av1cdfs));
>> +
>> + if (frame_is_intra) {
>> + int mv_offset = offsetof(struct av1cdfs, mv_cdf);
>> + /* Overwrite MV context area with intrabc MV context */
>> + memcpy(av1_dec->prob_tbl.cpu + mv_offset, av1_dec->cdfs_ndvc,
>> + sizeof(struct mvcdfs));
>> + }
>> +}
> <snip>
>
>> diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
>> index e4e21ad37323..5bf1f2689fbc 100644
>> --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
>> +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
>> @@ -7,622 +7,35 @@
> <snip>
>
>> static void rockchip_vpu981_av1_dec_set_cdef(struct hantro_ctx *ctx)
>> @@ -1617,12 +846,12 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> int ref_ind = 0;
>> int rf, idx;
>>
>> - alt_frame_offset = rockchip_vpu981_get_order_hint(ctx, ALT_BUF_IDX);
>> - gld_frame_offset = rockchip_vpu981_get_order_hint(ctx, GLD_BUF_IDX);
>> - bwd_frame_offset = rockchip_vpu981_get_order_hint(ctx, BWD_BUF_IDX);
>> - alt2_frame_offset = rockchip_vpu981_get_order_hint(ctx, ALT2_BUF_IDX);
>> + alt_frame_offset = hantro_av1_get_order_hint(ctx, ALT_BUF_IDX);
>> + gld_frame_offset = hantro_av1_get_order_hint(ctx, GLD_BUF_IDX);
>> + bwd_frame_offset = hantro_av1_get_order_hint(ctx, BWD_BUF_IDX);
>> + alt2_frame_offset = hantro_av1_get_order_hint(ctx, ALT2_BUF_IDX);
>>
>> - idx = rockchip_vpu981_get_frame_index(ctx, LST_BUF_IDX);
>> + idx = hantro_av1_get_frame_index(ctx, LST_BUF_IDX);
>> if (idx >= 0) {
>> int alt_frame_offset_in_lst =
>> av1_dec->frame_refs[idx].order_hints[V4L2_AV1_REF_ALTREF_FRAME];
>> @@ -1644,8 +873,8 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> ref_stamp--;
>> }
>>
>> - idx = rockchip_vpu981_get_frame_index(ctx, BWD_BUF_IDX);
>> - if (rockchip_vpu981_av1_dec_get_dist(ctx, bwd_frame_offset, cur_frame_offset) > 0) {
>> + idx = hantro_av1_get_frame_index(ctx, BWD_BUF_IDX);
> Can idx be AV1_INVALID_IDX? It's checked for LST_BUF_IDX, but not for BWD_BUF_IDX and others below.
>
>> + if (hantro_av1_get_dist(ctx, bwd_frame_offset, cur_frame_offset) > 0) {
>> int bwd_mi_cols = av1_dec->frame_refs[idx].mi_cols;
>> int bwd_mi_rows = av1_dec->frame_refs[idx].mi_rows;
>> bool bwd_intra_only =
>> @@ -1659,8 +888,8 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> }
>> }
>>
>> - idx = rockchip_vpu981_get_frame_index(ctx, ALT2_BUF_IDX);
>> - if (rockchip_vpu981_av1_dec_get_dist(ctx, alt2_frame_offset, cur_frame_offset) > 0) {
>> + idx = hantro_av1_get_frame_index(ctx, ALT2_BUF_IDX);
>> + if (hantro_av1_get_dist(ctx, alt2_frame_offset, cur_frame_offset) > 0) {
>> int alt2_mi_cols = av1_dec->frame_refs[idx].mi_cols;
>> int alt2_mi_rows = av1_dec->frame_refs[idx].mi_rows;
>> bool alt2_intra_only =
>> @@ -1674,8 +903,8 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> }
>> }
>>
>> - idx = rockchip_vpu981_get_frame_index(ctx, ALT_BUF_IDX);
>> - if (rockchip_vpu981_av1_dec_get_dist(ctx, alt_frame_offset, cur_frame_offset) > 0 &&
>> + idx = hantro_av1_get_frame_index(ctx, ALT_BUF_IDX);
>> + if (hantro_av1_get_dist(ctx, alt_frame_offset, cur_frame_offset) > 0 &&
>> ref_stamp >= 0) {
>> int alt_mi_cols = av1_dec->frame_refs[idx].mi_cols;
>> int alt_mi_rows = av1_dec->frame_refs[idx].mi_rows;
>> @@ -1690,7 +919,7 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> }
>> }
>>
>> - idx = rockchip_vpu981_get_frame_index(ctx, LST2_BUF_IDX);
>> + idx = hantro_av1_get_frame_index(ctx, LST2_BUF_IDX);
>> if (idx >= 0 && ref_stamp >= 0) {
>> int lst2_mi_cols = av1_dec->frame_refs[idx].mi_cols;
>> int lst2_mi_rows = av1_dec->frame_refs[idx].mi_rows;
>> @@ -1706,14 +935,14 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> }
>>
>> for (rf = 0; rf < V4L2_AV1_TOTAL_REFS_PER_FRAME - 1; ++rf) {
>> - idx = rockchip_vpu981_get_frame_index(ctx, rf);
>> + idx = hantro_av1_get_frame_index(ctx, rf);
>> if (idx >= 0) {
>> - int rf_order_hint = rockchip_vpu981_get_order_hint(ctx, rf);
>> + int rf_order_hint = hantro_av1_get_order_hint(ctx, rf);
>>
>> cur_offset[rf] =
>> - rockchip_vpu981_av1_dec_get_dist(ctx, cur_frame_offset, rf_order_hint);
>> + hantro_av1_get_dist(ctx, cur_frame_offset, rf_order_hint);
>> cur_roffset[rf] =
>> - rockchip_vpu981_av1_dec_get_dist(ctx, rf_order_hint, cur_frame_offset);
>> + hantro_av1_get_dist(ctx, rf_order_hint, cur_frame_offset);
>> } else {
>> cur_offset[rf] = 0;
>> cur_roffset[rf] = 0;
>> @@ -1736,32 +965,32 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> if (use_ref_frame_mvs && ref_ind > 0 &&
>> cur_offset[mf_types[0] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
>> cur_offset[mf_types[0] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
>> - int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[0]);
>> - int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[0]);
>> + int rf = hantro_av1_get_order_hint(ctx, refs_selected[0]);
>> + int idx = hantro_av1_get_frame_index(ctx, refs_selected[0]);
> There is no AV1_INVALID_IDX check here either.
>
> I think it might be helpful if there is a comment in the cases where idx is always
> valid. I don't know enough about this to be able to tell if it is a potential
> out-of-bounds access or not.
>
>> u32 *oh = av1_dec->frame_refs[idx].order_hints;
>> int val;
>>
>> hantro_reg_write(vpu, &av1_use_temporal0_mvs, 1);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
>> hantro_reg_write(vpu, &av1_mf1_last_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
>> hantro_reg_write(vpu, &av1_mf1_last2_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
>> hantro_reg_write(vpu, &av1_mf1_last3_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
>> hantro_reg_write(vpu, &av1_mf1_golden_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
>> hantro_reg_write(vpu, &av1_mf1_bwdref_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
>> hantro_reg_write(vpu, &av1_mf1_altref2_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
>> hantro_reg_write(vpu, &av1_mf1_altref_offset, val);
>> }
>>
>> @@ -1776,32 +1005,32 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> if (use_ref_frame_mvs && ref_ind > 1 &&
>> cur_offset[mf_types[1] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
>> cur_offset[mf_types[1] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
>> - int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[1]);
>> - int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[1]);
>> + int rf = hantro_av1_get_order_hint(ctx, refs_selected[1]);
>> + int idx = hantro_av1_get_frame_index(ctx, refs_selected[1]);
> Ditto.
>
>> u32 *oh = av1_dec->frame_refs[idx].order_hints;
>> int val;
>>
>> hantro_reg_write(vpu, &av1_use_temporal1_mvs, 1);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
>> hantro_reg_write(vpu, &av1_mf2_last_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
>> hantro_reg_write(vpu, &av1_mf2_last2_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
>> hantro_reg_write(vpu, &av1_mf2_last3_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
>> hantro_reg_write(vpu, &av1_mf2_golden_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
>> hantro_reg_write(vpu, &av1_mf2_bwdref_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
>> hantro_reg_write(vpu, &av1_mf2_altref2_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
>> hantro_reg_write(vpu, &av1_mf2_altref_offset, val);
>> }
>>
>> @@ -1816,32 +1045,32 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
>> if (use_ref_frame_mvs && ref_ind > 2 &&
>> cur_offset[mf_types[2] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
>> cur_offset[mf_types[2] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
>> - int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[2]);
>> - int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[2]);
>> + int rf = hantro_av1_get_order_hint(ctx, refs_selected[2]);
>> + int idx = hantro_av1_get_frame_index(ctx, refs_selected[2]);
> Ditto.
>
>> u32 *oh = av1_dec->frame_refs[idx].order_hints;
>> int val;
>>
>> hantro_reg_write(vpu, &av1_use_temporal2_mvs, 1);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
>> hantro_reg_write(vpu, &av1_mf3_last_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
>> hantro_reg_write(vpu, &av1_mf3_last2_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
>> hantro_reg_write(vpu, &av1_mf3_last3_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
>> hantro_reg_write(vpu, &av1_mf3_golden_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
>> hantro_reg_write(vpu, &av1_mf3_bwdref_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
>> hantro_reg_write(vpu, &av1_mf3_altref2_offset, val);
>>
>> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
>> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
>> hantro_reg_write(vpu, &av1_mf3_altref_offset, val);
>> }
>>
>> @@ -1883,7 +1112,7 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
>>
>> if (!allow_intrabc) {
>> for (i = 0; i < V4L2_AV1_REFS_PER_FRAME; i++) {
>> - int idx = rockchip_vpu981_get_frame_index(ctx, i);
>> + int idx = hantro_av1_get_frame_index(ctx, i);
>>
>> if (idx >= 0)
>> ref_count[idx]++;
>> @@ -1898,7 +1127,7 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
>> }
>> hantro_reg_write(vpu, &av1_ref_frames, ref_frames);
>>
>> - rockchip_vpu981_av1_dec_set_frame_sign_bias(ctx);
>> + hantro_av1_set_frame_sign_bias(ctx);
>>
>> for (i = V4L2_AV1_REF_LAST_FRAME; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++) {
>> u32 ref = i - 1;
>> @@ -1910,8 +1139,8 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
>> width = frame->frame_width_minus_1 + 1;
>> height = frame->frame_height_minus_1 + 1;
>> } else {
>> - if (rockchip_vpu981_get_frame_index(ctx, ref) > 0)
>> - idx = rockchip_vpu981_get_frame_index(ctx, ref);
>> + if (hantro_av1_get_frame_index(ctx, ref) > 0)
>> + idx = hantro_av1_get_frame_index(ctx, ref);
> Can idx be AV1_INVALID_IDX here?
>
>> width = av1_dec->frame_refs[idx].width;
>> height = av1_dec->frame_refs[idx].height;
>> }
>> @@ -1943,20 +1172,6 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
>> rockchip_vpu981_av1_dec_set_other_frames(ctx);
>> }
>>
>> -static int rockchip_vpu981_av1_get_hardware_tx_mode(enum v4l2_av1_tx_mode tx_mode)
>> -{
>> - switch (tx_mode) {
>> - case V4L2_AV1_TX_MODE_ONLY_4X4:
>> - return ROCKCHIP_AV1_TX_MODE_ONLY_4X4;
>> - case V4L2_AV1_TX_MODE_LARGEST:
>> - return ROCKCHIP_AV1_TX_MODE_32x32;
>> - case V4L2_AV1_TX_MODE_SELECT:
>> - return ROCKCHIP_AV1_TX_MODE_SELECT;
>> - }
>> -
>> - return ROCKCHIP_AV1_TX_MODE_32x32;
>> -}
>> -
>> static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
>> {
>> struct hantro_dev *vpu = ctx->dev;
>> @@ -2029,7 +1244,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
>> hantro_reg_write(vpu, &av1_comp_pred_mode,
>> (ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT) ? 2 : 0);
>>
>> - tx_mode = rockchip_vpu981_av1_get_hardware_tx_mode(ctrls->frame->tx_mode);
>> + tx_mode = hantro_av1_get_hardware_tx_mode(ctrls->frame->tx_mode);
>> hantro_reg_write(vpu, &av1_transform_mode, tx_mode);
>> hantro_reg_write(vpu, &av1_max_cb_size,
>> (ctrls->sequence->flags
>> @@ -2061,7 +1276,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
>> hantro_reg_write(vpu, &av1_qmlevel_v, 0xff);
>> }
>>
>> - hantro_reg_write(vpu, &av1_lossless_e, rockchip_vpu981_av1_dec_is_lossless(ctx));
>> + hantro_reg_write(vpu, &av1_lossless_e, hantro_av1_is_lossless(ctx));
>> hantro_reg_write(vpu, &av1_quant_delta_v_dc, ctrls->frame->quantization.delta_q_v_dc);
>> hantro_reg_write(vpu, &av1_quant_delta_v_ac, ctrls->frame->quantization.delta_q_v_ac);
>>
>> @@ -2109,8 +1324,8 @@ rockchip_vpu981_av1_dec_set_output_buffer(struct hantro_ctx *ctx)
>> struct hantro_decoded_buffer *dst;
>> struct vb2_v4l2_buffer *vb2_dst;
>> dma_addr_t luma_addr, chroma_addr, mv_addr = 0;
>> - size_t cr_offset = rockchip_vpu981_av1_dec_luma_size(ctx);
>> - size_t mv_offset = rockchip_vpu981_av1_dec_chroma_size(ctx);
>> + size_t cr_offset = hantro_av1_luma_size(ctx);
>> + size_t mv_offset = hantro_av1_chroma_size(ctx);
>>
>> vb2_dst = av1_dec->frame_refs[av1_dec->current_frame_index].vb2_ref;
>> dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf);
>> @@ -2134,7 +1349,7 @@ int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx)
>>
>> hantro_start_prepare_run(ctx);
>>
>> - ret = rockchip_vpu981_av1_dec_prepare_run(ctx);
>> + ret = hantro_av1_prepare_run(ctx);
>> if (ret)
>> goto prepare_error;
>>
>> @@ -2144,8 +1359,8 @@ int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx)
>> goto prepare_error;
>> }
>>
>> - rockchip_vpu981_av1_dec_clean_refs(ctx);
>> - rockchip_vpu981_av1_dec_frame_ref(ctx, vb2_src->vb2_buf.timestamp);
>> + hantro_av1_clean_refs(ctx);
>> + hantro_av1_frame_ref(ctx, vb2_src->vb2_buf.timestamp);
>>
>> rockchip_vpu981_av1_dec_set_parameters(ctx);
>> rockchip_vpu981_av1_dec_set_global_model(ctx);
> Regards,
>
> Hans
>
^ permalink raw reply
* Re: [PATCH net-next 1/2] net: ti: icssg: Derive stats array lengths from ARRAY_SIZE
From: David CARLIER @ 2026-05-12 7:58 UTC (permalink / raw)
To: MD Danish Anwar
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, Roger Quadros,
Andrew Lunn, Jacob Keller, Meghana Malladi, Kevin Hao,
Vadim Fedorenko, netdev, linux-doc, linux-kernel,
linux-arm-kernel, Vignesh Raghavendra
In-Reply-To: <20260512060627.3781329-2-danishanwar@ti.com>
Hi MD,
On Tue, 12 May 2026 at 07:06, MD Danish Anwar <danishanwar@ti.com> wrote:
>
> Replace the manually maintained ICSSG_NUM_MIIG_STATS and
> ICSSG_NUM_PA_STATS constants with ARRAY_SIZE() expressions derived
> directly from the corresponding stat descriptor arrays, so that adding
> new entries to icssg_all_miig_stats[] or icssg_all_pa_stats[] no longer
> requires a separate update to a numeric constant.
>
> To make this self-contained, break the circular include dependency
> between icssg_stats.h and icssg_prueth.h:
>
> - icssg_stats.h previously included icssg_prueth.h (transitively
> pulling in icssg_switch_map.h and ETH_GSTRING_LEN). Replace that
> with direct includes of <linux/ethtool.h>, <linux/kernel.h> and
> "icssg_switch_map.h".
>
> - icssg_prueth.h now includes icssg_stats.h, giving it access to
> the ARRAY_SIZE-based ICSSG_NUM_MIIG_STATS and ICSSG_NUM_PA_STATS
> before they are used in the prueth_emac struct and ICSSG_NUM_STATS.
>
> Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> ---
> drivers/net/ethernet/ti/icssg/icssg_prueth.h | 3 +--
> drivers/net/ethernet/ti/icssg/icssg_stats.h | 7 ++++++-
> 2 files changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> index df93d15c5b78..e2ccecb0a0dd 100644
> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> @@ -43,6 +43,7 @@
>
> #include "icssg_config.h"
> #include "icss_iep.h"
> +#include "icssg_stats.h"
> #include "icssg_switch_map.h"
>
> #define PRUETH_MAX_MTU (2000 - ETH_HLEN - ETH_FCS_LEN)
> @@ -57,8 +58,6 @@
>
> #define ICSSG_MAX_RFLOWS 8 /* per slice */
>
> -#define ICSSG_NUM_PA_STATS 32
> -#define ICSSG_NUM_MIIG_STATS 60
> /* Number of ICSSG related stats */
> #define ICSSG_NUM_STATS (ICSSG_NUM_MIIG_STATS + ICSSG_NUM_PA_STATS)
> #define ICSSG_NUM_STANDARD_STATS 31
> diff --git a/drivers/net/ethernet/ti/icssg/icssg_stats.h b/drivers/net/ethernet/ti/icssg/icssg_stats.h
> index 5ec0b38e0c67..b854eb587c1e 100644
> --- a/drivers/net/ethernet/ti/icssg/icssg_stats.h
> +++ b/drivers/net/ethernet/ti/icssg/icssg_stats.h
> @@ -8,10 +8,15 @@
> #ifndef __NET_TI_ICSSG_STATS_H
> #define __NET_TI_ICSSG_STATS_H
>
> -#include "icssg_prueth.h"
> +#include <linux/ethtool.h>
> +#include <linux/kernel.h>
> +#include "icssg_switch_map.h"
>
> #define STATS_TIME_LIMIT_1G_MS 25000 /* 25 seconds @ 1G */
>
> +#define ICSSG_NUM_MIIG_STATS ARRAY_SIZE(icssg_all_miig_stats)
> +#define ICSSG_NUM_PA_STATS ARRAY_SIZE(icssg_all_pa_stats)
> +
> struct miig_stats_regs {
> /* Rx */
> u32 rx_packets;
> --
> 2.34.1
>
One thing that caught my eye: icssg_all_miig_stats[] and
icssg_all_pa_stats[] are 'static const' arrays in icssg_stats.h with
ETH_GSTRING_LEN name buffers per entry. Right now only icssg_stats.c
and icssg_ethtool.c pull them in. After this patch icssg_prueth.h
includes icssg_stats.h, so every .c in the driver (classifier,
common, config, mii_cfg, queues, switchdev, ...) ends up with its own
static-const copy of both tables.
Would a static_assert() work for what you're after? Something like:
static const struct icssg_miig_stats icssg_all_miig_stats[] = {
...
};
static_assert(ARRAY_SIZE(icssg_all_miig_stats) == ICSSG_NUM_MIIG_STATS);
next to each array, keeping the numeric #defines as-is. Then 2/2 fails
to build the moment a new entry is added without bumping the count,
which is the case you're guarding against — without touching the
include graph.
What do you think ?
Cheers.
^ permalink raw reply
* Re: [PATCH 1/4] ARM: dts: imx6qdl-sabrelite: add mdio phy address 0
From: Francesco Dolcini @ 2026-05-12 7:52 UTC (permalink / raw)
To: Andrew Lunn
Cc: Frank Li, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, devicetree,
imx, linux-arm-kernel, linux-kernel
In-Reply-To: <f04fadf2-8d73-435b-b713-9d07e48e80ae@lunn.ch>
On Tue, May 12, 2026 at 12:15:08AM +0200, Andrew Lunn wrote:
> On Mon, May 11, 2026 at 05:04:56PM -0400, Frank Li wrote:
> > According to IEEE 802.3 Clause 22.2.4.5.5 PHYAD (PHY Address), A PHY that
> > is connected to the station management entity via the mechanical interface
> > defined in 22.6 shall always respond to transactions addressed to PHY
> > Address zero <00000>.
>
> Did you read 22.6? I've not seen a mechanical interface as defined in
> 22.6 for at least 20 years, maybe 30 years.
>
> That cause does not apply in this context.
>
> > - ethphy: ethernet-phy {
> > + ethphy: ethernet-phy@0 {
> > compatible = "ethernet-phy-ieee802.3-c22";
> > + reg = <0>;
>
> This could very well break this board. Without a reg value, the core
> will find the first PHY on the bus, at whatever address it is at. If
> you hard code 0, the PHY must be at 0, otherwise it will not be found.
Whoops, I replied without reading your answer first.
Yes, I agree, this change will break the board functionality.
^ permalink raw reply
* Re: [PATCH 3/4] remoteproc: add helper for optional ELF resource tables
From: Daniel Baluta @ 2026-05-12 7:55 UTC (permalink / raw)
To: Ben Levinsky, Bjorn Andersson, Mathieu Poirier, linux-remoteproc
Cc: Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Geert Uytterhoeven, Magnus Damm, Patrice Chotard, Maxime Coquelin,
Alexandre Torgue, imx, linux-arm-kernel, linux-kernel,
linux-renesas-soc, linux-stm32, tanmay.shah
In-Reply-To: <20260511211841.284809-4-ben.levinsky@amd.com>
On 5/12/26 00:18, Ben Levinsky wrote:
> [You don't often get email from ben.levinsky@amd.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> Add a small helper around rproc_elf_load_rsc_table() for remoteproc
> drivers that treat a missing ELF resource table as optional. The helper
> returns success on -EINVAL and propagates other failures unchanged.
>
> Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
> ---
> drivers/remoteproc/remoteproc_internal.h | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> index 3724a47a9748..dff87e468837 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -146,6 +146,18 @@ static inline int rproc_mem_entry_iounmap(struct rproc *rproc,
> return 0;
> }
>
> +static inline int rproc_elf_load_rsc_table_optional(struct rproc *rproc,
> + const struct firmware *fw)
> +{
> + int ret;
> +
> + ret = rproc_elf_load_rsc_table(rproc, fw);
> + if (ret == -EINVAL)
> + dev_dbg(&rproc->dev, "no resource table found\n");
You are changing loglevel here. Initial drivers use dev_info or dev_warn. At least I'm used
with seeing this messages in the logs.
So, what do you think on adding at least dev_info to this instead of dev_dbg?
> +
> + return ret == -EINVAL ? 0 : ret;
> +}
> +
> static inline int rproc_prepare_device(struct rproc *rproc)
> {
> if (rproc->ops->prepare)
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH 1/4] ARM: dts: imx6qdl-sabrelite: add mdio phy address 0
From: Francesco Dolcini @ 2026-05-12 7:51 UTC (permalink / raw)
To: Frank Li
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, devicetree, imx,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260511-b4-imx25_dts_simple_warning-v1-1-01b855a5ce25@nxp.com>
On Mon, May 11, 2026 at 05:04:56PM -0400, Frank Li wrote:
> According to IEEE 802.3 Clause 22.2.4.5.5 PHYAD (PHY Address), A PHY that
> is connected to the station management entity via the mechanical interface
> defined in 22.6 shall always respond to transactions addressed to PHY
> Address zero <00000>.
>
> Use address 0 is safe because only one MDIO phy under mdio bus.
I do not think this is generally safe.
Not all ethernet phy answer to the 0 broadcast address. The one that I
have personally worked with consider 0 a unicast address.
Francesco
^ permalink raw reply
* Re: [PATCH 07/16] arm64: dts: exynos: Add EL2 virtual timer interrupt
From: Jesper Nilsson @ 2026-05-12 7:47 UTC (permalink / raw)
To: Marc Zyngier
Cc: linux-arm-kernel, linux-acpi, linux-kernel, devicetree,
Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J. Wysocki, Mark Rutland, Daniel Lezcano,
Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Neil Armstrong,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Ge Gordon,
BST Linux Kernel Upstream Group, Jesper Nilsson, Lars Persson,
Alim Akhtar, Ivaylo Ivanov, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Dinh Nguyen,
Matthias Brugger, AngeloGioacchino Del Regno, Thierry Reding,
Jonathan Hunter, Bjorn Andersson, Konrad Dybcio,
Andreas Färber, Heiko Stuebner, Shawn Lin, Orson Zhai,
Baolin Wang, Michal Simek
In-Reply-To: <20260507125544.2903406-8-maz@kernel.org>
On Thu, May 07, 2026 at 01:55:35PM +0100, Marc Zyngier wrote
> [Some people who received this message don't often get email from maz@kernel.org. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> A bunch of Samsung SoCs are missing the EL2 virtual timer interrupt
> despite using ARMv8.1+ CPUs. Add the missing interrupt, except for
> those broken designs where the interrupt is documented as not being
> wired.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
> arch/arm64/boot/dts/exynos/axis/artpec9.dtsi | 3 ++-
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
/^JN - Jesper Nilsson
--
Jesper Nilsson -- jesper.nilsson@axis.com
^ permalink raw reply
* Re: [PATCH 2/2] drm/verisilicon: add support for Nuvoton MA35D1 DCUltra Lite display controller
From: Joey Lu @ 2026-05-12 7:45 UTC (permalink / raw)
To: Icenowy Zheng, maarten.lankhorst, mripard, tzimmermann, airlied,
simona, robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <93e69179dbc495188cfffd8015350b3a55ce7876.camel@iscas.ac.cn>
On 5/11/2026 5:47 PM, Icenowy Zheng wrote:
> 在 2026-05-11一的 15:51 +0800,Joey Lu写道:
>> The Nuvoton MA35D1 SoC integrates a Verisilicon DCUltra Lite display
>> controller, which is a previous generation of the DC8000 series.
>> While
>> the general register layout is similar to the DC8000, there are
>> several
>> key differences that require per-variant handling in the driver.
>>
>> Add a vs_dc_info platform data structure (in vs_hwdb.h) to describe
>> per-IP-variant capabilities, and use it throughout the driver to
>> select
>> the correct code paths at runtime.
>>
>> Key differences between DC8000 and DCUltra Lite handled:
> What the driver supports now is DC8200, DC8000 have the following point
> 1~4 the same with DCUltraLite (different to DC8200).
Understood. I'll rename all `vs_dc8000_*` identifiers to
`vs_dc8200_*` in v2. The commit message will also be corrected to say
that points 1~4 are differences from DC8200, not DC8000.
>> 1. No chip identity registers (0x0020-0x0030): DCUltra Lite uses
>> static
>> platform data instead of reading model/revision/customer_id from
>> HW.
> My test shows that revision and customer_id is correctly present, only
> model is 0 -- I think this can be also considered as a valid model
> value because the IP name has also no model number.
>
> The revision number is 0x5560 and customer id is 0x305 .
Thank you for testing. I'll drop the `has_chip_id` flag
entirely. In v2, `vs_fill_chip_identity()` will be called for all
variants. A new entry will be added to `vs_chip_identities[]` in
vs_hwdb.c with model=0x0, revision=0x5560, customer_id=0x305,
display_count=1, and `vs_formats_no_yuv444`.
e.g. verisilicon-dc 40260000.display: Found DC0 rev 5560 customer 305
>
>> 2. No CONFIG_EX commit mechanism: DC8000 uses registers at 0x1CC0
>> (FB_CONFIG_EX), 0x24D8 (FB_TOP_LEFT), 0x24E0 (FB_BOTTOM_RIGHT),
>> 0x2510 (FB_BLEND_CONFIG), 0x2518 (PANEL_CONFIG_EX). DCUltra Lite
>> omits all of these and instead uses enable/reset bits in FB_CONFIG
>> (bit 0 = enable, bit 4 = reset) for direct framebuffer updates.
>>
>> 3. No PANEL_START register (0x1CCC): DCUltra Lite panel output starts
>> when PANEL_CONFIG.RUNNING is set; no separate multi-display sync
>> start register is needed.
>>
>> 4. Different IRQ registers: DCUltra Lite uses 0x147C (IRQ_STA) /
>> 0x1480 (IRQ_EN); DC8000 uses 0x0010 (IRQ_ACK) / 0x0014 (IRQ_EN).
>>
>> 5. Different clock/reset topology: DCUltra Lite requires only "core"
>> (bus gate) and "pix0" (pixel divider) clocks with no reset lines
>> managed by the driver. DC8000 needs core/axi/ahb clocks and three
>> resets.
> It's possible that your SoC integration combines core clock gate with
> bus clock gate instead of bus clock gate not existing.
Agreed. In v2 I'll remove the family-gated clock handling and
instead use `devm_clk_get_optional_enabled()` for the axi and ahb
clocks, so they are simply skipped if not present in DT. Resets are
already optional. This keeps the probe path uniform and handles any
SoC-specific clock combinations naturally.
>> 6. Single output only: DCUltra Lite has one display output; per-
>> output
>> index logic is still in place but display_count is fixed at 1.
>>
>> 7. Reduced register space: max_register is 0x2000 vs DC8000's 0x2544.
>>
>> Add the "nuvoton,ma35d1-dcu" compatible string to the OF match table,
>> extend Kconfig to allow building on ARCH_MA35 platforms, and expose
>> vs_formats_no_yuv444 as the default format table for DCUltra Lite
>> (YUV444 blending is a DC8000-only feature).
>>
>> All changes have been tested on Nuvoton MA35D1 hardware and are
>> functioning correctly.
>>
>> Signed-off-by: Joey Lu <a0987203069@gmail.com>
>> ---
>> drivers/gpu/drm/verisilicon/Kconfig | 2 +-
>> drivers/gpu/drm/verisilicon/vs_bridge.c | 28 ++--
>> drivers/gpu/drm/verisilicon/vs_crtc.c | 13 +-
>> drivers/gpu/drm/verisilicon/vs_dc.c | 129 ++++++++++++----
>> --
>> drivers/gpu/drm/verisilicon/vs_dc.h | 1 +
>> drivers/gpu/drm/verisilicon/vs_drm.c | 16 ++-
>> drivers/gpu/drm/verisilicon/vs_hwdb.c | 2 +-
>> drivers/gpu/drm/verisilicon/vs_hwdb.h | 25 ++++
>> .../gpu/drm/verisilicon/vs_primary_plane.c | 43 +++---
>> .../drm/verisilicon/vs_primary_plane_regs.h | 2 +
>> 10 files changed, 187 insertions(+), 74 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/verisilicon/Kconfig
>> b/drivers/gpu/drm/verisilicon/Kconfig
>> index 7cce86ec8603..295d246eb4b4 100644
>> --- a/drivers/gpu/drm/verisilicon/Kconfig
>> +++ b/drivers/gpu/drm/verisilicon/Kconfig
>> @@ -2,7 +2,7 @@
>> config DRM_VERISILICON_DC
>> tristate "DRM Support for Verisilicon DC-series display
>> controllers"
>> depends on DRM && COMMON_CLK
>> - depends on RISCV || COMPILE_TEST
>> + depends on RISCV || ARCH_MA35 || COMPILE_TEST
>> select DRM_BRIDGE_CONNECTOR
>> select DRM_CLIENT_SELECTION
>> select DRM_DISPLAY_HELPER
>> diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
>> b/drivers/gpu/drm/verisilicon/vs_bridge.c
>> index 7a93049368db..225af322de32 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_bridge.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
>> @@ -164,13 +164,16 @@ static void vs_bridge_enable_common(struct
>> vs_crtc *crtc,
>> VSDC_DISP_PANEL_CONFIG_CLK_EN);
>> regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
>> VSDC_DISP_PANEL_CONFIG_RUNNING);
>> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> - VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
>> - VSDC_DISP_PANEL_START_RUNNING(output));
>>
>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
>>> id),
>> - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>> + if (dc->info->has_config_ex) {
>> + regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> +
>> VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
>> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
>> + VSDC_DISP_PANEL_START_RUNNING(output
>> ));
>> +
>> + regmap_set_bits(dc->regs,
>> VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
>> + VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
> Should the commit operation happen on DC8000/DCUltraLite too? (By
> writing to DcregFrameBufferConfig0.VALID).
>
> Many registers written has "Note: This field is double buffered" in the
> DCUltraLite documentation.
>
> I suggest create a static function for commit -- write to the
> corresponding commit bit on DC8200, and write to
> DcregFrameBufferConfig0.VALID on DC8000/DCUltraLite.
[a] There is no commit operation for DCUltra Lite.
I'll not add a `VSDC_FB_CONFIG_VALID` macro. VALID (BIT(3)) is a
hardware-managed double-buffer status bit: hardware writes 1=PENDING
when a new register set is ready and clears to 0=WORKING after the
VBLANK copy. Software must never write it, and there is no polling use
case in the driver that requires a named constant. For non-config_ex
variants, `vs_primary_plane_commit()` performs no commit operation —
`VSDC_FB_CONFIG_ENABLE` (OUTPUT, BIT(0)) is set in
`vs_crtc_atomic_enable()` and `VSDC_FB_CONFIG_RESET` (BIT(4)) is
set/cleared in the bridge enable/disable paths.
>> + }
>> }
>>
>> static void vs_bridge_atomic_enable_dpi(struct drm_bridge *bridge,
>> @@ -228,14 +231,17 @@ static void vs_bridge_atomic_disable(struct
>> drm_bridge *bridge,
>> struct vs_dc *dc = crtc->dc;
>> unsigned int output = crtc->id;
>>
>> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> - VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
>> - VSDC_DISP_PANEL_START_RUNNING(output));
>> regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
>> VSDC_DISP_PANEL_CONFIG_RUNNING);
> This bit seems to be not present in DCUltraLite, instead should
> DcregFrameBufferConfig0.RESET be clear, which will stall the DPI
> timing?
I'll move `VSDC_DISP_PANEL_CONFIG_RUNNING` inside the
`has_config_ex` block in both the enable and disable paths. For
non-config_ex variants (DCUltra Lite), the disable path will instead
clear `VSDC_FB_CONFIG_RESET` to stall DPI output.
`VSDC_DISP_PANEL_CONFIG_RUNNING` will only be set/cleared for
DC8200 (has_config_ex=true).
>>
>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
>>> id),
>> - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>> + if (dc->info->has_config_ex) {
>> + regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> +
>> VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
>> +
>> VSDC_DISP_PANEL_START_RUNNING(output));
>> +
>> + regmap_set_bits(dc->regs,
>> VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
>> + VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>> + }
> Ditto.
Covered by the same fix above — the else branch in
vs_bridge_atomic_disable() will handle the non-config_ex (DCUltra
Lite) path by clearing RESET.
>> }
>>
>> static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c
>> b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> index 9080344398ca..2f3e6d41c657 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_crtc.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> @@ -18,6 +18,7 @@
>> #include "vs_dc.h"
>> #include "vs_dc_top_regs.h"
>> #include "vs_drm.h"
>> +#include "vs_hwdb.h"
>> #include "vs_plane.h"
>>
>> static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> @@ -132,7 +133,11 @@ static int vs_crtc_enable_vblank(struct drm_crtc
>> *crtc)
>> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE)
>> + regmap_write(dc->regs, VSDC_DISP_IRQ_EN, BIT(0));
> Should `BIT(0)` be assigned with a named macro here, like
> `VSDC_DISP_IRQ_VSYNC(0)` ?
>
> In addition, even it's known that DC8000/DCUltraLite has only one
> output, hardcoding 0 here isn't so good. The header file at [1] (which
> is for DC8000) shows that two display interrupts and more other
> interrupts are present.
>
> BTW I don't like the idea of setting a "family" (because DC8000 behave
> nearly the same with DCUltraLite), maybe use `.irq_reg = VSDC_DISP_IRQ`
> (or a bool variable named `uses_top_irq`) is better?
>
> [1]
> https://github.com/milkv-megrez/rockos-u-boot/blob/c9221cf2fa77d39c0b241ab4b030c708e7ebe279/drivers/video/eswin/eswin_dc_reg.h#L2771
I'll address all three issues in v2:
1. Add `VSDC_DISP_IRQ_VSYNC(n)` macro (BIT(n)) to vs_crtc_regs.h.
2. Use `VSDC_DISP_IRQ_VSYNC(vcrtc->id)` with
`regmap_set_bits`/`regmap_clear_bits` instead of hardcoding 0 and
using `regmap_write` (which would clobber other IRQ bits).
3. Add `uses_top_irq` bool to `struct vs_chip_identity` in vs_hwdb.h.
DC8200 entries will have `uses_top_irq = true`; the DCUltra Lite
entry (model=0x0, rev=0x5560) will have `uses_top_irq = false`.
>> + else
>> + regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> + VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>>
>> return 0;
>> }
>> @@ -142,7 +147,11 @@ static void vs_crtc_disable_vblank(struct
>> drm_crtc *crtc)
>> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE)
>> + regmap_write(dc->regs, VSDC_DISP_IRQ_EN, 0);
>> + else
>> + regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> + VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>> }
>>
>> static const struct drm_crtc_funcs vs_crtc_funcs = {
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
>> b/drivers/gpu/drm/verisilicon/vs_dc.c
>> index dad9967bc10b..82a6a26f6d81 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_dc.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> @@ -9,21 +9,45 @@
>> #include <linux/of_graph.h>
>>
>> #include "vs_crtc.h"
>> +#include "vs_crtc_regs.h"
>> #include "vs_dc.h"
>> #include "vs_dc_top_regs.h"
>> #include "vs_drm.h"
>> #include "vs_hwdb.h"
>>
>> -static const struct regmap_config vs_dc_regmap_cfg = {
>> +static const struct regmap_config vs_dc8000_regmap_cfg = {
> As what I said, the currently supported thing is DC8200, not DC8000.
I'll keep the name `vs_dc_regmap_cfg` — the original name from
upstream. A separate DCUltra Lite regmap config is not needed; the
hardware simply does not implement registers above 0x2000, and a regmap
window up to 0x2544 is harmless for that variant.
>> .reg_bits = 32,
>> .val_bits = 32,
>> .reg_stride = sizeof(u32),
>> - /* VSDC_OVL_CONFIG_EX(1) */
>> .max_register = 0x2544,
>> };
>>
>> +static const struct regmap_config vs_dcultra_lite_regmap_cfg = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = sizeof(u32),
>> + .max_register = 0x2000,
>> +};
>> +
>> +static const struct vs_dc_info vs_dc8000_info = {
>> + .family = VS_DC_FAMILY_DC8000,
>> + .has_chip_id = true,
>> + .has_config_ex = true,
>> + .regmap_cfg = &vs_dc8000_regmap_cfg,
>> +};
>> +
>> +static const struct vs_dc_info vs_dcultra_lite_info = {
>> + .family = VS_DC_FAMILY_DCULTRA_LITE,
>> + .display_count = 1,
>> + .has_chip_id = false,
>> + .has_config_ex = false,
>> + .regmap_cfg = &vs_dcultra_lite_regmap_cfg,
>> + .formats = &vs_formats_no_yuv444,
>> +};
>> +
>> static const struct of_device_id vs_dc_driver_dt_match[] = {
>> - { .compatible = "verisilicon,dc" },
>> + { .compatible = "verisilicon,dc", .data = &vs_dc8000_info },
> "verisilicon,dc" is expected for DC8000 and 8200, and because of model,
> rev and customer_id values are all present for DCUltraLite, the special
> data might be dropped?
I'll drop `.data` from both `of_device_id` entries entirely.
`has_config_ex` and `uses_top_irq` will be added as fields to
`struct vs_chip_identity` in vs_hwdb.h, and populated for each entry
in `vs_chip_identities[]` in vs_hwdb.c. `vs_fill_chip_identity()` is
called unconditionally for all variants — chip-ID registers are present
on DCUltra Lite too. There is no need for a separate `vs_dc_info`
platform data structure.
>> + { .compatible = "nuvoton,ma35d1-dcu", .data =
>> &vs_dcultra_lite_info },
>> {},
>> };
>> MODULE_DEVICE_TABLE(of, vs_dc_driver_dt_match);
>> @@ -33,6 +57,13 @@ static irqreturn_t vs_dc_irq_handler(int irq, void
>> *private)
>> struct vs_dc *dc = private;
>> u32 irqs;
>>
>> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE) {
>> + regmap_read(dc->regs, VSDC_DISP_IRQ_STA, &irqs);
> Maybe add an item in the hwdb for the IRQ register?
I'll add `uses_top_irq` as a bool field to `struct vs_chip_identity`
in vs_hwdb.h instead. The IRQ handler will branch on
`dc->identity.uses_top_irq` to read either `VSDC_TOP_IRQ_ACK` or
`VSDC_DISP_IRQ_STA`. No register address needs to be stored in the
hwdb struct.
>> + if (irqs & BIT(0))
>> + vs_drm_handle_irq(dc,
>> VSDC_TOP_IRQ_VSYNC(0));
>> + return IRQ_HANDLED;
> Now I think for DC8200, the irq number decoding should be done here
> too.
I'll unify the IRQ handler: both paths will read their
respective IRQ status register and pass the raw `irqs` value to
`vs_drm_handle_irq()`. Since `VSDC_TOP_IRQ_VSYNC(n)` and
`VSDC_DISP_IRQ_VSYNC(n)` are both `BIT(n)`, the bit positions are
identical and `vs_drm_handle_irq()` handles both without change.
>> + }
>> +
>> regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
>>
>> vs_drm_handle_irq(dc, irqs);
>> @@ -43,6 +74,7 @@ static irqreturn_t vs_dc_irq_handler(int irq, void
>> *private)
>> static int vs_dc_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> + const struct vs_dc_info *info;
>> struct vs_dc *dc;
>> void __iomem *regs;
>> unsigned int port_count, i;
>> @@ -55,6 +87,10 @@ static int vs_dc_probe(struct platform_device
>> *pdev)
>> return -ENODEV;
>> }
>>
>> + info = of_device_get_match_data(dev);
>> + if (!info)
>> + return -ENODEV;
>> +
>> port_count = of_graph_get_port_count(dev->of_node);
>> if (!port_count) {
>> dev_err(dev, "can't find DC downstream ports\n");
>> @@ -75,15 +111,31 @@ static int vs_dc_probe(struct platform_device
>> *pdev)
>> if (!dc)
>> return -ENOMEM;
>>
>> - dc->rsts[0].id = "core";
>> - dc->rsts[1].id = "axi";
>> - dc->rsts[2].id = "ahb";
>> + dc->info = info;
>>
>> - ret = devm_reset_control_bulk_get_optional_shared(dev,
>> VSDC_RESET_COUNT,
>> - dc->rsts);
>> - if (ret) {
>> - dev_err(dev, "can't get reset lines\n");
>> - return ret;
>> + if (info->family == VS_DC_FAMILY_DC8000) {
>> + dc->rsts[0].id = "core";
>> + dc->rsts[1].id = "axi";
>> + dc->rsts[2].id = "ahb";
>> +
>> + ret =
>> devm_reset_control_bulk_get_optional_shared(dev,
>> + VSDC_RESET_COUNT, dc->rsts);
>> + if (ret) {
>> + dev_err(dev, "can't get reset lines\n");
>> + return ret;
>> + }
>> +
>> + dc->axi_clk = devm_clk_get_enabled(dev, "axi");
>> + if (IS_ERR(dc->axi_clk)) {
>> + dev_err(dev, "can't get axi clock\n");
>> + return PTR_ERR(dc->axi_clk);
>> + }
>> +
>> + dc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
>> + if (IS_ERR(dc->ahb_clk)) {
>> + dev_err(dev, "can't get ahb clock\n");
>> + return PTR_ERR(dc->ahb_clk);
>> + }
> Please make these clocks optional, instead of wrap them in a "family
> detection". The resets are already optional, see above.
I'll remove the family-gated block entirely. Resets will be
acquired unconditionally (they are optional, so absent ones are
skipped). axi and ahb clocks will use `devm_clk_get_optional_enabled()`
so they are silently skipped when not present in DT. The probe path
will be uniform for all variants.
>> }
>>
>> dc->core_clk = devm_clk_get_enabled(dev, "core");
>> @@ -92,28 +144,18 @@ static int vs_dc_probe(struct platform_device
>> *pdev)
>> return PTR_ERR(dc->core_clk);
>> }
>>
>> - dc->axi_clk = devm_clk_get_enabled(dev, "axi");
>> - if (IS_ERR(dc->axi_clk)) {
>> - dev_err(dev, "can't get axi clock\n");
>> - return PTR_ERR(dc->axi_clk);
>> - }
>> -
>> - dc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
>> - if (IS_ERR(dc->ahb_clk)) {
>> - dev_err(dev, "can't get ahb clock\n");
>> - return PTR_ERR(dc->ahb_clk);
>> - }
>> -
>> irq = platform_get_irq(pdev, 0);
>> if (irq < 0) {
>> dev_err(dev, "can't get irq\n");
>> return irq;
>> }
>>
>> - ret = reset_control_bulk_deassert(VSDC_RESET_COUNT, dc-
>>> rsts);
>> - if (ret) {
>> - dev_err(dev, "can't deassert reset lines\n");
>> - return ret;
>> + if (info->family == VS_DC_FAMILY_DC8000) {
>> + ret = reset_control_bulk_deassert(VSDC_RESET_COUNT,
>> dc->rsts);
>> + if (ret) {
>> + dev_err(dev, "can't deassert reset
>> lines\n");
>> + return ret;
>> + }
>> }
>>
>> regs = devm_platform_ioremap_resource(pdev, 0);
>> @@ -123,23 +165,30 @@ static int vs_dc_probe(struct platform_device
>> *pdev)
>> goto err_rst_assert;
>> }
>>
>> - dc->regs = devm_regmap_init_mmio(dev, regs,
>> &vs_dc_regmap_cfg);
>> + dc->regs = devm_regmap_init_mmio(dev, regs, info-
>>> regmap_cfg);
>> if (IS_ERR(dc->regs)) {
>> ret = PTR_ERR(dc->regs);
>> goto err_rst_assert;
>> }
>>
>> - ret = vs_fill_chip_identity(dc->regs, &dc->identity);
>> - if (ret)
>> - goto err_rst_assert;
>> + if (info->has_chip_id) {
>> + ret = vs_fill_chip_identity(dc->regs, &dc-
>>> identity);
>> + if (ret)
>> + goto err_rst_assert;
>>
>> - dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
>>> identity.model,
>> - dc->identity.revision, dc->identity.customer_id);
>> + dev_info(dev, "Found DC%x rev %x customer %x\n",
>> + dc->identity.model, dc->identity.revision,
>> + dc->identity.customer_id);
>>
>> - if (port_count > dc->identity.display_count) {
>> - dev_err(dev, "too many downstream ports than HW
>> capability\n");
>> - ret = -EINVAL;
>> - goto err_rst_assert;
>> + if (port_count > dc->identity.display_count) {
>> + dev_err(dev, "too many downstream ports than
>> HW capability\n");
>> + ret = -EINVAL;
>> + goto err_rst_assert;
>> + }
>> + } else {
>> + /* Fill identity from platform data */
>> + dc->identity.display_count = info->display_count;
>> + dc->identity.formats = info->formats;
>> }
>>
>> for (i = 0; i < dc->identity.display_count; i++) {
>> @@ -168,7 +217,8 @@ static int vs_dc_probe(struct platform_device
>> *pdev)
>> return 0;
>>
>> err_rst_assert:
>> - reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts);
>> + if (info->family == VS_DC_FAMILY_DC8000)
>> + reset_control_bulk_assert(VSDC_RESET_COUNT, dc-
>>> rsts);
>> return ret;
>> }
>>
>> @@ -180,7 +230,8 @@ static void vs_dc_remove(struct platform_device
>> *pdev)
>>
>> dev_set_drvdata(&pdev->dev, NULL);
>>
>> - reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts);
>> + if (dc->info->family == VS_DC_FAMILY_DC8000)
>> + reset_control_bulk_assert(VSDC_RESET_COUNT, dc-
>>> rsts);
>> }
>>
>> static void vs_dc_shutdown(struct platform_device *pdev)
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
>> b/drivers/gpu/drm/verisilicon/vs_dc.h
>> index ed1016f18758..f0613519af37 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_dc.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> @@ -31,6 +31,7 @@ struct vs_dc {
>> struct clk *pix_clk[VSDC_MAX_OUTPUTS];
>> struct reset_control_bulk_data rsts[VSDC_RESET_COUNT];
>>
>> + const struct vs_dc_info *info;
>> struct vs_drm_dev *drm_dev;
>> struct vs_chip_identity identity;
>> };
>> diff --git a/drivers/gpu/drm/verisilicon/vs_drm.c
>> b/drivers/gpu/drm/verisilicon/vs_drm.c
>> index fd259d53f49f..ff0fc6673006 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_drm.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_drm.c
>> @@ -27,6 +27,7 @@
>> #include "vs_dc.h"
>> #include "vs_dc_top_regs.h"
>> #include "vs_drm.h"
>> +#include "vs_hwdb.h"
>>
>> #define DRIVER_NAME "verisilicon"
>> #define DRIVER_DESC "Verisilicon DC-series display controller
>> driver"
>> @@ -72,12 +73,19 @@ static struct drm_mode_config_helper_funcs
>> vs_mode_config_helper_funcs = {
>> .atomic_commit_tail = drm_atomic_helper_commit_tail,
>> };
>>
>> -static void vs_mode_config_init(struct drm_device *drm)
>> +static void vs_mode_config_init(struct drm_device *drm, struct vs_dc
>> *dc)
>> {
>> drm->mode_config.min_width = 0;
>> drm->mode_config.min_height = 0;
>> - drm->mode_config.max_width = 8192;
>> - drm->mode_config.max_height = 8192;
>> +
>> + if (dc->info->family == VS_DC_FAMILY_DCULTRA_LITE) {
>> + drm->mode_config.max_width = 1920;
>> + drm->mode_config.max_height = 1080;
> Surely only max width 1920 and max height 1080? Can a display of
> 1080x1920 (e.g. a portrait DSI panel behind a RGB2DSI bridge) be
> supported? Can a 2170x60 display (MacBook Touch Bar panel, also a DSI
> panel) be supported? Both displays here will have no higher pixel clock
> than 1080p in the same refresh rate, although the width / height is
> higher than your restriction.
>
> In addition, these parameters decide how big a FB can be created -- the
> FB might be scaned out by multiple devices (e.g. a USB DisplayLink
> device scanning out the remaining part). The stride register is said to
> have 17-bit width in the MA35D1 TRM, so the possible FB width could be
> quite high -- assume the 17th bit is only for the value with one 1 and
> all remaining 0, we get 65536 bytes stride; with 4-byte-per-pixel
> divided this gets 16384 pixels -- the htotal/hdisplay/vtotal/vdisplay
> fields in the manual has 15-bit field width, which can reach 32767.
>
> I strongly suggest leave the original values here.
You are right. I'll revert to 8192x8192 for all variants. The
variant-specific max_width/max_height logic will be removed entirely
and vs_mode_config_init() will take no dc argument.
>> + } else {
>> + drm->mode_config.max_width = 8192;
>> + drm->mode_config.max_height = 8192;
>> + }
>> +
>> drm->mode_config.funcs = &vs_mode_config_funcs;
>> drm->mode_config.helper_private =
>> &vs_mode_config_helper_funcs;
>> }
>> @@ -125,7 +133,7 @@ int vs_drm_initialize(struct vs_dc *dc, struct
>> platform_device *pdev)
>> if (ret)
>> return ret;
>>
>> - vs_mode_config_init(drm);
>> + vs_mode_config_init(drm, dc);
>>
>> /* Enable connectors polling */
>> drm_kms_helper_poll_init(drm);
>> diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> b/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> index 09336af0900a..39402d75d841 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> @@ -78,7 +78,7 @@ static const u32 vs_formats_array_with_yuv444[] = {
>> /* TODO: non-RGB formats */
>> };
>>
>> -static const struct vs_formats vs_formats_no_yuv444 = {
>> +const struct vs_formats vs_formats_no_yuv444 = {
>> .array = vs_formats_array_no_yuv444,
>> .num = ARRAY_SIZE(vs_formats_array_no_yuv444)
>> };
>> diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> b/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> index 92192e4fa086..655cf93ca3aa 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> @@ -14,6 +14,29 @@ struct vs_formats {
>> unsigned int num;
>> };
>>
>> +enum vs_dc_family {
>> + VS_DC_FAMILY_DC8000,
>> + VS_DC_FAMILY_DCULTRA_LITE,
>> +};
>> +
>> +/**
>> + * struct vs_dc_info - per-SoC DC platform data
>> + * @family: DC IP family (DC8000, DCUltra Lite, etc.)
>> + * @display_count: number of display outputs (0 = auto-detect
>> from DT/HW)
>> + * @has_chip_id: whether chip identity registers exist
>> + * @has_config_ex: whether CONFIG_EX commit mechanism exists
>> + * @regmap_cfg: regmap configuration for this
>> variant
>> + * @formats: supported pixel formats (NULL = auto-detect
>> from chip ID)
>> + */
>> +struct vs_dc_info {
>> + enum vs_dc_family family;
>> + u32 display_count;
>> + bool has_chip_id;
>> + bool has_config_ex;
>> + const struct regmap_config *regmap_cfg;
>> + const struct vs_formats *formats;
>> +};
>> +
>> struct vs_chip_identity {
>> u32 model;
>> u32 revision;
>> @@ -23,6 +46,8 @@ struct vs_chip_identity {
>> const struct vs_formats *formats;
>> };
>>
>> +extern const struct vs_formats vs_formats_no_yuv444;
>> +
>> int vs_fill_chip_identity(struct regmap *regs,
>> struct vs_chip_identity *ident);
>>
>> diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> index 1f2be41ae496..197d5d683e22 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> @@ -55,8 +55,9 @@ static int vs_primary_plane_atomic_check(struct
>> drm_plane *plane,
>>
>> static void vs_primary_plane_commit(struct vs_dc *dc, unsigned int
>> output)
>> {
>> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_COMMIT);
>> + if (dc->info->has_config_ex)
>> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> + VSDC_FB_CONFIG_EX_COMMIT);
> Should VALID bit be written here instead of doing nothing on
> DC8000/DCUltraLite ?
Same as reply [a]. No `VSDC_FB_CONFIG_VALID` macro is needed.
For non-config_ex variants, `vs_primary_plane_commit()` is a no-op —
`VSDC_FB_CONFIG_ENABLE` (OUTPUT, bit 0) is set in `vs_crtc_atomic_enable()`
and `VSDC_FB_CONFIG_RESET` (bit 4) is managed in the bridge enable/disable
paths.
>> }
>>
>> static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
>> @@ -69,11 +70,13 @@ static void vs_primary_plane_atomic_enable(struct
>> drm_plane *plane,
>> unsigned int output = vcrtc->id;
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_FB_EN);
>> - regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
>> - VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
>> + if (dc->info->has_config_ex) {
>> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> + VSDC_FB_CONFIG_EX_FB_EN);
>> + regmap_update_bits(dc->regs,
>> VSDC_FB_CONFIG_EX(output),
>> +
>> VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
>> +
>> VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
>> + }
>>
>> vs_primary_plane_commit(dc, output);
>> }
>> @@ -88,8 +91,9 @@ static void vs_primary_plane_atomic_disable(struct
>> drm_plane *plane,
>> unsigned int output = vcrtc->id;
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_FB_EN);
>> + if (dc->info->has_config_ex)
>> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> + VSDC_FB_CONFIG_EX_FB_EN);
>>
>> vs_primary_plane_commit(dc, output);
>> }
>> @@ -126,6 +130,11 @@ static void
>> vs_primary_plane_atomic_update(struct drm_plane *plane,
>> VSDC_FB_CONFIG_UV_SWIZZLE_EN,
>> vs_state->format.uv_swizzle);
>>
>> + /* DCUltra Lite requires explicit enable/reset bits in
>> FB_CONFIG */
>> + if (!dc->info->has_config_ex)
>> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG(output),
>> + VSDC_FB_CONFIG_ENABLE |
>> VSDC_FB_CONFIG_RESET);
> Should VSDC_FB_CONFIG_RESET be only set when it's ready to output the
> signal (at least all timing is programmed)? I think it should be
> programmed in crtc/bridge instead of primary plane, although it's in
> the DcregFrameBufferConfig0 register (obviously this sounds a little
> weird, this might be why they changed this in DC8200).
>
> When ENABLE (OUTPUT in the document) is cleared, all pixels will be
> blacked out; so I think it's better to set ENABLE in CRTC, and then set
> RESET in bridge (doing the work of encoder in this driver) -- it seems
> that for DC8000/DCUltraLite the primary plane is not possible to be
> disabled.
I'll move these as suggested:
- `VSDC_FB_CONFIG_ENABLE` will be set in `vs_crtc_atomic_enable()` for
non-config_ex variants (after the pixel clock is enabled).
- `VSDC_FB_CONFIG_RESET` will be set in `vs_bridge_enable_common()` for
non-config_ex variants, after all timing registers are programmed.
- `VSDC_FB_CONFIG_RESET` will be cleared in `vs_bridge_atomic_disable()`
for non-config_ex variants to stall DPI output.
- The ENABLE/RESET writes will be removed from
`vs_primary_plane_atomic_update()`.
- `vs_primary_plane_commit()` will perform no action for non-config_ex
variants; the function only triggers `VSDC_FB_CONFIG_EX_COMMIT` for
config_ex variants.
>> dma_addr = vs_fb_get_dma_addr(fb, &state->src);
>>
>> regmap_write(dc->regs, VSDC_FB_ADDRESS(output),
>> @@ -133,16 +142,18 @@ static void
>> vs_primary_plane_atomic_update(struct drm_plane *plane,
>> regmap_write(dc->regs, VSDC_FB_STRIDE(output),
>> fb->pitches[0]);
>>
>> - regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
>> - VSDC_MAKE_PLANE_POS(state->crtc_x, state-
>>> crtc_y));
>> - regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
>> - VSDC_MAKE_PLANE_POS(state->crtc_x + state-
>>> crtc_w,
>> - state->crtc_y + state-
>>> crtc_h));
>> regmap_write(dc->regs, VSDC_FB_SIZE(output),
>> VSDC_MAKE_PLANE_SIZE(state->crtc_w, state-
>>> crtc_h));
>>
>> - regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
>> - VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
>> + if (dc->info->has_config_ex) {
>> + regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
>> + VSDC_MAKE_PLANE_POS(state->crtc_x,
>> state->crtc_y));
>> + regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
>> + VSDC_MAKE_PLANE_POS(state->crtc_x +
>> state->crtc_w,
>> + state->crtc_y +
>> state->crtc_h));
>> + regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
>> + VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
>> + }
>>
>> vs_primary_plane_commit(dc, output);
>> }
>> diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
>> b/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
>> index cbb125c46b39..288064760b48 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_primary_plane_regs.h
>> @@ -16,6 +16,8 @@
>> #define VSDC_FB_STRIDE(n) (0x1408 + 0x4 * (n))
>>
>> #define VSDC_FB_CONFIG(n) (0x1518 + 0x4 * (n))
>> +#define VSDC_FB_CONFIG_ENABLE BIT(0)
> As I mentioned that the VALID bit is quite important, please add it
> here (you can call it "COMMIT" too if you like).
>
> #define VSDC_FB_CONFIG_VALID BIT(3)
Same as reply [a]. No `VSDC_FB_CONFIG_VALID` macro is needed.
Thank you very much for taking the time to review my work. 🙂
>> +#define VSDC_FB_CONFIG_RESET BIT(4)
>> #define VSDC_FB_CONFIG_CLEAR_EN BIT(8)
>> #define VSDC_FB_CONFIG_ROT_MASK GENMASK(13,
>> 11)
>> #define VSDC_FB_CONFIG_ROT(v) ((v) << 11)
^ permalink raw reply
* RE: [PATCH v1 1/2] PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95
From: Hongxing Zhu @ 2026-05-12 7:41 UTC (permalink / raw)
To: Frank Li, l.stach@pengutronix.de, lpieralisi@kernel.org,
kwilczynski@kernel.org, mani@kernel.org, robh@kernel.org,
bhelgaas@google.com, s.hauer@pengutronix.de,
kernel@pengutronix.de, festevam@gmail.com
Cc: linux-pci@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
imx@lists.linux.dev, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
In-Reply-To: <20260512052244.49414-2-hongxing.zhu@nxp.com>
> -----Original Message-----
> From: Hongxing Zhu <hongxing.zhu@nxp.com>
> Sent: Tuesday, May 12, 2026 1:23 PM
> To: Frank Li <frank.li@nxp.com>; l.stach@pengutronix.de; lpieralisi@kernel.org;
> kwilczynski@kernel.org; mani@kernel.org; robh@kernel.org;
> bhelgaas@google.com; s.hauer@pengutronix.de; kernel@pengutronix.de;
> festevam@gmail.com
> Cc: linux-pci@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> imx@lists.linux.dev; linux-kernel@vger.kernel.org; Hongxing Zhu
> <hongxing.zhu@nxp.com>; stable@vger.kernel.org
> Subject: [PATCH v1 1/2] PCI: imx6: Configure REF_USE_PAD before PHY reset for
> i.MX95
>
> According to the i.MX95 PCIe PHY Databook, the ref_use_pad signal in the
> Common Block Signals section selects the reference clock source connected to
> the PHY pads. Per the specification, any change to this input must be followed by
> a PHY reset assertion to take effect.
>
> Move the REF_USE_PAD configuration before the PHY reset toggle to comply with
> the required initialization sequence.
>
> Fixes: 47f54a902dcd ("PCI: imx6: Toggle the core reset for i.MX95 PCIe")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
> ---
> drivers/pci/controller/dwc/pci-imx6.c | 27 ++++++++++++++++++++++++---
> 1 file changed, 24 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> b/drivers/pci/controller/dwc/pci-imx6.c
> index 1034ac5c5f5c1..c57f18d9e4ffa 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -137,6 +137,7 @@ struct imx_pcie_drvdata {
> const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
> const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
> const struct pci_epc_features *epc_features;
> + int (*init_pre_reset)(struct imx_pcie *pcie);
> int (*init_phy)(struct imx_pcie *pcie);
> int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
> int (*core_reset)(struct imx_pcie *pcie, bool assert); @@ -247,6 +248,24
> @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie)
> return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 :
> IOMUXC_GPR14; }
>
> +static int imx95_pcie_init_pre_reset(struct imx_pcie *imx_pcie) {
> + bool ext = imx_pcie->enable_ext_refclk;
> +
> + /*
> + * Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is
> + * used to select reference clock connected to a pair of pads.
> + *
> + * Any change in this input must be followed by phy_reset assertion.
> + */
> +
> + regmap_update_bits(imx_pcie->iomuxc_gpr,
> IMX95_PCIE_SS_RW_REG_0,
Sorry, the register name IMX95_PCIE_SS_RW_REG_0 is incorrect and should be
replaced with IMX95_PCIE_PHY_GEN_CTRL. I will update this in the next version.
> + IMX95_PCIE_REF_USE_PAD,
> + ext ? IMX95_PCIE_REF_USE_PAD : 0);
> +
> + return 0;
> +}
> +
> static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) {
> bool ext = imx_pcie->enable_ext_refclk; @@ -269,9 +288,6 @@ static
> int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
> IMX95_PCIE_PHY_CR_PARA_SEL,
> IMX95_PCIE_PHY_CR_PARA_SEL);
>
> - regmap_update_bits(imx_pcie->iomuxc_gpr,
> IMX95_PCIE_PHY_GEN_CTRL,
> - IMX95_PCIE_REF_USE_PAD,
> - ext ? IMX95_PCIE_REF_USE_PAD : 0);
> regmap_update_bits(imx_pcie->iomuxc_gpr,
> IMX95_PCIE_SS_RW_REG_0,
> IMX95_PCIE_REF_CLKEN,
> ext ? 0 : IMX95_PCIE_REF_CLKEN);
> @@ -1251,6 +1267,9 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
> pp->bridge->disable_device = imx_pcie_disable_device;
> }
>
> + if (imx_pcie->drvdata->init_pre_reset)
> + imx_pcie->drvdata->init_pre_reset(imx_pcie);
> +
> imx_pcie_assert_core_reset(imx_pcie);
> imx_pcie_assert_perst(imx_pcie, true);
>
> @@ -1961,6 +1980,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
> .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
> .core_reset = imx95_pcie_core_reset,
> .init_phy = imx95_pcie_init_phy,
> + .init_pre_reset = imx95_pcie_init_pre_reset,
> .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
> .enable_ref_clk = imx95_pcie_enable_ref_clk,
> .clr_clkreq_override = imx95_pcie_clr_clkreq_override, @@ -
> 2016,6 +2036,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
> .ltssm_mask = IMX95_PCIE_LTSSM_EN,
> .mode_off[0] = IMX95_PE0_GEN_CTRL_1,
> .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
> + .init_pre_reset = imx95_pcie_init_pre_reset,
> .init_phy = imx95_pcie_init_phy,
> .core_reset = imx95_pcie_core_reset,
> .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
>
> base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83
> --
> 2.37.1
^ permalink raw reply
* [PATCH v3 3/3] arm64: dts: nuvoton: Add I2C nodes for MA35D1 SoC
From: Zi-Yu Chen @ 2026-05-12 7:39 UTC (permalink / raw)
To: Jacky Huang, Andi Shyti
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-arm-kernel,
linux-i2c, devicetree, linux-kernel, Zi-Yu Chen
In-Reply-To: <20260512073953.564323-1-zychennvt@gmail.com>
Add I2C controller nodes to the MA35D1 SoC dtsi.
Also enable the I2C interfaces on the MA35D1 SOM board
to allow communication with onboard peripherals.
Signed-off-by: Zi-Yu Chen <zychennvt@gmail.com>
---
.../boot/dts/nuvoton/ma35d1-som-256m.dts | 15 +++++
arch/arm64/boot/dts/nuvoton/ma35d1.dtsi | 60 +++++++++++++++++++
2 files changed, 75 insertions(+)
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
index f6f20a17e501..a0f1d76d288e 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
@@ -13,6 +13,7 @@ / {
compatible = "nuvoton,ma35d1-som", "nuvoton,ma35d1";
aliases {
+ i2c0 = &i2c2;
serial0 = &uart0;
serial11 = &uart11;
serial12 = &uart12;
@@ -55,6 +56,12 @@ &clk {
"integer";
};
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+};
+
&pinctrl {
uart-grp {
pinctrl_uart0: uart0-pins {
@@ -98,6 +105,14 @@ pinctrl_uart16: uart16-pins {
power-source = <1>;
};
};
+
+ i2c-grp {
+ pinctrl_i2c1: i2c1-pins {
+ nuvoton,pins = <1 10 12>,
+ <1 11 12>;
+ bias-disable;
+ };
+ };
};
&uart0 {
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
index e51b98f5bdce..a88d1c2938e7 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
@@ -372,6 +372,66 @@ uart15: serial@407f0000 {
status = "disabled";
};
+ i2c1: i2c@40810000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40810000 0x0 0x1000>;
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C1_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C1>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c2: i2c@40820000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40820000 0x0 0x1000>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C2_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C2>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c3: i2c@40830000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40830000 0x0 0x1000>;
+ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C3_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C3>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c4: i2c@40840000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40840000 0x0 0x1000>;
+ interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C4_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c5: i2c@40850000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40850000 0x0 0x1000>;
+ interrupts = <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C5_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C5>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
uart16: serial@40880000 {
compatible = "nuvoton,ma35d1-uart";
reg = <0x0 0x40880000 0x0 0x100>;
--
2.34.1
^ permalink raw reply related
* [PATCH v3 2/3] i2c: ma35d1: Add Nuvoton MA35D1 I2C driver support
From: Zi-Yu Chen @ 2026-05-12 7:39 UTC (permalink / raw)
To: Jacky Huang, Andi Shyti
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-arm-kernel,
linux-i2c, devicetree, linux-kernel, Zi-Yu Chen
In-Reply-To: <20260512073953.564323-1-zychennvt@gmail.com>
Add I2C support for Nuvoton MA35D1 SoC.
The controller supports standard, fast and fast-plus modes,
and provides controller/target functionality.
Signed-off-by: Zi-Yu Chen <zychennvt@gmail.com>
---
drivers/i2c/busses/Kconfig | 13 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-ma35d1.c | 792 ++++++++++++++++++++++++++++++++
3 files changed, 806 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-ma35d1.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index ea3e7e92465d..401dcc9be46b 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1601,4 +1601,17 @@ config I2C_VIRTIO
This driver can also be built as a module. If so, the module
will be called i2c-virtio.
+config I2C_MA35D1
+ tristate "Nuvoton MA35D1 I2C driver"
+ depends on ARCH_MA35 || COMPILE_TEST
+ select I2C_SLAVE
+ help
+ If you say yes to this option, support will be included for the
+ I2C controller in the Nuvoton MA35D1 SoC. This driver
+ supports the standard I2C bus protocols, including master and
+ slave modes.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-ma35d1.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 3755c54b3d82..ca75dae4955c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
obj-$(CONFIG_I2C_GXP) += i2c-gxp.o
+obj-$(CONFIG_I2C_MA35D1) += i2c-ma35d1.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-ma35d1.c b/drivers/i2c/busses/i2c-ma35d1.c
new file mode 100644
index 000000000000..c643f406e000
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ma35d1.c
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Nuvoton technology corporation.
+ *
+ * Author: Zi-Yu Chen <zychennvt@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+/* MA35D1 I2C registers offset */
+#define MA35_CTL0 0x00
+#define MA35_ADDR0 0x04
+#define MA35_DAT 0x08
+#define MA35_STATUS0 0x0c
+#define MA35_CLKDIV 0x10
+#define MA35_TOCTL 0x14
+#define MA35_ADDR1 0x18
+#define MA35_ADDR2 0x1c
+#define MA35_ADDR3 0x20
+#define MA35_ADDRMSK0 0x24
+#define MA35_ADDRMSK1 0x28
+#define MA35_ADDRMSK2 0x2c
+#define MA35_ADDRMSK3 0x30
+#define MA35_WKCTL 0x3c
+#define MA35_WKSTS 0x40
+#define MA35_CTL1 0x44
+#define MA35_STATUS1 0x48
+#define MA35_TMCTL 0x4c
+#define MA35_BUSCTL 0x50
+#define MA35_BUSTCTL 0x54
+#define MA35_BUSSTS 0x58
+#define MA35_PKTSIZE 0x5c
+#define MA35_PKTCRC 0x60
+#define MA35_BUSTOUT 0x64
+#define MA35_CLKTOUT 0x68
+#define MA35_AUTOCNT 0x78
+
+/* MA35D1 I2C Status */
+/* Controller */
+#define MA35_M_START 0x08 /* Start */
+#define MA35_M_REPEAT_START 0x10 /* Controller Repeat Start */
+#define MA35_M_TRAN_ADDR_ACK 0x18 /* Controller Transmit Address ACK */
+#define MA35_M_TRAN_ADDR_NACK 0x20 /* Controller Transmit Address NACK */
+#define MA35_M_TRAN_DATA_ACK 0x28 /* Controller Transmit Data ACK */
+#define MA35_M_TRAN_DATA_NACK 0x30 /* Controller Transmit Data NACK */
+#define MA35_M_ARB_LOST 0x38 /* Controller Arbitration Lost */
+#define MA35_M_RECE_ADDR_ACK 0x40 /* Controller Receive Address ACK */
+#define MA35_M_RECE_ADDR_NACK 0x48 /* Controller Receive Address NACK */
+#define MA35_M_RECE_DATA_ACK 0x50 /* Controller Receive Data ACK */
+#define MA35_M_RECE_DATA_NACK 0x58 /* Controller Receive Data NACK */
+#define MA35_BUS_ERROR 0x00 /* Bus error */
+
+/* Target */
+#define MA35_S_REPEAT_START_STOP 0xa0 /* Target Transmit Repeat Start or Stop */
+#define MA35_S_TRAN_ADDR_ACK 0xa8 /* Target Transmit Address ACK */
+#define MA35_S_TRAN_DATA_ACK 0xb8 /* Target Transmit Data ACK */
+#define MA35_S_TRAN_DATA_NACK 0xc0 /* Target Transmit Data NACK */
+#define MA35_S_TRAN_LAST_DATA_ACK 0xc8 /* Target Transmit Last Data ACK */
+#define MA35_S_RECE_ADDR_ACK 0x60 /* Target Receive Address ACK */
+#define MA35_S_RECE_ARB_LOST 0x68 /* Target Receive Arbitration Lost */
+#define MA35_S_RECE_DATA_ACK 0x80 /* Target Receive Data ACK */
+#define MA35_S_RECE_DATA_NACK 0x88 /* Target Receive Data NACK */
+
+/* GC Mode */
+#define MA35_GC_ADDR_ACK 0x70 /* GC mode Address ACK */
+#define MA35_GC_ARB_LOST 0x78 /* GC mode Arbitration Lost */
+#define MA35_GC_DATA_ACK 0x90 /* GC mode Data ACK */
+#define MA35_GC_DATA_NACK 0x98 /* GC mode Data NACK */
+
+/* Other */
+#define MA35_ADDR_TRAN_ARB_LOST 0xb0 /* Address Transmit Arbitration Lost */
+#define MA35_BUS_RELEASED 0xf8 /* Bus Released */
+
+/* I2C_CTL constant definitions. */
+#define MA35_CTL_AA BIT(2)
+#define MA35_CTL_SI BIT(3)
+#define MA35_CTL_STO BIT(4)
+#define MA35_CTL_STA BIT(5)
+#define MA35_CTL_I2CEN BIT(6)
+#define MA35_CTL_INTEN BIT(7)
+#define MA35_CTL_SI_AA (MA35_CTL_SI | MA35_CTL_AA)
+#define MA35_CTL_STO_SI (MA35_CTL_STO | MA35_CTL_SI)
+#define MA35_CTL_STA_SI (MA35_CTL_STA | MA35_CTL_SI)
+#define MA35_CTL_STA_SI_AA (MA35_CTL_STA | MA35_CTL_SI | MA35_CTL_AA)
+#define MA35_CTL_STO_SI_AA (MA35_CTL_STO | MA35_CTL_SI | MA35_CTL_AA)
+
+/* Constants */
+#define MA35_CLKDIV_MSK GENMASK(15, 0)
+#define MA35_I2C_ADDR_MASK (0x7f << 1)
+#define I2C_PM_TIMEOUT_MS 5000
+#define STOP_TIMEOUT_MS 50
+#define MA35_I2C_GC_EN 1
+#define MA35_I2C_GC_DIS 0
+
+struct ma35d1_i2c {
+ spinlock_t lock; /* Protects I2C register access and state */
+ wait_queue_head_t wait;
+ struct i2c_msg *msg;
+ unsigned int msg_num;
+ unsigned int msg_idx;
+ unsigned int msg_ptr;
+ unsigned int irq;
+ unsigned int arblost;
+ void __iomem *regs;
+ struct clk *clk;
+ struct device *dev;
+ struct i2c_adapter adap;
+ struct i2c_client *target;
+ struct reset_control *rst;
+};
+
+static inline bool ma35d1_is_controller_status(unsigned int status)
+{
+ return status >= MA35_M_START && status <= MA35_M_RECE_DATA_NACK;
+}
+
+/*
+ * ma35d1_i2c_write_CTL - Update the I2C control register
+ * @i2c: Pointer to the ma35d1 i2c instance
+ * @ctl: Control bits to set (e.g., MA35_CTL_STA, SI, AA)
+ *
+ * This helper reads CTL0, clears the sticky state-change bits (STA, STO, SI, AA),
+ * and then applies the new control bits provided by @ctl.
+ */
+static void ma35d1_i2c_write_CTL(struct ma35d1_i2c *i2c, unsigned int ctl)
+{
+ unsigned int val;
+
+ val = readl(i2c->regs + MA35_CTL0);
+ val &= ~(MA35_CTL_STA_SI_AA | MA35_CTL_STO);
+ val |= ctl;
+ writel(val, i2c->regs + MA35_CTL0);
+}
+
+static void ma35d1_i2c_set_addr(struct ma35d1_i2c *i2c)
+{
+ unsigned int rw = i2c->msg->flags & I2C_M_RD;
+
+ writel(((i2c->msg->addr & 0x7f) << 1) | rw, i2c->regs + MA35_DAT);
+}
+
+static void ma35d1_i2c_controller_complete(struct ma35d1_i2c *i2c, int ret)
+{
+ dev_dbg(i2c->dev, "controller_complete %d\n", ret);
+
+ i2c->msg_ptr = 0;
+ i2c->msg = NULL;
+ i2c->msg_idx++;
+ i2c->msg_num = 0;
+ if (ret)
+ i2c->msg_idx = ret;
+
+ wake_up(&i2c->wait);
+}
+
+static void ma35d1_i2c_disable_irq(struct ma35d1_i2c *i2c)
+{
+ unsigned long tmp;
+
+ tmp = readl(i2c->regs + MA35_CTL0);
+ writel(tmp & ~MA35_CTL_INTEN, i2c->regs + MA35_CTL0);
+}
+
+static void ma35d1_i2c_enable_irq(struct ma35d1_i2c *i2c)
+{
+ unsigned long tmp;
+
+ tmp = readl(i2c->regs + MA35_CTL0);
+ writel(tmp | MA35_CTL_INTEN, i2c->regs + MA35_CTL0);
+}
+
+static void ma35d1_i2c_reset(struct ma35d1_i2c *i2c)
+{
+ unsigned int tmp;
+
+ tmp = readl(i2c->regs + MA35_CLKDIV);
+
+ reset_control_assert(i2c->rst);
+ usleep_range(10, 20);
+ reset_control_deassert(i2c->rst);
+
+ writel(tmp, (i2c->regs + MA35_CLKDIV));
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_I2CEN);
+
+ if (i2c->target)
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI_AA);
+}
+
+static void ma35d1_i2c_stop(struct ma35d1_i2c *i2c, int ret)
+{
+ unsigned int val;
+ int err;
+
+ /* Ensure AA is cleared to prevent the controller
+ * from re-claiming the bus unnecessarily
+ */
+ if (readl(i2c->regs + MA35_CTL0) & MA35_CTL_AA) {
+ val = readl(i2c->regs + MA35_CTL0);
+ val &= ~MA35_CTL_AA;
+ writel(val, (i2c->regs + MA35_CTL0));
+
+ err = readl_poll_timeout_atomic(i2c->regs + MA35_CTL0, val,
+ !(val & MA35_CTL_AA), 1, 1000);
+ if (err)
+ dev_warn(i2c->dev,
+ "AA bit could not be cleared in time\n");
+ }
+
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_STO_SI);
+
+ err = readl_poll_timeout_atomic(i2c->regs + MA35_CTL0, val,
+ !(val & MA35_CTL_STO), 1, 1 * 1000);
+ if (err)
+ dev_warn(i2c->dev, "I2C Stop Timeout\n");
+
+ if (i2c->target)
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI_AA);
+ else
+ ma35d1_i2c_disable_irq(i2c);
+
+ ma35d1_i2c_controller_complete(i2c, ret);
+}
+
+/* Check if this is the last message in the set */
+static inline bool is_last_msg(struct ma35d1_i2c *i2c)
+{
+ return i2c->msg_idx >= (i2c->msg_num - 1);
+}
+
+/* Check if this is the last byte in the current message */
+static inline bool is_last_byte(struct ma35d1_i2c *i2c)
+{
+ return i2c->msg_ptr == i2c->msg->len - 1;
+}
+
+/* Check if reached the end of the current message */
+static inline bool is_msgend(struct ma35d1_i2c *i2c)
+{
+ return i2c->msg_ptr >= i2c->msg->len;
+}
+
+/*
+ * i2c_ma35d1_irq_target_trx - I2C Target state machine handler
+ * @i2c: ma35d1 i2c instance
+ * @i2c_status: hardware status code from MA35_STATUS0
+ */
+static void i2c_ma35d1_irq_target_trx(struct ma35d1_i2c *i2c,
+ unsigned long i2c_status)
+{
+ unsigned char byte;
+
+ switch (i2c_status) {
+ case MA35_S_RECE_ADDR_ACK:
+ /* Own SLA+W has been receive; ACK has been return */
+ i2c_slave_event(i2c->target, I2C_SLAVE_WRITE_REQUESTED, &byte);
+ break;
+ case MA35_S_TRAN_DATA_NACK:
+ /* Data byte or last data in I2CDAT has been transmitted.
+ * Not ACK has been received
+ */
+ case MA35_S_RECE_DATA_NACK:
+ /* Previously addressed with own SLA address;
+ * NOT ACK has been returned
+ */
+ break;
+
+ case MA35_S_RECE_DATA_ACK:
+ /* Previously address with own SLA address Data has been received;
+ * ACK has been returned
+ */
+ byte = readb(i2c->regs + MA35_DAT);
+ i2c_slave_event(i2c->target, I2C_SLAVE_WRITE_RECEIVED, &byte);
+ break;
+
+ case MA35_S_TRAN_ADDR_ACK:
+ /* Own SLA+R has been receive; ACK has been return */
+ i2c_slave_event(i2c->target, I2C_SLAVE_READ_REQUESTED, &byte);
+
+ writel(byte, i2c->regs + MA35_DAT);
+ break;
+
+ case MA35_S_TRAN_DATA_ACK:
+ i2c_slave_event(i2c->target, I2C_SLAVE_READ_PROCESSED, &byte);
+ writel(byte, i2c->regs + MA35_DAT);
+ break;
+
+ case MA35_S_REPEAT_START_STOP:
+ /* A STOP or repeated START has been received
+ * while still addressed as Target/Receiver
+ */
+ i2c_slave_event(i2c->target, I2C_SLAVE_STOP, &byte);
+ break;
+
+ default:
+ dev_err(i2c->dev, "Status 0x%02lx is NOT processed\n",
+ i2c_status);
+ break;
+ }
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI_AA);
+}
+
+/*
+ * i2c_ma35d1_irq_controller_trx - I2C Controller state machine handler
+ * @i2c: ma35d1 i2c instance
+ * @i2c_status: hardware status code from MA35_STATUS0
+ */
+static void i2c_ma35d1_irq_controller_trx(struct ma35d1_i2c *i2c,
+ unsigned long i2c_status)
+{
+ unsigned char byte;
+
+ switch (i2c_status) {
+ case MA35_M_START:
+ case MA35_M_REPEAT_START:
+ ma35d1_i2c_set_addr(i2c);
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI);
+ break;
+
+ case MA35_M_TRAN_ADDR_ACK:
+ case MA35_M_TRAN_DATA_ACK:
+ /* SLA+W has been transmitted and ACK has been received */
+ if (i2c_status == MA35_M_TRAN_ADDR_ACK) {
+ if (is_last_msg(i2c) && i2c->msg->len == 0) {
+ ma35d1_i2c_stop(i2c, 0);
+ return;
+ }
+ }
+
+ if (!is_msgend(i2c)) {
+ byte = i2c->msg->buf[i2c->msg_ptr++];
+ writel(byte, i2c->regs + MA35_DAT);
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI);
+ } else if (!is_last_msg(i2c)) {
+ dev_dbg(i2c->dev, "WRITE: Next Message\n");
+
+ i2c->msg_ptr = 0;
+ i2c->msg_idx++;
+ i2c->msg++;
+
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_STA | MA35_CTL_SI);
+ } else {
+ ma35d1_i2c_stop(i2c, 0);
+ }
+ break;
+
+ case MA35_M_TRAN_DATA_NACK:
+ ma35d1_i2c_stop(i2c, 0);
+ break;
+
+ case MA35_M_TRAN_ADDR_NACK:
+ case MA35_M_RECE_ADDR_NACK:
+ /* Controller Transmit Address NACK */
+ /* 0x20: SLA+W has been transmitted and NACK has been received */
+ /* 0x48: SLA+R has been transmitted and NACK has been received */
+ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+ dev_dbg(i2c->dev, "\n i2c: ack was not received\n");
+ ma35d1_i2c_stop(i2c, -ENXIO);
+ }
+ break;
+
+ case MA35_M_RECE_ADDR_ACK:
+ if (is_last_msg(i2c) && i2c->msg->len == 0)
+ ma35d1_i2c_stop(i2c, 0);
+ else if (is_last_msg(i2c) && (i2c->msg->len == 1))
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI);
+ else
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI_AA);
+ break;
+
+ case MA35_M_RECE_DATA_ACK:
+ case MA35_M_RECE_DATA_NACK:
+ /* DATA has been transmitted and ACK has been received */
+ byte = readb(i2c->regs + MA35_DAT);
+ i2c->msg->buf[i2c->msg_ptr++] = byte;
+
+ if (is_last_byte(i2c)) {
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI);
+ } else if (is_msgend(i2c)) {
+ if (is_last_msg(i2c)) {
+ dev_dbg(i2c->dev, "READ: Send Stop\n");
+
+ ma35d1_i2c_stop(i2c, 0);
+ } else {
+ dev_dbg(i2c->dev, "READ: Next Transfer\n");
+
+ i2c->msg_ptr = 0;
+ i2c->msg_idx++;
+ i2c->msg++;
+
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_STA_SI);
+ }
+ } else {
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI_AA);
+ }
+ break;
+
+ default:
+ dev_err(i2c->dev, "Status 0x%02lx is NOT processed\n",
+ i2c_status);
+ ma35d1_i2c_disable_irq(i2c);
+ ma35d1_i2c_stop(i2c, 0);
+ break;
+ }
+}
+
+static irqreturn_t ma35d1_i2c_irq(int irqno, void *dev_id)
+{
+ struct ma35d1_i2c *i2c = dev_id;
+ unsigned long status, flags;
+
+ status = readl(i2c->regs + MA35_STATUS0);
+
+ spin_lock_irqsave(&i2c->lock, flags);
+
+ if (status == MA35_M_ARB_LOST) {
+ dev_err(i2c->dev, "Arbitration lost\n");
+ i2c->arblost = 1;
+ ma35d1_i2c_disable_irq(i2c);
+ ma35d1_i2c_stop(i2c, -EAGAIN);
+ goto out;
+ }
+
+ else if (status == MA35_BUS_ERROR) {
+ dev_err(i2c->dev, "Bus error during transfer\n");
+ ma35d1_i2c_disable_irq(i2c);
+ ma35d1_i2c_stop(i2c, 0);
+ goto out;
+ }
+
+ if (ma35d1_is_controller_status(status))
+ i2c_ma35d1_irq_controller_trx(i2c, status);
+ else
+ i2c_ma35d1_irq_target_trx(i2c, status);
+
+out:
+ spin_unlock_irqrestore(&i2c->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int ma35d1_i2c_doxfer(struct ma35d1_i2c *i2c, struct i2c_msg *msgs,
+ int num)
+{
+ unsigned long timeout;
+ unsigned int val;
+ int ret, err;
+
+ spin_lock_irq(&i2c->lock);
+
+ ma35d1_i2c_enable_irq(i2c);
+
+ i2c->msg = msgs;
+ i2c->msg_num = num;
+ i2c->msg_ptr = 0;
+ i2c->msg_idx = 0;
+
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI | MA35_CTL_STA);
+ spin_unlock_irq(&i2c->lock);
+
+ timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
+ ret = i2c->msg_idx;
+
+ if (timeout == 0)
+ dev_dbg(i2c->dev, "timeout\n");
+ else if (ret != num)
+ dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
+
+ err = readl_poll_timeout(i2c->regs + MA35_CTL0, val,
+ !(val & MA35_CTL_STO), 100,
+ STOP_TIMEOUT_MS * 1000);
+
+ if (err) {
+ dev_err(i2c->dev, "Bus stuck! Resetting controller...\n");
+ ma35d1_i2c_reset(i2c);
+ }
+
+ if (i2c->arblost) {
+ dev_dbg(i2c->dev, "arb lost, stop\n");
+ i2c->arblost = 0;
+ }
+
+ return ret;
+}
+
+static int ma35d1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct ma35d1_i2c *i2c = i2c_get_adapdata(adap);
+ int retry, ret;
+
+ ret = pm_runtime_resume_and_get(i2c->dev);
+ if (ret)
+ return ret;
+
+ for (retry = 0; retry < adap->retries; retry++) {
+ ret = ma35d1_i2c_doxfer(i2c, msgs, num);
+
+ if (ret != -EAGAIN)
+ break;
+
+ dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
+ fsleep(100);
+ }
+
+ if (ret == -EAGAIN)
+ ret = -EREMOTEIO;
+
+ pm_runtime_put_autosuspend(i2c->dev);
+
+ return ret;
+}
+
+static int ma35d1_reg_target(struct i2c_client *target)
+{
+ struct ma35d1_i2c *i2c = i2c_get_adapdata(target->adapter);
+ unsigned int val, slvaddr;
+ int ret;
+
+ if (i2c->target)
+ return -EBUSY;
+
+ if (target->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ ma35d1_i2c_enable_irq(i2c);
+
+ ret = pm_runtime_resume_and_get(i2c->dev);
+ if (ret) {
+ dev_err(i2c->dev, "failed to resume i2c controller\n");
+ return ret;
+ }
+
+ i2c->target = target;
+
+ val = readl(i2c->regs + MA35_CTL0);
+ val |= MA35_CTL_I2CEN;
+ writel(val, i2c->regs + MA35_CTL0);
+ slvaddr = target->addr << 1;
+ writel(slvaddr, i2c->regs + MA35_ADDR0);
+
+ /* I2C enter SLV mode */
+ ma35d1_i2c_write_CTL(i2c, MA35_CTL_SI_AA);
+
+ return 0;
+}
+
+static int ma35d1_unreg_target(struct i2c_client *target)
+{
+ struct ma35d1_i2c *i2c = i2c_get_adapdata(target->adapter);
+ unsigned int val;
+ int ret;
+
+ /* Disable I2C */
+ val = readl(i2c->regs + MA35_CTL0);
+ val &= ~MA35_CTL_I2CEN;
+ writel(val, i2c->regs + MA35_CTL0);
+
+ /* Disable I2C interrupt */
+ ma35d1_i2c_disable_irq(i2c);
+
+ i2c->target = NULL;
+
+ ret = pm_runtime_put_sync(i2c->dev);
+ if (ret)
+ dev_err(i2c->dev, "failed to suspend i2c controller");
+
+ return 0;
+}
+
+/* Declare Our I2C Functionality */
+static u32 ma35d1_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_SMBUS_EMUL;
+}
+
+/* I2C Bus Registration Info */
+static const struct i2c_algorithm ma35d1_i2c_algorithm = {
+ .xfer = ma35d1_i2c_xfer,
+ .functionality = ma35d1_i2c_func,
+ .reg_target = ma35d1_reg_target,
+ .unreg_target = ma35d1_unreg_target,
+};
+
+static int ma35d1_i2c_probe(struct platform_device *pdev)
+{
+ struct ma35d1_i2c *i2c;
+ struct resource *res;
+ int ret, clkdiv;
+ unsigned int busfreq;
+ struct device *dev = &pdev->dev;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ spin_lock_init(&i2c->lock);
+ init_waitqueue_head(&i2c->wait);
+
+ i2c->dev = dev;
+
+ i2c->clk = devm_clk_get_prepared(dev, NULL);
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(dev, PTR_ERR(i2c->clk),
+ "failed to get core clk\n");
+
+ i2c->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(i2c->regs))
+ return PTR_ERR(i2c->regs);
+
+ i2c->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(i2c->rst))
+ return dev_err_probe(dev, PTR_ERR(i2c->rst),
+ "failed to get reset control\n");
+
+ /* Setup info block for the I2C core */
+ strscpy(i2c->adap.name, "ma35d1-i2c", sizeof(i2c->adap.name));
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.algo = &ma35d1_i2c_algorithm;
+ i2c->adap.retries = 2;
+ i2c->adap.algo_data = i2c;
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c->adap.dev.of_node = pdev->dev.of_node;
+ i2c_set_adapdata(&i2c->adap, i2c);
+
+ /* Default to 100kHz if not specified in DT */
+ busfreq = 100000;
+ device_property_read_u32(dev, "clock-frequency", &busfreq);
+
+ /* Calculate divider based on the current peripheral clock rate */
+ clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(i2c->clk), busfreq * 4) - 1;
+ if (clkdiv < 0 || clkdiv > 0xffff)
+ return dev_err_probe(dev, -EINVAL, "invalid clkdiv value: %d\n",
+ clkdiv);
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq < 0)
+ return dev_err_probe(dev, i2c->irq, "failed to get irq\n");
+
+ platform_set_drvdata(pdev, i2c);
+
+ pm_runtime_set_autosuspend_delay(dev, I2C_PM_TIMEOUT_MS);
+ pm_runtime_use_autosuspend(dev);
+ devm_pm_runtime_enable(dev);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to resume device\n");
+
+ writel(FIELD_PREP(MA35_CLKDIV_MSK, clkdiv), i2c->regs + MA35_CLKDIV);
+
+ ret = devm_request_irq(dev, i2c->irq, ma35d1_i2c_irq, IRQF_SHARED,
+ dev_name(dev), i2c);
+ if (ret) {
+ dev_err_probe(dev, ret, "cannot claim IRQ %d\n", i2c->irq);
+ goto rpm_put;
+ }
+
+ ret = devm_i2c_add_adapter(dev, &i2c->adap);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add bus to i2c core\n");
+ goto rpm_put;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+rpm_put:
+ pm_runtime_put_noidle(dev);
+ return ret;
+}
+
+static int ma35d1_i2c_suspend(struct device *dev)
+{
+ struct ma35d1_i2c *i2c = dev_get_drvdata(dev);
+ unsigned int val;
+
+ spin_lock_irq(&i2c->lock);
+
+ /* Prepare for wake-up from I2C events if target mode is active */
+ if (i2c->target) {
+ val = readl(i2c->regs + MA35_CTL0);
+ val |= (MA35_CTL_SI | MA35_CTL_AA);
+ writel(val, i2c->regs + MA35_CTL0);
+ ma35d1_i2c_enable_irq(i2c);
+ }
+
+ spin_unlock_irq(&i2c->lock);
+
+ /* Setup wake-up control */
+ writel(0x1, i2c->regs + MA35_WKCTL);
+
+ /* Clear pending wake-up flags */
+ val = readl(i2c->regs + MA35_WKSTS);
+ writel(val, i2c->regs + MA35_WKSTS);
+
+ enable_irq_wake(i2c->irq);
+
+ return 0;
+}
+
+static int ma35d1_i2c_resume(struct device *dev)
+{
+ struct ma35d1_i2c *i2c = dev_get_drvdata(dev);
+ unsigned int val;
+
+ /* Disable wake-up */
+ writel(0x0, i2c->regs + MA35_WKCTL);
+
+ /* Clear pending wake-up flags */
+ val = readl(i2c->regs + MA35_WKSTS);
+ writel(val, i2c->regs + MA35_WKSTS);
+
+ disable_irq_wake(i2c->irq);
+
+ return 0;
+}
+
+static int ma35d1_i2c_runtime_suspend(struct device *dev)
+{
+ struct ma35d1_i2c *i2c = dev_get_drvdata(dev);
+ unsigned int val;
+
+ /* Disable I2C controller */
+ val = readl(i2c->regs + MA35_CTL0);
+ val &= ~MA35_CTL_I2CEN;
+ writel(val, i2c->regs + MA35_CTL0);
+
+ clk_disable(i2c->clk);
+
+ return 0;
+}
+
+static int ma35d1_i2c_runtime_resume(struct device *dev)
+{
+ struct ma35d1_i2c *i2c = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ ret = clk_enable(i2c->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock in resume\n");
+ return ret;
+ }
+
+ /* Enable I2C controller */
+ val = readl(i2c->regs + MA35_CTL0);
+ val |= MA35_CTL_I2CEN;
+ writel(val, i2c->regs + MA35_CTL0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ma35d1_i2c_pmops = {
+ SYSTEM_SLEEP_PM_OPS(ma35d1_i2c_suspend, ma35d1_i2c_resume)
+ RUNTIME_PM_OPS(ma35d1_i2c_runtime_suspend,
+ ma35d1_i2c_runtime_resume, NULL)
+};
+
+static const struct of_device_id ma35d1_i2c_of_match[] = {
+ { .compatible = "nuvoton,ma35d1-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ma35d1_i2c_of_match);
+
+static struct platform_driver ma35d1_i2c_driver = {
+ .probe = ma35d1_i2c_probe,
+ .driver = {
+ .name = "ma35d1-i2c",
+ .of_match_table = ma35d1_i2c_of_match,
+ .pm = pm_ptr(&ma35d1_i2c_pmops),
+ },
+};
+module_platform_driver(ma35d1_i2c_driver);
+
+MODULE_AUTHOR("Zi-Yu Chen <zychennvt@gmail.com>");
+MODULE_DESCRIPTION("MA35D1 I2C Bus Driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* [PATCH v3 1/3] dt-bindings: i2c: nuvoton,ma35d1-i2c: Add MA35D1 I2C controller
From: Zi-Yu Chen @ 2026-05-12 7:39 UTC (permalink / raw)
To: Jacky Huang, Andi Shyti
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-arm-kernel,
linux-i2c, devicetree, linux-kernel, Zi-Yu Chen,
Krzysztof Kozlowski
In-Reply-To: <20260512073953.564323-1-zychennvt@gmail.com>
Add device tree binding documentation for the I2C controller
found in the Nuvoton MA35D1 SoC.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Zi-Yu Chen <zychennvt@gmail.com>
---
.../bindings/i2c/nuvoton,ma35d1-i2c.yaml | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/nuvoton,ma35d1-i2c.yaml
diff --git a/Documentation/devicetree/bindings/i2c/nuvoton,ma35d1-i2c.yaml b/Documentation/devicetree/bindings/i2c/nuvoton,ma35d1-i2c.yaml
new file mode 100644
index 000000000000..f2c004049d86
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/nuvoton,ma35d1-i2c.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/nuvoton,ma35d1-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 I2C Controller
+
+maintainers:
+ - Zi-Yu Chen <zychennvt@gmail.com>
+
+description:
+ The Nuvoton MA35D1 I2C controller supports controller and optional target mode.
+
+allOf:
+ - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+ compatible:
+ const: nuvoton,ma35d1-i2c
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-frequency:
+ description:
+ Desired I2C bus clock frequency in Hz. The absence of this property
+ indicates the default frequency 100 kHz.
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - resets
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+ #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
+
+ i2c0: i2c@40800000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x40800000 0x1000>;
+ interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C0_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/3] i2c: ma35d1: Add support for MA35D1 I2C controller
From: Zi-Yu Chen @ 2026-05-12 7:39 UTC (permalink / raw)
To: Jacky Huang, Andi Shyti
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-arm-kernel,
linux-i2c, devicetree, linux-kernel, Zi-Yu Chen
This series adds support for the I2C controller found in the Nuvoton
MA35D1 SoC. The driver supports controller and optional target mode
and runtime power management.
The implementation has been tested on the Nuvoton MA35D1 SOM board.
Changes in v3:
- Fix minor DTS formatting issues (whitespace, missing newline)
v2: https://lore.kernel.org/r/20260316063726.41048-1-zychennvt@gmail.com
Changes in v2:
- Overall:
- Rebase on linux-i2c/i2c-next
- Switched terminology from "master/slave" to "controller/target".
- Patch 1 (dt-bindings):
- Simplified description and fixed 'reg' size in example.
- Patch 2 (driver):
- Modernized using devm_*, generic device properties, and FIELD_PREP/GENMASK.
- Optimized power management by moving clock control to runtime PM.
- Simplified code by removing redundant .remove(), .owner, and inlines.
- Added dev_err_probe() and default bus frequency handling.
- Patch 3 (dts):
- Moved i2c aliases to board dts and reordered nodes alphabetically.
v1: https://lore.kernel.org/r/20260302020822.13936-1-zychennvt@gmail.com
Zi-Yu Chen (3):
dt-bindings: i2c: nuvoton,ma35d1-i2c: Add MA35D1 I2C controller
i2c: ma35d1: Add Nuvoton MA35D1 I2C driver support
arm64: dts: nuvoton: Add I2C nodes for MA35D1 SoC
.../bindings/i2c/nuvoton,ma35d1-i2c.yaml | 63 ++
.../boot/dts/nuvoton/ma35d1-som-256m.dts | 15 +
arch/arm64/boot/dts/nuvoton/ma35d1.dtsi | 60 ++
drivers/i2c/busses/Kconfig | 13 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-ma35d1.c | 792 ++++++++++++++++++
6 files changed, 944 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/nuvoton,ma35d1-i2c.yaml
create mode 100644 drivers/i2c/busses/i2c-ma35d1.c
--
2.34.1
^ permalink raw reply
* Re: [LINUX PATCH] serial: xilinx_uartps: fix runtime PM race during probe
From: Greg Kroah-Hartman @ 2026-05-12 7:38 UTC (permalink / raw)
To: Shubhrajyoti Datta
Cc: linux-kernel, git, shubhrajyoti.datta, Jiri Slaby, Michal Simek,
linux-serial, linux-arm-kernel
In-Reply-To: <176bdb50948e99c40f8baaeaac9ffc5eaf10816f.1778567817.git.shubhrajyoti.datta@amd.com>
On Tue, May 12, 2026 at 12:11:33PM +0530, Shubhrajyoti Datta wrote:
> pm_runtime_enable() was called with usage_count=0, allowing the PM
> core to immediately queue a deferred suspend via pm_runtime_work.
> This raced with console write, causing cdns_runtime_suspend to
> fire before the port was fully registered.
>
> Hold a reference with pm_runtime_get_noresume() before enabling
> runtime PM.
>
> Fixes: d62100f1aac2 ("serial: xilinx_uartps: Add pm runtime support")
> Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
> ---
> drivers/tty/serial/xilinx_uartps.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
> index a072b75dbaf2..e316cac4f35b 100644
> --- a/drivers/tty/serial/xilinx_uartps.c
> +++ b/drivers/tty/serial/xilinx_uartps.c
> @@ -1800,6 +1800,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
> pm_runtime_use_autosuspend(&pdev->dev);
> pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
> pm_runtime_set_active(&pdev->dev);
> + pm_runtime_get_noresume(&pdev->dev);
> pm_runtime_enable(&pdev->dev);
> device_init_wakeup(port->dev, true);
>
> @@ -1824,6 +1825,8 @@ static int cdns_uart_probe(struct platform_device *pdev)
> "uart_add_one_port() failed; err=%i\n", rc);
> goto err_out_pm_disable;
> }
> + pm_runtime_mark_last_busy(&pdev->dev);
> + pm_runtime_put_autosuspend(&pdev->dev);
>
> #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
> /* This is not port which is used for console that's why clean it up */
> @@ -1842,6 +1845,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
> return 0;
>
> err_out_pm_disable:
> + pm_runtime_put_noidle(&pdev->dev);
> pm_runtime_disable(&pdev->dev);
> pm_runtime_set_suspended(&pdev->dev);
> pm_runtime_dont_use_autosuspend(&pdev->dev);
> --
> 2.49.1
>
Hi,
This is the friendly patch-bot of Greg Kroah-Hartman. You have sent him
a patch that has triggered this response. He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created. Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.
You are receiving this message because of the following common error(s)
as indicated below:
- You have marked a patch with a "Fixes:" tag for a commit that is in an
older released kernel, yet you do not have a cc: stable line in the
signed-off-by area at all, which means that the patch will not be
applied to any older kernel releases. To properly fix this, please
follow the documented rules in the
Documentation/process/stable-kernel-rules.rst file for how to resolve
this.
If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.
thanks,
greg k-h's patch email bot
^ permalink raw reply
* Re: [PATCH] ARM: OMAP2+: Make OMAP4 finish_suspend callback CFI-safe
From: Nathan Chancellor @ 2026-05-12 7:34 UTC (permalink / raw)
To: Mithil Bavishi
Cc: Aaro Koskinen, Andreas Kemnade, Kevin Hilman, Roger Quadros,
Tony Lindgren, Russell King, Sami Tolvanen, Kees Cook,
linux-arm-kernel, linux-omap, llvm, linux-kernel
In-Reply-To: <20260512042341.1452-1-bavishimithil@gmail.com>
On Tue, May 12, 2026 at 12:23:41AM -0400, Mithil Bavishi wrote:
> With CONFIG_CFI enabled, OMAP4 can trap in omap4_enter_lowpower()
> because omap_pm_ops.finish_suspend points directly to the assembly
> routine omap4_finish_suspend, which lacks the expected KCFI type
> metadata.
It sounds like omap4_finish_suspend() should be defined with
SYM_TYPED_FUNC_START then? Is that the case for all of the other
functions that are added to omap_pm_ops?
> Add a small C wrapper with the exact callback prototype and use it for
> finish_suspend.
>
> Signed-off-by: Mithil Bavishi <bavishimithil@gmail.com>
> ---
> arch/arm/mach-omap2/omap-mpuss-lowpower.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
> index f18ef45e2..1c51b2f97 100644
> --- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c
> +++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
> @@ -111,6 +111,11 @@ static struct cpu_pm_ops omap_pm_ops = {
> .hotplug_restart = dummy_cpu_resume,
> };
>
> +static int omap4_finish_suspend_wrapper(unsigned long cpu_state)
> +{
> + return omap4_finish_suspend(cpu_state);
> +}
> +
> /*
> * Program the wakeup routine address for the CPU0 and CPU1
> * used for OFF or DORMANT wakeup.
> @@ -437,7 +442,7 @@ int __init omap4_mpuss_init(void)
> }
>
> if (cpu_is_omap44xx()) {
> - omap_pm_ops.finish_suspend = omap4_finish_suspend;
> + omap_pm_ops.finish_suspend = omap4_finish_suspend_wrapper;
> omap_pm_ops.resume = omap4_cpu_resume;
> omap_pm_ops.scu_prepare = scu_pwrst_prepare;
> omap_pm_ops.hotplug_restart = omap4_secondary_startup;
> --
> 2.43.0
>
--
Cheers,
Nathan
^ permalink raw reply
* Re: [PATCH v17 24/24] KVM: selftests: Add guest_memfd testcase to fault-in on !mmap()'d memory
From: Zenghui Yu @ 2026-05-12 7:28 UTC (permalink / raw)
To: Sean Christopherson
Cc: Zenghui Yu, Paolo Bonzini, Marc Zyngier, Oliver Upton, kvm,
linux-arm-kernel, kvmarm, linux-kernel, Ira Weiny, Gavin Shan,
Shivank Garg, Vlastimil Babka, Xiaoyao Li, David Hildenbrand,
Fuad Tabba, Ackerley Tng, Tao Chan, James Houghton
In-Reply-To: <aeJkCGwsrRmuBikT@google.com>
Hi Sean,
On 2026/4/18 0:47, Sean Christopherson wrote:
>
> Does this fix things for you? If so, I'll send a proper patch.
>
> diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
> index ec7644aae999..c682bda722f9 100644
> --- a/tools/testing/selftests/kvm/guest_memfd_test.c
> +++ b/tools/testing/selftests/kvm/guest_memfd_test.c
> @@ -510,9 +510,15 @@ static void test_guest_memfd_guest(void)
> "Default VM type should support INIT_SHARED, supported flags = 0x%x",
> vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));
>
> + /*
> + * Use the guest's page size for all accesses, e.g. to avoid having to
> + * map multiple pages, but create the guest_memfd instance with the max
> + * of the host or guest page size, as KVM requires gmem files to be
> + * aligned to the host page size.
> + */
> size = vm->page_size;
> - fd = vm_create_guest_memfd(vm, size, GUEST_MEMFD_FLAG_MMAP |
> - GUEST_MEMFD_FLAG_INIT_SHARED);
> + fd = vm_create_guest_memfd(vm, max(size, page_size), GUEST_MEMFD_FLAG_MMAP |
> + GUEST_MEMFD_FLAG_INIT_SHARED);
> vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0);
>
> mem = kvm_mmap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
This results in another failure at
| vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size,
NULL, fd, 0);
# ./guest_memfd_test
Random seed: 0x6b8b4567
__vm_create: mode='PA-bits:40, VA-bits:48, 4K pages' type='0', pages='672'
==== Test Assertion Failure ====
lib/kvm_util.c:974: !ret
pid=284539 tid=284539 errno=22 - Invalid argument
1 0x00000000004008fb: test_guest_memfd_guest at guest_memfd_test.c:522
2 (inlined by) main at guest_memfd_test.c:561
3 0x0000000000417623: __libc_start_call_main at libc-start.o:?
4 0x000000000041796b: __libc_start_main_impl at ??:?
5 0x0000000000400cef: _start at ??:?
KVM_SET_USER_MEMORY_REGION2 failed, errno = 22 (Invalid argument)
Thanks,
Zenghui
^ permalink raw reply
* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Arnaud POULIQUEN @ 2026-05-12 7:22 UTC (permalink / raw)
To: Andrew Lunn, Mathieu Poirier
Cc: tanmay.shah, Beleswar Prasad Padhi, Shenwei Wang, Linus Walleij,
Bartosz Golaszewski, Jonathan Corbet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Frank Li,
Sascha Hauer, Shuah Khan, linux-gpio@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
Pengutronix Kernel Team, Fabio Estevam, Peng Fan,
devicetree@vger.kernel.org, linux-remoteproc@vger.kernel.org,
imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
dl-linux-imx, Bartosz Golaszewski
In-Reply-To: <4ae35920-2539-4b12-8dea-efd407b8aaeb@lunn.ch>
Hello Andrew,
On 5/11/26 20:18, Andrew Lunn wrote:
>> Arnaud, Beleswar, Andrew and I are all advocating for one endpoint per
>> GPIO controller. The remaining issue it about the best way to work
>> out source and destination addresses between Linux and the remote
>> processor. I'm running out of time for today but I'll return to this
>> thread with a final analysis by the end of the week.
>
> How many of the participants here will be in Minneapolis next week for
> the Embedded Linux Conference? There is even a talk about this:
>
> https://osselcna2026.sched.com/event/2JQpx/building-virtual-drivers-with-rpmsg-key-design-principles-challenges-trade-offs-beleswar-prasad-padhi-texas-instruments?iframe=yes&w=100%&sidebar=yes&bg=no
>
> Maybe we can get together and decide on the final design after the
> session.
I won’t be there, but I can join remotely if a call is scheduled,
Regards,
Arnaud
>
> Andrew
^ permalink raw reply
* Re: [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors
From: Shaopeng Tan (Fujitsu) @ 2026-05-12 7:16 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght@marvell.com, baisheng.gao@unisoc.com,
baolin.wang@linux.alibaba.com, carl@os.amperecomputing.com,
dave.martin@arm.com, david@kernel.org, dfustini@baylibre.com,
fenghuay@nvidia.com, gshan@redhat.com, james.morse@arm.com,
jonathan.cameron@huawei.com, kobak@nvidia.com,
lcherian@marvell.com, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, peternewman@google.com,
punit.agrawal@oss.qualcomm.com, quic_jiles@quicinc.com,
reinette.chatre@intel.com, rohit.mathew@arm.com,
scott@os.amperecomputing.com, sdonthineni@nvidia.com,
xhao@linux.alibaba.com, zengheng4@huawei.com, x86@kernel.org
In-Reply-To: <20260511154147.557481-3-ben.horgan@arm.com>
Hello Ben,
> MPAM is able to emulate ABMC, i.e. mbm_event mode, by making memory
> bandwidth monitors assignable. Rather than supporting the 'default'
> mbm_assign_mode always use 'mbm_event' mode even if there are sufficient
> memory bandwidth monitors. The per monitor event configuration is only
> provided by resctrl when in 'mbm_event' mode and so only allowing
> 'mbm_event' mode will make it easier to support per-monitor event
> configuration for MPAM. For the moment, the only event supported is
> mbm_total_event with no bandwidth type configuration. The 'mbm_assign_mode'
> file will still show 'default' when there is no support for memory
> bandwidth monitoring.
>
> The monitors need to be allocated from the driver, and mapped to whichever
> control/monitor group resctrl wants to use them with.
>
> Add a second array to hold the monitor values indexed by resctrl's cntr_id.
>
> When CDP is in use, two monitors are needed so the available number of
> counters halves. Platforms with one monitor will have zero monitors when
> CDP is in use.
>
> Co-developed-by: James Morse <james.morse@arm.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc v1:
> abmc enabled even if enough counters
> Helpers from dropped free running commits
> carry on with zero counters if using cdp
> set config bits
> use kmalloc_objs
> drop tags for rework
> Configure mbm_cntr_configurable, mbm_cntr_assign_fixed
>
> Changes since rfc v2:
> Don't set mon->assigned_counters to an error pointer
> Fix mpam_resctrl_teardown_mon()
> Remove free running check
> Separate cleanup allocations, e.g. __free(), from the rest
> Restrict scope on err in mpam_resctrl_monitor_init()
> ---
> drivers/resctrl/mpam_internal.h | 6 +-
> drivers/resctrl/mpam_resctrl.c | 138 +++++++++++++++++++++++++++++++-
> 2 files changed, 140 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
> index 1914aefdcba9..7a166b395b5a 100644
> --- a/drivers/resctrl/mpam_internal.h
> +++ b/drivers/resctrl/mpam_internal.h
> @@ -411,7 +411,11 @@ struct mpam_resctrl_res {
> struct mpam_resctrl_mon {
> struct mpam_class *class;
>
> - /* per-class data that resctrl needs will live here */
> + /* Array of allocated MBWU monitors, indexed by (closid, rmid). */
> + int *mbwu_idx_to_mon;
> +
> + /* Array of assigned MBWU monitors, indexed by idx argument. */
> + int *assigned_counters;
> };
>
> static inline int mpam_alloc_csu_mon(struct mpam_class *class)
> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
> index f70fa65d39e4..cba295621f56 100644
> --- a/drivers/resctrl/mpam_resctrl.c
> +++ b/drivers/resctrl/mpam_resctrl.c
> @@ -75,6 +75,8 @@ static DECLARE_WAIT_QUEUE_HEAD(wait_cacheinfo_ready);
> */
> static bool resctrl_enabled;
>
> +static unsigned int l3_num_allocated_mbwu = ~0;
> +
> bool resctrl_arch_alloc_capable(void)
> {
> struct mpam_resctrl_res *res;
> @@ -140,7 +142,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
>
> bool resctrl_arch_mbm_cntr_assign_enabled(struct rdt_resource *r)
> {
> - return false;
> + return (r == &mpam_resctrl_controls[RDT_RESOURCE_L3].resctrl_res);
> }
>
> int resctrl_arch_mbm_cntr_assign_set(struct rdt_resource *r, bool enable)
> @@ -185,6 +187,22 @@ static void resctrl_reset_task_closids(void)
> read_unlock(&tasklist_lock);
> }
>
> +static void mpam_resctrl_monitor_sync_abmc_vals(struct rdt_resource *l3)
> +{
> + l3->mon.num_mbm_cntrs = l3_num_allocated_mbwu;
> + if (cdp_enabled)
> + l3->mon.num_mbm_cntrs /= 2;
> +
> + /*
> + * Continue as normal even if there are zero counters to avoid giving
> + * resctrl mixed messages.
> + */
> + l3->mon.mbm_cntr_assignable = true;
> + l3->mon.mbm_assign_on_mkdir = true;
> + l3->mon.mbm_cntr_configurable = false;
> + l3->mon.mbm_cntr_assign_fixed = true;
> +}
> +
> int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
> {
> u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID;
> @@ -244,6 +262,7 @@ int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
> WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current));
>
> resctrl_reset_task_closids();
> + mpam_resctrl_monitor_sync_abmc_vals(l3);
>
> for_each_possible_cpu(cpu)
> mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0);
> @@ -613,6 +632,9 @@ static bool class_has_usable_mbwu(struct mpam_class *class)
> if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
> return false;
>
> + if (!cprops->num_mbwu_mon)
> + return false;
> +
> return true;
> }
>
> @@ -935,6 +957,52 @@ static void mpam_resctrl_pick_mba(void)
> }
> }
>
> +static void __free_mbwu_mon(struct mpam_class *class, int *array,
> + u16 num_mbwu_mon)
> +{
> + for (int i = 0; i < num_mbwu_mon; i++) {
> + if (array[i] < 0)
> + continue;
> +
> + mpam_free_mbwu_mon(class, array[i]);
> + array[i] = ~0;
> + }
> +}
> +
> +static int __alloc_mbwu_mon(struct mpam_class *class, int *array,
> + u16 num_mbwu_mon)
> +{
> + for (int i = 0; i < num_mbwu_mon; i++) {
> + int mbwu_mon = mpam_alloc_mbwu_mon(class);
> +
> + if (mbwu_mon < 0) {
> + __free_mbwu_mon(class, array, num_mbwu_mon);
> + return mbwu_mon;
> + }
> + array[i] = mbwu_mon;
> + }
> +
> + l3_num_allocated_mbwu = min(l3_num_allocated_mbwu, num_mbwu_mon);
> +
> + return 0;
> +}
> +
> +static int *__alloc_mbwu_array(struct mpam_class *class, u16 num_mbwu_mon)
> +{
> + int err;
> +
> + int *array __free(kfree) = kmalloc_objs(*array, num_mbwu_mon);
> + if (!array)
> + return ERR_PTR(-ENOMEM);
> +
> + memset(array, -1, num_mbwu_mon * sizeof(*array));
> +
> + err = __alloc_mbwu_mon(class, array, num_mbwu_mon);
> + if (err)
> + return ERR_PTR(err);
> + return_ptr(array);
> +}
> +
> static void counter_update_class(enum resctrl_event_id evt_id,
> struct mpam_class *class)
> {
> @@ -1089,6 +1157,38 @@ static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp)
> return comp->comp_id;
> }
>
> +/*
> + * This must run after all event counters have been picked so that any free
> + * running counters have already been allocated.
> + */
> +static int mpam_resctrl_monitor_init_abmc(struct mpam_resctrl_mon *mon)
> +{
> + struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
> + struct rdt_resource *l3 = &res->resctrl_res;
> + struct mpam_class *class = mon->class;
> + u16 num_mbwu_mon;
> + size_t num_rmid = resctrl_arch_system_num_rmid_idx();
> + int *cntrs;
Wouldn't a reverse tree format be better?
> + int *rmid_array __free(kfree) = kmalloc_objs(*rmid_array, num_rmid);
> + if (!rmid_array) {
> + pr_debug("Failed to allocate RMID array\n");
> + return -ENOMEM;
> + }
> + memset(rmid_array, -1, num_rmid * sizeof(*rmid_array));
> +
> + num_mbwu_mon = class->props.num_mbwu_mon;
> + cntrs = __alloc_mbwu_array(mon->class, num_mbwu_mon);
> + if (IS_ERR(cntrs))
> + return PTR_ERR(cntrs);
> + mon->assigned_counters = cntrs;
> + mon->mbwu_idx_to_mon = no_free_ptr(rmid_array);
> +
> + mpam_resctrl_monitor_sync_abmc_vals(l3);
> +
> + return 0;
> +}
> +
> static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
> enum resctrl_event_id type)
> {
> @@ -1133,8 +1233,21 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
> */
> l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
>
> - if (resctrl_enable_mon_event(type, false, 0, NULL))
> - l3->mon_capable = true;
> + if (type == QOS_L3_MBM_TOTAL_EVENT_ID) {
> + int err;
> +
> + err = mpam_resctrl_monitor_init_abmc(mon);
> + if (err)
> + return err;
> +
> + static_assert(MAX_EVT_CONFIG_BITS == 0x7f);
> + l3->mon.mbm_cfg_mask = MAX_EVT_CONFIG_BITS;
> + }
> +
> + if (!resctrl_enable_mon_event(type, false, 0, NULL))
> + return -EINVAL;
> +
> + l3->mon_capable = true;
>
> return 0;
> }
> @@ -1697,6 +1810,23 @@ void mpam_resctrl_exit(void)
> resctrl_exit();
> }
>
> +static void mpam_resctrl_teardown_mon(struct mpam_resctrl_mon *mon, struct mpam_class *class)
> +{
> + u32 num_mbwu_mon = l3_num_allocated_mbwu;
> +
> + if (mon->mbwu_idx_to_mon)
> + return;
Isn't it 'if (mon->mbwu_idx_to_mon == NULL)' ?
Best regards,
Shaopeng TAN
> + if (mon->assigned_counters) {
> + __free_mbwu_mon(class, mon->assigned_counters, num_mbwu_mon);
> + kfree(mon->assigned_counters);
> + mon->assigned_counters = NULL;
> + }
> +
> + kfree(mon->mbwu_idx_to_mon);
> + mon->mbwu_idx_to_mon = NULL;
> +}
> +
> /*
> * The driver is detaching an MSC from this class, if resctrl was using it,
> * pull on resctrl_exit().
> @@ -1719,6 +1849,8 @@ void mpam_resctrl_teardown_class(struct mpam_class *class)
> for_each_mpam_resctrl_mon(mon, eventid) {
> if (mon->class == class) {
> mon->class = NULL;
> +
> + mpam_resctrl_teardown_mon(mon, class);
> break;
> }
> }
> --
> 2.43.0
^ permalink raw reply
* Re: [PATCH v3 4/7] PCI: dwc: Use common pci_host_common_link_train_delay() helper
From: Krzysztof Wilczyński @ 2026-05-12 7:14 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Hans Zhang, bhelgaas, lpieralisi, vigneshr, jingoohan1,
thomas.petazzoni, pali, ryder.lee, claudiu.beznea.uj, mpillai,
robh, s-vadapalli, linux-omap, linux-arm-kernel, claudiu.beznea,
linux-mediatek, linux-renesas-soc, linux-pci, linux-kernel
In-Reply-To: <6xcq6e6c2jc7wxhvv3kjwp5d2ik62ed7kukb3e62yogf3phtxk@6besox4h2v4n>
Hello,
> > > - /*
> > > - * As per PCIe r6.0, sec 6.6.1, a Downstream Port that supports Link
> > > - * speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms
> > > - * after Link training completes before sending a Configuration Request.
> > > - */
> > > - if (pci->max_link_speed > 2)
> > > - msleep(PCIE_RESET_CONFIG_WAIT_MS);
> > > + pci_host_common_link_train_delay(pci->max_link_speed);
> >
> > This comment could move to the helper you added.
> >
>
> Not required, as the macro definition in drivers/pci/pci.h already has an
> elaborative comment with spec citation.
So it does, indeed. Thank you for checking!
Krzysztof
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox