From fc1e8a6f129d87c64ac8e58b50d9dfa66217cfda Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 18 Feb 2026 14:21:35 -0800 Subject: [PATCH 001/712] Input: bcm5974 - recover from failed mode switch Mode switches sent before control response are ignored. This results in an unresponsive trackpad and "bcm5974: bad trackpad package, length: 8" repeated in logs. On receiving unknown 8-byte packets, assume that mode switch was ignored and schedule an asynchronous mode reset. The reset will switch the device to normal mode, wait, then switch back to wellspring mode. Signed-off-by: Liam Mitchell Link: https://lore.kernel.org/linux-input/CAOQ1CL4+DP1TuLAGNsz5GdFBTHvnTg=5q=Dr2Z1OQc6RXydSYA@mail.gmail.com/ Acked-by: Henrik Rydberg Link: https://patch.msgid.link/20260213-bcm5974-reset-v2-1-1837851336b0@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/bcm5974.c | 42 ++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index dfdfb59cc8b5..fe52f15c0c10 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -286,6 +286,8 @@ struct bcm5974 { const struct tp_finger *index[MAX_FINGERS]; /* finger index data */ struct input_mt_pos pos[MAX_FINGERS]; /* position array */ int slots[MAX_FINGERS]; /* slot assignments */ + struct work_struct mode_reset_work; + unsigned long last_mode_reset; }; /* trackpad finger block data, le16-aligned */ @@ -696,6 +698,32 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) return retval; } +/* + * Mode switches sent before the control response are ignored. + * Fixing this state requires switching to normal mode and waiting + * about 1ms before switching back to wellspring mode. + */ +static void bcm5974_mode_reset_work(struct work_struct *work) +{ + struct bcm5974 *dev = container_of(work, struct bcm5974, mode_reset_work); + int error; + + guard(mutex)(&dev->pm_mutex); + dev->last_mode_reset = jiffies; + + error = bcm5974_wellspring_mode(dev, false); + if (error) { + dev_err(&dev->intf->dev, "reset to normal mode failed\n"); + return; + } + + fsleep(1000); + + error = bcm5974_wellspring_mode(dev, true); + if (error) + dev_err(&dev->intf->dev, "mode switch after reset failed\n"); +} + static void bcm5974_irq_button(struct urb *urb) { struct bcm5974 *dev = urb->context; @@ -752,10 +780,20 @@ static void bcm5974_irq_trackpad(struct urb *urb) if (dev->tp_urb->actual_length == 2) goto exit; - if (report_tp_state(dev, dev->tp_urb->actual_length)) + if (report_tp_state(dev, dev->tp_urb->actual_length)) { dprintk(1, "bcm5974: bad trackpad package, length: %d\n", dev->tp_urb->actual_length); + /* + * Receiving a HID packet means we aren't in wellspring mode. + * If we haven't tried a reset in the last second, try now. + */ + if (dev->tp_urb->actual_length == 8 && + time_after(jiffies, dev->last_mode_reset + msecs_to_jiffies(1000))) { + schedule_work(&dev->mode_reset_work); + } + } + exit: error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC); if (error) @@ -906,6 +944,7 @@ static int bcm5974_probe(struct usb_interface *iface, dev->intf = iface; dev->input = input_dev; dev->cfg = *cfg; + INIT_WORK(&dev->mode_reset_work, bcm5974_mode_reset_work); mutex_init(&dev->pm_mutex); /* setup urbs */ @@ -998,6 +1037,7 @@ static void bcm5974_disconnect(struct usb_interface *iface) { struct bcm5974 *dev = usb_get_intfdata(iface); + disable_work_sync(&dev->mode_reset_work); usb_set_intfdata(iface, NULL); input_unregister_device(dev->input); From 5839419cffc7788a356428d321e3ec18055c0286 Mon Sep 17 00:00:00 2001 From: Christoffer Sandberg Date: Mon, 23 Feb 2026 15:20:45 +0100 Subject: [PATCH 002/712] Input: i8042 - add TUXEDO InfinityBook Max 16 Gen10 AMD to i8042 quirk table The device occasionally wakes up from suspend with missing input on the internal keyboard and the following suspend attempt results in an instant wake-up. The quirks fix both issues for this device. Signed-off-by: Christoffer Sandberg Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260223142054.50310-1-wse@tuxedocomputers.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-acpipnpio.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h index d2cf940b105a..8ebdf4fb9030 100644 --- a/drivers/input/serio/i8042-acpipnpio.h +++ b/drivers/input/serio/i8042-acpipnpio.h @@ -1187,6 +1187,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "WUJIE Series-X5SP4NAG"), From 7adaaee5edd35a423ae199c41b86bd1ed60ed483 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 23 Feb 2026 15:05:15 -0800 Subject: [PATCH 003/712] Input: synaptics-rmi4 - fix a locking bug in an error path Lock f54->data_mutex when entering the function statement since jumping to the 'error' label when checking report_size fails causes that mutex to be unlocked. This bug has been detected by the Clang thread-safety checker. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics") Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/20260223215118.2154194-16-bvanassche@acm.org Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f54.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index ac4041a69fcd..61909e1a39e2 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -538,6 +538,8 @@ static void rmi_f54_work(struct work_struct *work) int error; int i; + mutex_lock(&f54->data_mutex); + report_size = rmi_f54_get_report_size(f54); if (report_size == 0) { dev_err(&fn->dev, "Bad report size, report type=%d\n", @@ -546,8 +548,6 @@ static void rmi_f54_work(struct work_struct *work) goto error; /* retry won't help */ } - mutex_lock(&f54->data_mutex); - /* * Need to check if command has completed. * If not try again later. From 289cf6f9145913590f74f8d00a4a23e4e9be75bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Tue, 10 Feb 2026 21:38:12 +0300 Subject: [PATCH 004/712] drm/tyr: gpu: fix GpuInfo::log model/version decoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GpuInfo::log() was decoding GPU_ID like this: major = (self.gpu_id >> 16) & 0xff; minor = (self.gpu_id >> 8) & 0xff; status = self.gpu_id & 0xff; That does not match the Mali GPU_ID layout and mixes unrelated fields. Due to that, model detection becomes `mali-unknown` on rk3588s which is wrong. We can already get all the version information with a single GpuId::from call (less code and cleaner), so this patch uses it. Also renamed `GpuModels` fields from `major/minor` to `arch_major/prod_major` to reflect their real meaning. This change was tested on Orange Pi 5 (rk3588s) board and the results are as follows: Before this change: $ dmesg | grep 'tyr' [ 19.698338] tyr fb000000.gpu: mali-unknown id 0xa867 major 0x67 minor 0x0 status 0x5 [ 19.699050] tyr fb000000.gpu: Features: L2:0x7120306 Tiler:0x809 Mem:0x301 MMU:0x2830 AS:0xff [ 19.699817] tyr fb000000.gpu: shader_present=0x0000000000050005 l2_present=0x0000000000000001 tiler_present=0x0000000000000001 [ 19.702493] tyr fb000000.gpu: Tyr initialized correctly. After this change: $ dmesg | grep 'tyr' [ 19.591692] tyr fb000000.gpu: mali-g610 id 0xa867 major 0x0 minor 0x0 status 0x5 [ 19.592374] tyr fb000000.gpu: Features: L2:0x7120306 Tiler:0x809 Mem:0x301 MMU:0x2830 AS:0xff [ 19.593141] tyr fb000000.gpu: shader_present=0x0000000000050005 l2_present=0x0000000000000001 tiler_present=0x0000000000000001 [ 19.595831] tyr fb000000.gpu: Tyr initialized correctly. Signed-off-by: Onur Özkan Reviewed-by: Boris Brezillon Tested-by: Alvin Sun Link: https://patch.msgid.link/20260210183812.261142-1-work@onurozkan.dev Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/gpu.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/tyr/gpu.rs b/drivers/gpu/drm/tyr/gpu.rs index 64ca8311d4e8..ca2a6309e760 100644 --- a/drivers/gpu/drm/tyr/gpu.rs +++ b/drivers/gpu/drm/tyr/gpu.rs @@ -84,13 +84,11 @@ pub(crate) fn new(dev: &Device, iomem: &Devres) -> Result { } pub(crate) fn log(&self, pdev: &platform::Device) { - let major = (self.gpu_id >> 16) & 0xff; - let minor = (self.gpu_id >> 8) & 0xff; - let status = self.gpu_id & 0xff; + let gpu_id = GpuId::from(self.gpu_id); let model_name = if let Some(model) = GPU_MODELS .iter() - .find(|&f| f.major == major && f.minor == minor) + .find(|&f| f.arch_major == gpu_id.arch_major && f.prod_major == gpu_id.prod_major) { model.name } else { @@ -102,9 +100,9 @@ pub(crate) fn log(&self, pdev: &platform::Device) { "mali-{} id 0x{:x} major 0x{:x} minor 0x{:x} status 0x{:x}", model_name, self.gpu_id >> 16, - major, - minor, - status + gpu_id.ver_major, + gpu_id.ver_minor, + gpu_id.ver_status ); dev_info!( @@ -166,14 +164,14 @@ unsafe impl AsBytes for GpuInfo {} struct GpuModels { name: &'static str, - major: u32, - minor: u32, + arch_major: u32, + prod_major: u32, } const GPU_MODELS: [GpuModels; 1] = [GpuModels { name: "g610", - major: 10, - minor: 7, + arch_major: 10, + prod_major: 7, }]; #[allow(dead_code)] From ef2964f11e9f24b4681b52161cc37ba1a226d5af Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Mon, 23 Feb 2026 12:38:33 -0800 Subject: [PATCH 005/712] drm/tyr: Use vertical style for imports Currently Tyr uses rustfmt style for imports, but the kernel uses a vertical layout that makes it easier to resolve conflicts and rebase. Import guidelines are documented here: https://docs.kernel.org/rust/coding-guidelines.html#imports Change all of Tyr's imports to use the vertical layout. This will ease the introduction of additional Tyr patches upstream. There should be no functional changes in this patch. Reviewed-by: Boris Brezillon Signed-off-by: Deborah Brouwer Link: https://patch.msgid.link/20260223203833.207955-1-deborah.brouwer@collabora.com Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/driver.rs | 60 ++++++++++++++++++++--------------- drivers/gpu/drm/tyr/file.rs | 16 ++++++---- drivers/gpu/drm/tyr/gem.rs | 13 +++++--- drivers/gpu/drm/tyr/gpu.rs | 36 +++++++++++++-------- drivers/gpu/drm/tyr/regs.rs | 16 ++++++---- 5 files changed, 86 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index beeffe36b6cb..259a5157eb47 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -1,31 +1,41 @@ // SPDX-License-Identifier: GPL-2.0 or MIT -use kernel::clk::Clk; -use kernel::clk::OptionalClk; -use kernel::device::Bound; -use kernel::device::Core; -use kernel::device::Device; -use kernel::devres::Devres; -use kernel::drm; -use kernel::drm::ioctl; -use kernel::io::poll; -use kernel::new_mutex; -use kernel::of; -use kernel::platform; -use kernel::prelude::*; -use kernel::regulator; -use kernel::regulator::Regulator; -use kernel::sizes::SZ_2M; -use kernel::sync::aref::ARef; -use kernel::sync::Arc; -use kernel::sync::Mutex; -use kernel::time; +use kernel::{ + clk::{ + Clk, + OptionalClk, // + }, + device::{ + Bound, + Core, + Device, // + }, + devres::Devres, + drm, + drm::ioctl, + io::poll, + new_mutex, + of, + platform, + prelude::*, + regulator, + regulator::Regulator, + sizes::SZ_2M, + sync::{ + aref::ARef, + Arc, + Mutex, // + }, + time, // +}; -use crate::file::File; -use crate::gem::TyrObject; -use crate::gpu; -use crate::gpu::GpuInfo; -use crate::regs; +use crate::{ + file::File, + gem::TyrObject, + gpu, + gpu::GpuInfo, + regs, // +}; pub(crate) type IoMem = kernel::io::mem::IoMem; diff --git a/drivers/gpu/drm/tyr/file.rs b/drivers/gpu/drm/tyr/file.rs index 0ef432947b73..48bff4476d74 100644 --- a/drivers/gpu/drm/tyr/file.rs +++ b/drivers/gpu/drm/tyr/file.rs @@ -1,12 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 or MIT -use kernel::drm; -use kernel::prelude::*; -use kernel::uaccess::UserSlice; -use kernel::uapi; +use kernel::{ + drm, + prelude::*, + uaccess::UserSlice, + uapi, // +}; -use crate::driver::TyrDevice; -use crate::TyrDriver; +use crate::{ + driver::TyrDevice, + TyrDriver, // +}; #[pin_data] pub(crate) struct File {} diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index 1273bf89dbd5..8f2d23e3c093 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 or MIT -use crate::driver::TyrDevice; -use crate::driver::TyrDriver; -use kernel::drm::gem; -use kernel::prelude::*; +use kernel::{ + drm::gem, + prelude::*, // +}; + +use crate::driver::{ + TyrDevice, + TyrDriver, // +}; /// GEM Object inner driver data #[pin_data] diff --git a/drivers/gpu/drm/tyr/gpu.rs b/drivers/gpu/drm/tyr/gpu.rs index ca2a6309e760..a88775160f98 100644 --- a/drivers/gpu/drm/tyr/gpu.rs +++ b/drivers/gpu/drm/tyr/gpu.rs @@ -1,20 +1,28 @@ // SPDX-License-Identifier: GPL-2.0 or MIT -use core::ops::Deref; -use core::ops::DerefMut; -use kernel::bits::genmask_u32; -use kernel::device::Bound; -use kernel::device::Device; -use kernel::devres::Devres; -use kernel::io::poll; -use kernel::platform; -use kernel::prelude::*; -use kernel::time::Delta; -use kernel::transmute::AsBytes; -use kernel::uapi; +use core::ops::{ + Deref, + DerefMut, // +}; +use kernel::{ + bits::genmask_u32, + device::{ + Bound, + Device, // + }, + devres::Devres, + io::poll, + platform, + prelude::*, + time::Delta, + transmute::AsBytes, + uapi, // +}; -use crate::driver::IoMem; -use crate::regs; +use crate::{ + driver::IoMem, + regs, // +}; /// Struct containing information that can be queried by userspace. This is read from /// the GPU's registers. diff --git a/drivers/gpu/drm/tyr/regs.rs b/drivers/gpu/drm/tyr/regs.rs index d3a541cb37c6..611870c2e6af 100644 --- a/drivers/gpu/drm/tyr/regs.rs +++ b/drivers/gpu/drm/tyr/regs.rs @@ -7,12 +7,16 @@ // does. #![allow(dead_code)] -use kernel::bits::bit_u32; -use kernel::device::Bound; -use kernel::device::Device; -use kernel::devres::Devres; -use kernel::io::Io; -use kernel::prelude::*; +use kernel::{ + bits::bit_u32, + device::{ + Bound, + Device, // + }, + devres::Devres, + io::Io, + prelude::*, // +}; use crate::driver::IoMem; From 8d1a65c2defdc4213a49008d0531bd35d26fdf35 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 23 Jan 2026 17:58:44 +0000 Subject: [PATCH 006/712] gpu: nova-core: remove redundant `.as_ref()` for `dev_*` print This is now handled by the macro itself. Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260123175854.176735-7-gary@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 2 +- drivers/gpu/nova-core/gpu.rs | 4 ++-- drivers/gpu/nova-core/gsp/boot.rs | 32 +++++++------------------------ 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 5a4cc047bcfc..e39885c0d5ca 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -70,7 +70,7 @@ impl pci::Driver for NovaCore { fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { pin_init::pin_init_scope(move || { - dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); + dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); pdev.enable_device_mem()?; pdev.set_master(); diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 9b042ef1a308..60c85fffaeaf 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -262,13 +262,13 @@ pub(crate) fn new<'a>( ) -> impl PinInit + 'a { try_pin_init!(Self { spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| { - dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec); + dev_info!(pdev,"NVIDIA ({})\n", spec); })?, // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. _: { gfw::wait_gfw_boot_completion(bar) - .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete\n"))?; + .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?; }, sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index be427fe26a58..c56029f444cb 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -170,15 +170,10 @@ pub(crate) fn boot( Some(libos_handle as u32), Some((libos_handle >> 32) as u32), )?; - dev_dbg!( - pdev.as_ref(), - "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", - mbox0, - mbox1 - ); + dev_dbg!(pdev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); dev_dbg!( - pdev.as_ref(), + pdev, "Using SEC2 to load and run the booter_load firmware...\n" ); @@ -190,19 +185,10 @@ pub(crate) fn boot( Some(wpr_handle as u32), Some((wpr_handle >> 32) as u32), )?; - dev_dbg!( - pdev.as_ref(), - "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n", - mbox0, - mbox1 - ); + dev_dbg!(pdev, "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n", mbox0, mbox1); if mbox0 != 0 { - dev_err!( - pdev.as_ref(), - "Booter-load failed with error {:#x}\n", - mbox0 - ); + dev_err!(pdev, "Booter-load failed with error {:#x}\n", mbox0); return Err(ENODEV); } @@ -216,11 +202,7 @@ pub(crate) fn boot( Delta::from_secs(5), )?; - dev_dbg!( - pdev.as_ref(), - "RISC-V active? {}\n", - gsp_falcon.is_riscv_active(bar), - ); + dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),); // Create and run the GSP sequencer. let seq_params = GspSequencerParams { @@ -239,8 +221,8 @@ pub(crate) fn boot( // Obtain and display basic GPU information. let info = commands::get_gsp_info(&mut self.cmdq, bar)?; match info.gpu_name() { - Ok(name) => dev_info!(pdev.as_ref(), "GPU name: {}\n", name), - Err(e) => dev_warn!(pdev.as_ref(), "GPU name unavailable: {:?}\n", e), + Ok(name) => dev_info!(pdev, "GPU name: {}\n", name), + Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e), } Ok(()) From d3f36fa57aa289c43e01da16c928a2cd971ad5dc Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Fri, 20 Feb 2026 18:09:15 -0800 Subject: [PATCH 007/712] gpu: nova-core: fix aux device registration for multi-GPU systems The auxiliary device registration was using a hardcoded ID of 0, which caused probe() to fail on multi-GPU systems with: sysfs: cannot create duplicate filename '/bus/auxiliary/devices/NovaCore.nova-drm.0' Fix this by using an atomic counter to generate unique IDs for each GPU's aux device registration. The TODO item to eventually use XArray for recycling aux device IDs is retained, but for now, this works very nicely. This has the side effect of making debugfs[1] work on multi-GPU systems. [1] https://lore.kernel.org/20260203224757.871729-1-ttabi@nvidia.com Reviewed-by: Gary Guo Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260221020952.412352-2-jhubbard@nvidia.com [ Use LKMM atomics; inline and slightly reword TODO comment. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index e39885c0d5ca..84b0e1703150 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -14,11 +14,20 @@ }, prelude::*, sizes::SZ_16M, - sync::Arc, // + sync::{ + atomic::{ + Atomic, + Relaxed, // + }, + Arc, + }, }; use crate::gpu::Gpu; +/// Counter for generating unique auxiliary device IDs. +static AUXILIARY_ID_COUNTER: Atomic = Atomic::new(0); + #[pin_data] pub(crate) struct NovaCore { #[pin] @@ -90,7 +99,9 @@ fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit Date: Thu, 22 Jan 2026 16:43:02 -0500 Subject: [PATCH 008/712] rust/drm: Fixup import styles This is to match https://docs.kernel.org/rust/coding-guidelines.html#imports There should be no functional changes in this patch. Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260122214316.3281257-1-lyude@redhat.com [ Move trailing `//` at the end. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/drm/device.rs | 31 ++++++++++++++++++++++++------- rust/kernel/drm/driver.rs | 12 +++++++++--- rust/kernel/drm/file.rs | 14 +++++++++++--- rust/kernel/drm/gem/mod.rs | 25 ++++++++++++++++++++----- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 3ce8f62a0056..ae123ffece79 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -6,15 +6,32 @@ use crate::{ alloc::allocator::Kmalloc, - bindings, device, drm, - drm::driver::AllocImpl, - error::from_err_ptr, - error::Result, + bindings, + device, + drm::{ + self, + driver::AllocImpl, // + }, + error::{ + from_err_ptr, + Result, // + }, prelude::*, - sync::aref::{ARef, AlwaysRefCounted}, - types::Opaque, + sync::aref::{ + ARef, + AlwaysRefCounted, // + }, + types::Opaque, // +}; +use core::{ + alloc::Layout, + mem, + ops::Deref, + ptr::{ + self, + NonNull, // + }, }; -use core::{alloc::Layout, mem, ops::Deref, ptr, ptr::NonNull}; #[cfg(CONFIG_DRM_LEGACY)] macro_rules! drm_legacy_fields { diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index e09f977b5b51..6bd097f16ce7 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -5,10 +5,16 @@ //! C header: [`include/drm/drm_drv.h`](srctree/include/drm/drm_drv.h) use crate::{ - bindings, device, devres, drm, - error::{to_result, Result}, + bindings, + device, + devres, + drm, + error::{ + to_result, + Result, // + }, prelude::*, - sync::aref::ARef, + sync::aref::ARef, // }; use macros::vtable; diff --git a/rust/kernel/drm/file.rs b/rust/kernel/drm/file.rs index 8c46f8d51951..7dade6dfa1ba 100644 --- a/rust/kernel/drm/file.rs +++ b/rust/kernel/drm/file.rs @@ -4,9 +4,17 @@ //! //! C header: [`include/drm/drm_file.h`](srctree/include/drm/drm_file.h) -use crate::{bindings, drm, error::Result, prelude::*, types::Opaque}; -use core::marker::PhantomData; -use core::pin::Pin; +use crate::{ + bindings, + drm, + error::Result, + prelude::*, + types::Opaque, // +}; +use core::{ + marker::PhantomData, + pin::Pin, // +}; /// Trait that must be implemented by DRM drivers to represent a DRM File (a client instance). pub trait DriverFile { diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index d49a9ba02635..56b7641b1405 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -6,14 +6,29 @@ use crate::{ alloc::flags::*, - bindings, drm, - drm::driver::{AllocImpl, AllocOps}, - error::{to_result, Result}, + bindings, + drm::{ + self, + driver::{ + AllocImpl, + AllocOps, // + }, + }, + error::{ + to_result, + Result, // + }, prelude::*, - sync::aref::{ARef, AlwaysRefCounted}, + sync::aref::{ + ARef, + AlwaysRefCounted, // + }, types::Opaque, }; -use core::{ops::Deref, ptr::NonNull}; +use core::{ + ops::Deref, + ptr::NonNull, // +}; /// A type alias for retrieving a [`Driver`]s [`DriverFile`] implementation from its /// [`DriverObject`] implementation. From 6ad005ce6994599e2ae338007a54dd21063aae15 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 22 Jan 2026 16:43:03 -0500 Subject: [PATCH 009/712] rust/drm: Remove imports covered by prelude::* This just removes any explicit imports of items in files that are already being pulled in by `use prelude::*;`. There should be no functional changes in this patch. Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260122214316.3281257-2-lyude@redhat.com Signed-off-by: Danilo Krummrich --- rust/kernel/drm/device.rs | 5 +---- rust/kernel/drm/driver.rs | 6 +----- rust/kernel/drm/file.rs | 6 +----- rust/kernel/drm/gem/mod.rs | 6 +----- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index ae123ffece79..629ef0bd1188 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -12,10 +12,7 @@ self, driver::AllocImpl, // }, - error::{ - from_err_ptr, - Result, // - }, + error::from_err_ptr, prelude::*, sync::aref::{ ARef, diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index 6bd097f16ce7..5233bdebc9fc 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -9,14 +9,10 @@ device, devres, drm, - error::{ - to_result, - Result, // - }, + error::to_result, prelude::*, sync::aref::ARef, // }; -use macros::vtable; /// Driver use the GEM memory manager. This should be set for all modern drivers. pub(crate) const FEAT_GEM: u32 = bindings::drm_driver_feature_DRIVER_GEM; diff --git a/rust/kernel/drm/file.rs b/rust/kernel/drm/file.rs index 7dade6dfa1ba..10160601ce5a 100644 --- a/rust/kernel/drm/file.rs +++ b/rust/kernel/drm/file.rs @@ -7,14 +7,10 @@ use crate::{ bindings, drm, - error::Result, prelude::*, types::Opaque, // }; -use core::{ - marker::PhantomData, - pin::Pin, // -}; +use core::marker::PhantomData; /// Trait that must be implemented by DRM drivers to represent a DRM File (a client instance). pub trait DriverFile { diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 56b7641b1405..b4199945db37 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -5,7 +5,6 @@ //! C header: [`include/drm/drm_gem.h`](srctree/include/drm/drm_gem.h) use crate::{ - alloc::flags::*, bindings, drm::{ self, @@ -14,10 +13,7 @@ AllocOps, // }, }, - error::{ - to_result, - Result, // - }, + error::to_result, prelude::*, sync::aref::{ ARef, From 0568b376a0b13da6582bce1f2e2bbb2eae7fc266 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 26 Jan 2026 15:23:01 -0500 Subject: [PATCH 010/712] gpu: nova-core: use checked arithmetic in FWSEC firmware parsing Use checked_add() and checked_mul() when computing offsets from firmware-provided values in new_fwsec(). Without checked arithmetic, corrupt firmware could cause integer overflow. The danger is not just wrapping to a huge value, but potentially wrapping to a small plausible offset that passes validation yet accesses entirely wrong data, causing silent corruption or security issues. Reviewed-by: Zhi Wang Signed-off-by: Joel Fernandes Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260126202305.2526618-2-joelagnelf@nvidia.com [acourbot@nvidia.com: rewrap commit message to make checkpatch happy.] [acourbot@nvidia.com: add missing empty lines after new code blocks.] [acourbot@nvidia.com: move SAFETY comments to the unsafe statement they describe.] [acourbot@nvidia.com: remove obvious computation comments and use `CALC:` for the remaining ones.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/fwsec.rs | 64 ++++++++++++++----------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index bfb7b06b13d1..df3d8de14ca1 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -45,10 +45,7 @@ Signed, Unsigned, // }, - num::{ - FromSafeCast, - IntoSafeCast, // - }, + num::FromSafeCast, vbios::Vbios, }; @@ -266,7 +263,12 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re let ucode = bios.fwsec_image().ucode(&desc)?; let mut dma_object = DmaObject::from_data(dev, ucode)?; - let hdr_offset = usize::from_safe_cast(desc.imem_load_size() + desc.interface_offset()); + let hdr_offset = desc + .imem_load_size() + .checked_add(desc.interface_offset()) + .map(usize::from_safe_cast) + .ok_or(EINVAL)?; + // SAFETY: we have exclusive access to `dma_object`. let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?; @@ -276,26 +278,29 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re // Find the DMEM mapper section in the firmware. for i in 0..usize::from(hdr.entry_count) { + // CALC: hdr_offset + header_size + i * entry_size. + let entry_offset = hdr_offset + .checked_add(usize::from(hdr.header_size)) + .and_then(|o| o.checked_add(i.checked_mul(usize::from(hdr.entry_size))?)) + .ok_or(EINVAL)?; + // SAFETY: we have exclusive access to `dma_object`. - let app: &FalconAppifV1 = unsafe { - transmute( - &dma_object, - hdr_offset + usize::from(hdr.header_size) + i * usize::from(hdr.entry_size), - ) - }?; + let app: &FalconAppifV1 = unsafe { transmute(&dma_object, entry_offset) }?; if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER { continue; } let dmem_base = app.dmem_base; - // SAFETY: we have exclusive access to `dma_object`. - let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe { - transmute_mut( - &mut dma_object, - (desc.imem_load_size() + dmem_base).into_safe_cast(), - ) - }?; + let dmem_mapper_offset = desc + .imem_load_size() + .checked_add(dmem_base) + .map(usize::from_safe_cast) + .ok_or(EINVAL)?; + + let dmem_mapper: &mut FalconAppifDmemmapperV3 = + // SAFETY: we have exclusive access to `dma_object`. + unsafe { transmute_mut(&mut dma_object, dmem_mapper_offset) }?; dmem_mapper.init_cmd = match cmd { FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS, @@ -303,13 +308,15 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re }; let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset; - // SAFETY: we have exclusive access to `dma_object`. - let frts_cmd: &mut FrtsCmd = unsafe { - transmute_mut( - &mut dma_object, - (desc.imem_load_size() + cmd_in_buffer_offset).into_safe_cast(), - ) - }?; + let frts_cmd_offset = desc + .imem_load_size() + .checked_add(cmd_in_buffer_offset) + .map(usize::from_safe_cast) + .ok_or(EINVAL)?; + + let frts_cmd: &mut FrtsCmd = + // SAFETY: we have exclusive access to `dma_object`. + unsafe { transmute_mut(&mut dma_object, frts_cmd_offset) }?; frts_cmd.read_vbios = ReadVbios { ver: 1, @@ -355,8 +362,11 @@ pub(crate) fn new( // Patch signature if needed. let desc = bios.fwsec_image().header()?; let ucode_signed = if desc.signature_count() != 0 { - let sig_base_img = - usize::from_safe_cast(desc.imem_load_size() + desc.pkc_data_offset()); + let sig_base_img = desc + .imem_load_size() + .checked_add(desc.pkc_data_offset()) + .map(usize::from_safe_cast) + .ok_or(EINVAL)?; let desc_sig_versions = u32::from(desc.signature_versions()); let reg_fuse_version = falcon.signature_reg_fuse_version(bar, desc.engine_id_mask(), desc.ucode_id())?; From 4f2609685418cc995ff6a2d558ed62214dec75dc Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 26 Jan 2026 15:23:02 -0500 Subject: [PATCH 011/712] gpu: nova-core: use checked arithmetic in Booter signature parsing Use checked_add() when computing signature offsets from firmware- provided values in signatures_iter(). Without checked arithmetic, overflow could wrap to a small plausible offset that points to entirely wrong data. Reviewed-by: Zhi Wang Signed-off-by: Joel Fernandes Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260126202305.2526618-3-joelagnelf@nvidia.com [acourbot@nvidia.com: remove obvious computation comments.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/booter.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index 86556cee8e67..21cd437a3c95 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -119,14 +119,21 @@ fn signatures_iter(&'a self) -> Result> Some(sig_size) => { let patch_sig = frombytes_at::(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?; - let signatures_start = usize::from_safe_cast(self.hdr.sig_prod_offset + patch_sig); + + let signatures_start = self + .hdr + .sig_prod_offset + .checked_add(patch_sig) + .map(usize::from_safe_cast) + .ok_or(EINVAL)?; + + let signatures_end = signatures_start + .checked_add(usize::from_safe_cast(self.hdr.sig_prod_size)) + .ok_or(EINVAL)?; self.fw // Get signatures range. - .get( - signatures_start - ..signatures_start + usize::from_safe_cast(self.hdr.sig_prod_size), - ) + .get(signatures_start..signatures_end) .ok_or(EINVAL)? .chunks_exact(sig_size.into_safe_cast()) } From 457c70b7dde5c14f940664fdc7f0e1998aff56be Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 26 Jan 2026 15:23:03 -0500 Subject: [PATCH 012/712] gpu: nova-core: use checked arithmetic in frombytes_at helper Use checked_add() when computing the end offset in the frombytes_at() helper function. This function is called with firmware-provided offsets. Reviewed-by: Zhi Wang Signed-off-by: Joel Fernandes Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260126202305.2526618-4-joelagnelf@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/booter.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index 21cd437a3c95..ab374026b1f4 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -43,8 +43,9 @@ /// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at /// `offset` in `slice`. fn frombytes_at(slice: &[u8], offset: usize) -> Result { + let end = offset.checked_add(size_of::()).ok_or(EINVAL)?; slice - .get(offset..offset + size_of::()) + .get(offset..end) .and_then(S::from_bytes_copy) .ok_or(EINVAL) } From 35ae4e58a7c0edd7249a0bcd0d2c151afc185bc2 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 26 Jan 2026 15:23:04 -0500 Subject: [PATCH 013/712] gpu: nova-core: use checked arithmetic in BinFirmware::data Use checked_add() when computing the firmware data end offset in the BinFirmware::data() method. The data_offset and data_size fields come from the BinHdr structure parsed from the firmware file header. Reviewed-by: Zhi Wang Signed-off-by: Joel Fernandes Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260126202305.2526618-5-joelagnelf@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 68779540aa28..4f57a270e142 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -394,8 +394,9 @@ fn new(fw: &'a firmware::Firmware) -> Result { fn data(&self) -> Option<&[u8]> { let fw_start = usize::from_safe_cast(self.hdr.data_offset); let fw_size = usize::from_safe_cast(self.hdr.data_size); + let fw_end = fw_start.checked_add(fw_size)?; - self.fw.get(fw_start..fw_start + fw_size) + self.fw.get(fw_start..fw_end) } } From 4bef417ea46cbc701500b1b92b962586ec6e0900 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 26 Jan 2026 15:23:05 -0500 Subject: [PATCH 014/712] gpu: nova-core: use checked arithmetic in RISC-V firmware parsing Use checked_add() when computing offsets from firmware-provided values in the RISC-V firmware parsing code. These values come from the BinHdr structure parsed from the firmware file header. Reviewed-by: Zhi Wang Signed-off-by: Joel Fernandes Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260126202305.2526618-6-joelagnelf@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/riscv.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs index 4bdd89bd0757..14aad2f0ee8a 100644 --- a/drivers/gpu/nova-core/firmware/riscv.rs +++ b/drivers/gpu/nova-core/firmware/riscv.rs @@ -45,10 +45,11 @@ impl RmRiscvUCodeDesc { /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image. fn new(bin_fw: &BinFirmware<'_>) -> Result { let offset = usize::from_safe_cast(bin_fw.hdr.header_offset); + let end = offset.checked_add(size_of::()).ok_or(EINVAL)?; bin_fw .fw - .get(offset..offset + size_of::()) + .get(offset..end) .and_then(Self::from_bytes_copy) .ok_or(EINVAL) } @@ -78,8 +79,9 @@ pub(crate) fn new(dev: &device::Device, fw: &Firmware) -> Result< let ucode = { let start = usize::from_safe_cast(bin_fw.hdr.data_offset); let len = usize::from_safe_cast(bin_fw.hdr.data_size); + let end = start.checked_add(len).ok_or(EINVAL)?; - DmaObject::from_data(dev, fw.data().get(start..start + len).ok_or(EINVAL)?)? + DmaObject::from_data(dev, fw.data().get(start..end).ok_or(EINVAL)?)? }; Ok(Self { From 9045ae2afc7b7cd51a98d7d773529b56572a4b1b Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Thu, 29 Jan 2026 16:44:59 +0900 Subject: [PATCH 015/712] gpu: nova-core: gsp: fix incorrect advancing of write pointer We should modulo not bitwise-and here. The current code could, for example, set wptr to MSGQ_NUM_PAGES which is not valid. Fixes: 75f6b1de8133 ("gpu: nova-core: gsp: Add GSP command queue bindings and handling") Signed-off-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260129-nova-core-cmdq1-v3-1-2ede85493a27@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 46819a82a51a..f139aad7af3f 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -384,7 +384,7 @@ fn cpu_write_ptr(&self) -> u32 { // Informs the GSP that it can process `elem_count` new pages from the command queue. fn advance_cpu_write_ptr(&mut self, elem_count: u32) { - let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES; + let wptr = self.cpu_write_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES; let gsp_mem = self.0.start_ptr_mut(); // SAFETY: From bbe6831c02d8a381d0858382597b0bea3252fd6a Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Thu, 29 Jan 2026 16:45:00 +0900 Subject: [PATCH 016/712] gpu: nova-core: gsp: clarify comments about invariants and pointer roles Disambiguate a few things in comments in cmdq.rs. Signed-off-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260129-nova-core-cmdq1-v3-2-2ede85493a27@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index f139aad7af3f..0743597779f1 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -161,12 +161,14 @@ struct GspMem { /// Self-mapping page table entries. ptes: PteArray<{ GSP_PAGE_SIZE / size_of::() }>, /// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the - /// write and read pointers that the CPU updates. + /// write and read pointers that the CPU updates. This means that the read pointer here is an + /// index into the GSP queue. /// /// This member is read-only for the GSP. cpuq: Msgq, /// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the - /// write and read pointers that the GSP updates. + /// write and read pointers that the GSP updates. This means that the read pointer here is an + /// index into the CPU queue. /// /// This member is read-only for the driver. gspq: Msgq, @@ -222,7 +224,7 @@ fn new(dev: &device::Device) -> Result { // - We will only access the driver-owned part of the shared memory. // - Per the safety statement of the function, no concurrent access will be performed. let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0]; - // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `<= MSGQ_NUM_PAGES`. + // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`. let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx); if rx <= tx { @@ -257,7 +259,7 @@ fn new(dev: &device::Device) -> Result { // - We will only access the driver-owned part of the shared memory. // - Per the safety statement of the function, no concurrent access will be performed. let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0]; - // PANIC: per the invariant of `cpu_read_ptr`, `xx` is `<= MSGQ_NUM_PAGES`. + // PANIC: per the invariant of `cpu_read_ptr`, `rx` is `< MSGQ_NUM_PAGES`. let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx); match tx.cmp(&rx) { @@ -315,7 +317,7 @@ fn allocate_command(&mut self, size: usize) -> Result> { // // # Invariants // - // - The returned value is between `0` and `MSGQ_NUM_PAGES`. + // - The returned value is within `0..MSGQ_NUM_PAGES`. fn gsp_write_ptr(&self) -> u32 { let gsp_mem = self.0.start_ptr(); @@ -329,7 +331,7 @@ fn gsp_write_ptr(&self) -> u32 { // // # Invariants // - // - The returned value is between `0` and `MSGQ_NUM_PAGES`. + // - The returned value is within `0..MSGQ_NUM_PAGES`. fn gsp_read_ptr(&self) -> u32 { let gsp_mem = self.0.start_ptr(); @@ -343,7 +345,7 @@ fn gsp_read_ptr(&self) -> u32 { // // # Invariants // - // - The returned value is between `0` and `MSGQ_NUM_PAGES`. + // - The returned value is within `0..MSGQ_NUM_PAGES`. fn cpu_read_ptr(&self) -> u32 { let gsp_mem = self.0.start_ptr(); @@ -372,7 +374,7 @@ fn advance_cpu_read_ptr(&mut self, elem_count: u32) { // // # Invariants // - // - The returned value is between `0` and `MSGQ_NUM_PAGES`. + // - The returned value is within `0..MSGQ_NUM_PAGES`. fn cpu_write_ptr(&self) -> u32 { let gsp_mem = self.0.start_ptr(); From f6f072d8ef06ff5d29a6bb1bade3da29a1aafeec Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Thu, 29 Jan 2026 16:45:01 +0900 Subject: [PATCH 017/712] gpu: nova-core: gsp: use empty slices instead of [0..0] ranges The current code unnecessarily uses, for example, &before_rx[0..0] to return an empty slice. Instead, just use an empty slice. Signed-off-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260129-nova-core-cmdq1-v3-3-2ede85493a27@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 0743597779f1..b88ff8ebc098 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -232,7 +232,7 @@ fn new(dev: &device::Device) -> Result { // to `rx`, minus one unit, belongs to the driver. if rx == 0 { let last = after_tx.len() - 1; - (&mut after_tx[..last], &mut before_tx[0..0]) + (&mut after_tx[..last], &mut []) } else { (after_tx, &mut before_tx[..rx]) } @@ -241,7 +241,7 @@ fn new(dev: &device::Device) -> Result { // // PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are // `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`. - (after_tx.split_at_mut(rx - tx).0, &mut before_tx[0..0]) + (after_tx.split_at_mut(rx - tx).0, &mut []) } } @@ -263,8 +263,8 @@ fn new(dev: &device::Device) -> Result { let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx); match tx.cmp(&rx) { - cmp::Ordering::Equal => (&after_rx[0..0], &after_rx[0..0]), - cmp::Ordering::Greater => (&after_rx[..tx], &before_rx[0..0]), + cmp::Ordering::Equal => (&[], &[]), + cmp::Ordering::Greater => (&after_rx[..tx], &[]), cmp::Ordering::Less => (after_rx, &before_rx[..tx]), } } From f64caf673cb5add9ac2065609a52049e2317c498 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Thu, 29 Jan 2026 16:45:02 +0900 Subject: [PATCH 018/712] gpu: nova-core: gsp: fix improper handling of empty slot in cmdq The current code hands out buffers that go all the way up to and including `rx - 1`, but we need to maintain an empty slot to prevent the ring buffer from wrapping around into having 'tx == rx', which means empty. Also add more rigorous no-panic proofs. Fixes: 75f6b1de8133 ("gpu: nova-core: gsp: Add GSP command queue bindings and handling") Signed-off-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260129-nova-core-cmdq1-v3-4-2ede85493a27@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 34 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index b88ff8ebc098..333bf0125d74 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -227,21 +227,27 @@ fn new(dev: &device::Device) -> Result { // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`. let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx); - if rx <= tx { - // The area from `tx` up to the end of the ring, and from the beginning of the ring up - // to `rx`, minus one unit, belongs to the driver. - if rx == 0 { - let last = after_tx.len() - 1; - (&mut after_tx[..last], &mut []) - } else { - (after_tx, &mut before_tx[..rx]) - } + // The area starting at `tx` and ending at `rx - 2` modulo MSGQ_NUM_PAGES, inclusive, + // belongs to the driver for writing. + + if rx == 0 { + // Since `rx` is zero, leave an empty slot at end of the buffer. + let last = after_tx.len() - 1; + (&mut after_tx[..last], &mut []) + } else if rx <= tx { + // The area is discontiguous and we leave an empty slot before `rx`. + // PANIC: + // - The index `rx - 1` is non-negative because `rx != 0` in this branch. + // - The index does not exceed `before_tx.len()` (which equals `tx`) because + // `rx <= tx` in this branch. + (after_tx, &mut before_tx[..(rx - 1)]) } else { - // The area from `tx` to `rx`, minus one unit, belongs to the driver. - // - // PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are - // `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`. - (after_tx.split_at_mut(rx - tx).0, &mut []) + // The area is contiguous and we leave an empty slot before `rx`. + // PANIC: + // - The index `rx - tx - 1` is non-negative because `rx > tx` in this branch. + // - The index does not exceed `after_tx.len()` (which is `MSGQ_NUM_PAGES - tx`) + // because `rx < MSGQ_NUM_PAGES` by the `gsp_read_ptr` invariant. + (&mut after_tx[..(rx - tx - 1)], &mut []) } } From 5cdbed3ad782700d6381bf5901e3f61c4d8b28bc Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Thu, 29 Jan 2026 16:45:03 +0900 Subject: [PATCH 019/712] gpu: nova-core: gsp: fix improper indexing in driver_read_area The current code indexes into `after_rx` using `tx` which is an index for the whole buffer, not the split buffer `after_rx`. Also add more rigorous no-panic proofs. Fixes: 75f6b1de8133 ("gpu: nova-core: gsp: Add GSP command queue bindings and handling") Signed-off-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260129-nova-core-cmdq1-v3-5-2ede85493a27@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 333bf0125d74..16895f5281b7 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 use core::{ - cmp, mem, sync::atomic::{ fence, @@ -265,13 +264,19 @@ fn new(dev: &device::Device) -> Result { // - We will only access the driver-owned part of the shared memory. // - Per the safety statement of the function, no concurrent access will be performed. let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0]; - // PANIC: per the invariant of `cpu_read_ptr`, `rx` is `< MSGQ_NUM_PAGES`. - let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx); + let data = &gsp_mem.gspq.msgq.data; - match tx.cmp(&rx) { - cmp::Ordering::Equal => (&[], &[]), - cmp::Ordering::Greater => (&after_rx[..tx], &[]), - cmp::Ordering::Less => (after_rx, &before_rx[..tx]), + // The area starting at `rx` and ending at `tx - 1` modulo MSGQ_NUM_PAGES, inclusive, + // belongs to the driver for reading. + // PANIC: + // - per the invariant of `cpu_read_ptr`, `rx < MSGQ_NUM_PAGES` + // - per the invariant of `gsp_write_ptr`, `tx < MSGQ_NUM_PAGES` + if rx <= tx { + // The area is contiguous. + (&data[rx..tx], &[]) + } else { + // The area is discontiguous. + (&data[rx..], &data[..tx]) } } From b45b9f2668b723f8117a3585d75d01e93281aa38 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Feb 2026 11:45:49 +0900 Subject: [PATCH 020/712] gpu: nova-core: gsp: warn if data remains after processing a message Not processing the whole data from a received message is a strong indicator of a bug - emit a warning when such cases are detected. Reviewed-by: Lyude Paul Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260217-nova-misc-v3-1-b4e2d45eafbc@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 16895f5281b7..156f1fc91d31 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -674,7 +674,17 @@ pub(crate) fn receive_msg(&mut self, timeout: Delta) -> Resul let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?; let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]); - M::read(cmd, &mut sbuffer).map_err(|e| e.into()) + M::read(cmd, &mut sbuffer) + .map_err(|e| e.into()) + .inspect(|_| { + if !sbuffer.is_empty() { + dev_warn!( + &self.dev, + "GSP message {:?} has unprocessed data\n", + function + ); + } + }) } else { Err(ERANGE) }; From 3614290d75a4853a74ac501a64f1a4916c99bfe6 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Feb 2026 11:45:50 +0900 Subject: [PATCH 021/712] gpu: nova-core: gsp: remove unnecessary Display impls We only ever display these in debug context, for which the automatically derived `Debug` impls work just fine - so use them and remove these boilerplate-looking implementations. Reviewed-by: Lyude Paul Reviewed-by: Alistair Popple Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260217-nova-misc-v3-2-b4e2d45eafbc@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 2 +- drivers/gpu/nova-core/gsp/fw.rs | 54 ------------------------------- 2 files changed, 1 insertion(+), 55 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 156f1fc91d31..87dbbd6d1be9 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -544,7 +544,7 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result dev_dbg!( &self.dev, - "GSP RPC: send: seq# {}, function={}, length=0x{:x}\n", + "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n", self.seq, M::FUNCTION, dst.header.length(), diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 83ff91614e36..3c26b165038e 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -10,7 +10,6 @@ use kernel::{ dma::CoherentAllocation, - fmt, prelude::*, ptr::{ Alignable, @@ -223,43 +222,6 @@ pub(crate) enum MsgFunction { UcodeLibOsPrint = bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT, } -impl fmt::Display for MsgFunction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - // Common function codes - MsgFunction::Nop => write!(f, "NOP"), - MsgFunction::SetGuestSystemInfo => write!(f, "SET_GUEST_SYSTEM_INFO"), - MsgFunction::AllocRoot => write!(f, "ALLOC_ROOT"), - MsgFunction::AllocDevice => write!(f, "ALLOC_DEVICE"), - MsgFunction::AllocMemory => write!(f, "ALLOC_MEMORY"), - MsgFunction::AllocCtxDma => write!(f, "ALLOC_CTX_DMA"), - MsgFunction::AllocChannelDma => write!(f, "ALLOC_CHANNEL_DMA"), - MsgFunction::MapMemory => write!(f, "MAP_MEMORY"), - MsgFunction::BindCtxDma => write!(f, "BIND_CTX_DMA"), - MsgFunction::AllocObject => write!(f, "ALLOC_OBJECT"), - MsgFunction::Free => write!(f, "FREE"), - MsgFunction::Log => write!(f, "LOG"), - MsgFunction::GetGspStaticInfo => write!(f, "GET_GSP_STATIC_INFO"), - MsgFunction::SetRegistry => write!(f, "SET_REGISTRY"), - MsgFunction::GspSetSystemInfo => write!(f, "GSP_SET_SYSTEM_INFO"), - MsgFunction::GspInitPostObjGpu => write!(f, "GSP_INIT_POST_OBJGPU"), - MsgFunction::GspRmControl => write!(f, "GSP_RM_CONTROL"), - MsgFunction::GetStaticInfo => write!(f, "GET_STATIC_INFO"), - - // Event codes - MsgFunction::GspInitDone => write!(f, "INIT_DONE"), - MsgFunction::GspRunCpuSequencer => write!(f, "RUN_CPU_SEQUENCER"), - MsgFunction::PostEvent => write!(f, "POST_EVENT"), - MsgFunction::RcTriggered => write!(f, "RC_TRIGGERED"), - MsgFunction::MmuFaultQueued => write!(f, "MMU_FAULT_QUEUED"), - MsgFunction::OsErrorLog => write!(f, "OS_ERROR_LOG"), - MsgFunction::GspPostNoCat => write!(f, "NOCAT"), - MsgFunction::GspLockdownNotice => write!(f, "LOCKDOWN_NOTICE"), - MsgFunction::UcodeLibOsPrint => write!(f, "LIBOS_PRINT"), - } - } -} - impl TryFrom for MsgFunction { type Error = kernel::error::Error; @@ -330,22 +292,6 @@ pub(crate) enum SeqBufOpcode { RegWrite = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE, } -impl fmt::Display for SeqBufOpcode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SeqBufOpcode::CoreReset => write!(f, "CORE_RESET"), - SeqBufOpcode::CoreResume => write!(f, "CORE_RESUME"), - SeqBufOpcode::CoreStart => write!(f, "CORE_START"), - SeqBufOpcode::CoreWaitForHalt => write!(f, "CORE_WAIT_FOR_HALT"), - SeqBufOpcode::DelayUs => write!(f, "DELAY_US"), - SeqBufOpcode::RegModify => write!(f, "REG_MODIFY"), - SeqBufOpcode::RegPoll => write!(f, "REG_POLL"), - SeqBufOpcode::RegStore => write!(f, "REG_STORE"), - SeqBufOpcode::RegWrite => write!(f, "REG_WRITE"), - } - } -} - impl TryFrom for SeqBufOpcode { type Error = kernel::error::Error; From 4503e61a625c1afff6d3f3e2a2e357a4007cc5c0 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Feb 2026 11:45:51 +0900 Subject: [PATCH 022/712] gpu: nova-core: gsp: simplify sequencer opcode parsing The opcodes are already the right type in the C union, so we can use them directly instead of converting them to a byte stream and back again using `FromBytes`. Reviewed-by: Lyude Paul Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260217-nova-misc-v3-3-b4e2d45eafbc@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/fw.rs | 40 +++++---------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 3c26b165038e..624f5670ed50 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -472,13 +472,7 @@ pub(crate) fn reg_write_payload(&self) -> Result { return Err(EINVAL); } // SAFETY: Opcode is verified to be `RegWrite`, so union contains valid `RegWritePayload`. - let payload_bytes = unsafe { - core::slice::from_raw_parts( - core::ptr::addr_of!(self.0.payload.regWrite).cast::(), - core::mem::size_of::(), - ) - }; - Ok(*RegWritePayload::from_bytes(payload_bytes).ok_or(EINVAL)?) + Ok(RegWritePayload(unsafe { self.0.payload.regWrite })) } /// Returns the register modify payload by value. @@ -489,13 +483,7 @@ pub(crate) fn reg_modify_payload(&self) -> Result { return Err(EINVAL); } // SAFETY: Opcode is verified to be `RegModify`, so union contains valid `RegModifyPayload`. - let payload_bytes = unsafe { - core::slice::from_raw_parts( - core::ptr::addr_of!(self.0.payload.regModify).cast::(), - core::mem::size_of::(), - ) - }; - Ok(*RegModifyPayload::from_bytes(payload_bytes).ok_or(EINVAL)?) + Ok(RegModifyPayload(unsafe { self.0.payload.regModify })) } /// Returns the register poll payload by value. @@ -506,13 +494,7 @@ pub(crate) fn reg_poll_payload(&self) -> Result { return Err(EINVAL); } // SAFETY: Opcode is verified to be `RegPoll`, so union contains valid `RegPollPayload`. - let payload_bytes = unsafe { - core::slice::from_raw_parts( - core::ptr::addr_of!(self.0.payload.regPoll).cast::(), - core::mem::size_of::(), - ) - }; - Ok(*RegPollPayload::from_bytes(payload_bytes).ok_or(EINVAL)?) + Ok(RegPollPayload(unsafe { self.0.payload.regPoll })) } /// Returns the delay payload by value. @@ -523,13 +505,7 @@ pub(crate) fn delay_us_payload(&self) -> Result { return Err(EINVAL); } // SAFETY: Opcode is verified to be `DelayUs`, so union contains valid `DelayUsPayload`. - let payload_bytes = unsafe { - core::slice::from_raw_parts( - core::ptr::addr_of!(self.0.payload.delayUs).cast::(), - core::mem::size_of::(), - ) - }; - Ok(*DelayUsPayload::from_bytes(payload_bytes).ok_or(EINVAL)?) + Ok(DelayUsPayload(unsafe { self.0.payload.delayUs })) } /// Returns the register store payload by value. @@ -540,13 +516,7 @@ pub(crate) fn reg_store_payload(&self) -> Result { return Err(EINVAL); } // SAFETY: Opcode is verified to be `RegStore`, so union contains valid `RegStorePayload`. - let payload_bytes = unsafe { - core::slice::from_raw_parts( - core::ptr::addr_of!(self.0.payload.regStore).cast::(), - core::mem::size_of::(), - ) - }; - Ok(*RegStorePayload::from_bytes(payload_bytes).ok_or(EINVAL)?) + Ok(RegStorePayload(unsafe { self.0.payload.regStore })) } } From 953278c19d3496b8b0848d60b80485db42782d72 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Feb 2026 11:45:52 +0900 Subject: [PATCH 023/712] gpu: nova-core: gsp: remove unneeded sequencer trait The `GspSeqCmdRunner` trait is never used as we never call the `run` methods from generic code. Remove it. Reviewed-by: Lyude Paul Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260217-nova-misc-v3-4-b4e2d45eafbc@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/sequencer.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index e415a2aa3203..9278ffd5216d 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -144,12 +144,7 @@ pub(crate) struct GspSequencer<'a> { dev: ARef, } -/// Trait for running sequencer commands. -pub(crate) trait GspSeqCmdRunner { - fn run(&self, sequencer: &GspSequencer<'_>) -> Result; -} - -impl GspSeqCmdRunner for fw::RegWritePayload { +impl fw::RegWritePayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); @@ -157,7 +152,7 @@ fn run(&self, sequencer: &GspSequencer<'_>) -> Result { } } -impl GspSeqCmdRunner for fw::RegModifyPayload { +impl fw::RegModifyPayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); @@ -169,7 +164,7 @@ fn run(&self, sequencer: &GspSequencer<'_>) -> Result { } } -impl GspSeqCmdRunner for fw::RegPollPayload { +impl fw::RegPollPayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); @@ -194,14 +189,14 @@ fn run(&self, sequencer: &GspSequencer<'_>) -> Result { } } -impl GspSeqCmdRunner for fw::DelayUsPayload { +impl fw::DelayUsPayload { fn run(&self, _sequencer: &GspSequencer<'_>) -> Result { fsleep(Delta::from_micros(i64::from(self.val()))); Ok(()) } } -impl GspSeqCmdRunner for fw::RegStorePayload { +impl fw::RegStorePayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); @@ -209,7 +204,7 @@ fn run(&self, sequencer: &GspSequencer<'_>) -> Result { } } -impl GspSeqCmdRunner for GspSeqCmd { +impl GspSeqCmd { fn run(&self, seq: &GspSequencer<'_>) -> Result { match self { GspSeqCmd::RegWrite(cmd) => cmd.run(seq), From f86226d3c67b72ae1908f82776dcc7f259e42ff6 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Feb 2026 11:45:53 +0900 Subject: [PATCH 024/712] gpu: nova-core: gsp: derive `Debug` on more sequencer types Being able to print these is useful when debugging the sequencer. Reviewed-by: Lyude Paul Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260217-nova-misc-v3-5-b4e2d45eafbc@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/fw.rs | 10 +++++----- drivers/gpu/nova-core/gsp/sequencer.rs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 624f5670ed50..f1797e1f0d9d 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -330,7 +330,7 @@ fn from(value: SeqBufOpcode) -> Self { /// Wrapper for GSP sequencer register write payload. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) struct RegWritePayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_WRITE); impl RegWritePayload { @@ -353,7 +353,7 @@ unsafe impl AsBytes for RegWritePayload {} /// Wrapper for GSP sequencer register modify payload. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) struct RegModifyPayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY); impl RegModifyPayload { @@ -381,7 +381,7 @@ unsafe impl AsBytes for RegModifyPayload {} /// Wrapper for GSP sequencer register poll payload. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) struct RegPollPayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_POLL); impl RegPollPayload { @@ -414,7 +414,7 @@ unsafe impl AsBytes for RegPollPayload {} /// Wrapper for GSP sequencer delay payload. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) struct DelayUsPayload(bindings::GSP_SEQ_BUF_PAYLOAD_DELAY_US); impl DelayUsPayload { @@ -432,7 +432,7 @@ unsafe impl AsBytes for DelayUsPayload {} /// Wrapper for GSP sequencer register store payload. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) struct RegStorePayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_STORE); impl RegStorePayload { diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index 9278ffd5216d..0cfbedc47fcf 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -67,6 +67,7 @@ fn read( /// GSP Sequencer Command types with payload data. /// Commands have an opcode and an opcode-dependent struct. #[allow(clippy::enum_variant_names)] +#[derive(Debug)] pub(crate) enum GspSeqCmd { RegWrite(fw::RegWritePayload), RegModify(fw::RegModifyPayload), From 8e10d462e66db8b4702a8bd40642b214599270ba Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Feb 2026 11:45:54 +0900 Subject: [PATCH 025/712] gpu: nova-core: gsp: derive Zeroable for GspStaticConfigInfo We can now derive `Zeroable` on tuple structs, so do this instead of providing our own implementation. Reviewed-by: Lyude Paul Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260217-nova-misc-v3-6-b4e2d45eafbc@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/fw/commands.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs index 21be44199693..67f44421fcc3 100644 --- a/drivers/gpu/nova-core/gsp/fw/commands.rs +++ b/drivers/gpu/nova-core/gsp/fw/commands.rs @@ -107,6 +107,7 @@ unsafe impl FromBytes for PackedRegistryTable {} /// Payload of the `GetGspStaticInfo` command and message. #[repr(transparent)] +#[derive(Zeroable)] pub(crate) struct GspStaticConfigInfo(bindings::GspStaticConfigInfo_t); impl GspStaticConfigInfo { @@ -122,7 +123,3 @@ unsafe impl AsBytes for GspStaticConfigInfo {} // SAFETY: This struct only contains integer types for which all bit patterns // are valid. unsafe impl FromBytes for GspStaticConfigInfo {} - -// SAFETY: This struct only contains integer types and fixed-size arrays for which -// all bit patterns are valid. -unsafe impl Zeroable for GspStaticConfigInfo {} From 4a49fe23e357b48845e31fe9c28a802c05458198 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Feb 2026 11:45:55 +0900 Subject: [PATCH 026/712] gpu: nova-core: use core library's CStr instead of kernel one The kernel's own CStr type has been replaced by the one in the core library, and is now an alias to the latter. Change our imports to directly reference the actual type. Reviewed-by: Lyude Paul Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260217-nova-misc-v3-7-b4e2d45eafbc@nvidia.com [acourbot@nvidia.com: remove unneeded imports reorganization in firmware/gsp.rs] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 2 +- drivers/gpu/nova-core/nova_core.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 4f57a270e142..815e8000bf81 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -425,7 +425,7 @@ const fn make_entry_chipset(self, chipset: &str) -> Self { } pub(crate) const fn create( - module_name: &'static kernel::str::CStr, + module_name: &'static core::ffi::CStr, ) -> firmware::ModInfoBuilder { let mut this = Self(firmware::ModInfoBuilder::new(module_name)); let mut i = 0; diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index c1121e7c64c5..b5caf1044697 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -18,7 +18,7 @@ mod sbuffer; mod vbios; -pub(crate) const MODULE_NAME: &kernel::str::CStr = ::NAME; +pub(crate) const MODULE_NAME: &core::ffi::CStr = ::NAME; kernel::module_pci_driver! { type: driver::NovaCore, From 16fdabe143fce2cbf89139677728e17e21b46c28 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Thu, 5 Feb 2026 09:28:40 +0000 Subject: [PATCH 027/712] gpio: Fix resource leaks on errors in gpiochip_add_data_with_key() Since commit aab5c6f20023 ("gpio: set device type for GPIO chips"), `gdev->dev.release` is unset. As a result, the reference count to `gdev->dev` isn't dropped on the error handling paths. Drop the reference on errors. Also reorder the instructions to make the error handling simpler. Now gpiochip_add_data_with_key() roughly looks like: >>> Some memory allocation. Go to ERR ZONE 1 on errors. >>> device_initialize(). gpiodev_release() takes over the responsibility for freeing the resources of `gdev->dev`. The subsequent error handling paths shouldn't go through ERR ZONE 1 again which leads to double free. >>> Some initialization mainly on `gdev`. >>> The rest of initialization. Go to ERR ZONE 2 on errors. >>> Chip registration success and exit. >>> ERR ZONE 2. gpio_device_put() and exit. >>> ERR ZONE 1. Cc: stable@vger.kernel.org Fixes: aab5c6f20023 ("gpio: set device type for GPIO chips") Reviewed-by: Linus Walleij Signed-off-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260205092840.2574840-1-tzungbi@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 123 ++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 86a171e96b0e..f77d5121a8a8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -892,13 +892,15 @@ static const struct device_type gpio_dev_type = { #define gcdev_unregister(gdev) device_del(&(gdev)->dev) #endif +/* + * An initial reference count has been held in gpiochip_add_data_with_key(). + * The caller should drop the reference via gpio_device_put() on errors. + */ static int gpiochip_setup_dev(struct gpio_device *gdev) { struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev); int ret; - device_initialize(&gdev->dev); - /* * If fwnode doesn't belong to another device, it's safe to clear its * initialized flag. @@ -964,9 +966,11 @@ static void gpiochip_setup_devs(void) list_for_each_entry_srcu(gdev, &gpio_devices, list, srcu_read_lock_held(&gpio_devices_srcu)) { ret = gpiochip_setup_dev(gdev); - if (ret) + if (ret) { + gpio_device_put(gdev); dev_err(&gdev->dev, "Failed to initialize gpio device (%d)\n", ret); + } } } @@ -1047,33 +1051,65 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret; - /* - * First: allocate and populate the internal stat container, and - * set up the struct device. - */ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); if (!gdev) return -ENOMEM; - - gdev->dev.type = &gpio_dev_type; - gdev->dev.bus = &gpio_bus_type; - gdev->dev.parent = gc->parent; - rcu_assign_pointer(gdev->chip, gc); - gc->gpiodev = gdev; gpiochip_set_data(gc, data); - device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); - ret = ida_alloc(&gpio_ida, GFP_KERNEL); if (ret < 0) goto err_free_gdev; gdev->id = ret; - ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); + ret = init_srcu_struct(&gdev->srcu); if (ret) goto err_free_ida; + rcu_assign_pointer(gdev->chip, gc); + ret = init_srcu_struct(&gdev->desc_srcu); + if (ret) + goto err_cleanup_gdev_srcu; + + ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); + if (ret) + goto err_cleanup_desc_srcu; + + device_initialize(&gdev->dev); + /* + * After this point any allocated resources to `gdev` will be + * free():ed by gpiodev_release(). If you add new resources + * then make sure they get free():ed there. + */ + gdev->dev.type = &gpio_dev_type; + gdev->dev.bus = &gpio_bus_type; + gdev->dev.parent = gc->parent; + device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); + + ret = gpiochip_get_ngpios(gc, &gdev->dev); + if (ret) + goto err_put_device; + gdev->ngpio = gc->ngpio; + + gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL); + if (!gdev->descs) { + ret = -ENOMEM; + goto err_put_device; + } + + gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL); + if (!gdev->label) { + ret = -ENOMEM; + goto err_put_device; + } + + gdev->can_sleep = gc->can_sleep; + rwlock_init(&gdev->line_state_lock); + RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); +#ifdef CONFIG_PINCTRL + INIT_LIST_HEAD(&gdev->pin_ranges); +#endif if (gc->parent && gc->parent->driver) gdev->owner = gc->parent->driver->owner; else if (gc->owner) @@ -1082,37 +1118,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, else gdev->owner = THIS_MODULE; - ret = gpiochip_get_ngpios(gc, &gdev->dev); - if (ret) - goto err_free_dev_name; - - gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL); - if (!gdev->descs) { - ret = -ENOMEM; - goto err_free_dev_name; - } - - gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL); - if (!gdev->label) { - ret = -ENOMEM; - goto err_free_descs; - } - - gdev->ngpio = gc->ngpio; - gdev->can_sleep = gc->can_sleep; - - rwlock_init(&gdev->line_state_lock); - RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); - BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); - - ret = init_srcu_struct(&gdev->srcu); - if (ret) - goto err_free_label; - - ret = init_srcu_struct(&gdev->desc_srcu); - if (ret) - goto err_cleanup_gdev_srcu; - scoped_guard(mutex, &gpio_devices_lock) { /* * TODO: this allocates a Linux GPIO number base in the global @@ -1127,7 +1132,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (base < 0) { ret = base; base = 0; - goto err_cleanup_desc_srcu; + goto err_put_device; } /* @@ -1147,14 +1152,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiodev_add_to_list_unlocked(gdev); if (ret) { gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n"); - goto err_cleanup_desc_srcu; + goto err_put_device; } } -#ifdef CONFIG_PINCTRL - INIT_LIST_HEAD(&gdev->pin_ranges); -#endif - if (gc->names) gpiochip_set_desc_names(gc); @@ -1248,25 +1249,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, scoped_guard(mutex, &gpio_devices_lock) list_del_rcu(&gdev->list); synchronize_srcu(&gpio_devices_srcu); - if (gdev->dev.release) { - /* release() has been registered by gpiochip_setup_dev() */ - gpio_device_put(gdev); - goto err_print_message; - } +err_put_device: + gpio_device_put(gdev); + goto err_print_message; + err_cleanup_desc_srcu: cleanup_srcu_struct(&gdev->desc_srcu); err_cleanup_gdev_srcu: cleanup_srcu_struct(&gdev->srcu); -err_free_label: - kfree_const(gdev->label); -err_free_descs: - kfree(gdev->descs); -err_free_dev_name: - kfree(dev_name(&gdev->dev)); err_free_ida: ida_free(&gpio_ida, gdev->id); err_free_gdev: kfree(gdev); + err_print_message: /* failures here can mean systems won't boot... */ if (ret != -EPROBE_DEFER) { From 48a5c36577ebe0144f8ede70e59b59ea18b75089 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 28 Feb 2026 01:48:19 +0800 Subject: [PATCH 028/712] iio: adc: ti-ads1119: Fix unbalanced pm reference count in ds1119_single_conversion() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ads1119_single_conversion(), if pm_runtime_resume_and_get() fails, the code jumps to the pdown label, which calls pm_runtime_put_autosuspend(). Since pm_runtime_resume_and_get() automatically decrements the usage counter on failure, the subsequent call to pm_runtime_put_autosuspend() causes an unbalanced reference counter. Fixes: a9306887eba4 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Felix Gu Reviewed-by: João Paulo Gonçalves Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index c9cedc59cdcd..4454f28b2b58 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -274,7 +274,7 @@ static int ads1119_single_conversion(struct ads1119_state *st, ret = pm_runtime_resume_and_get(dev); if (ret) - goto pdown; + return ret; ret = ads1119_configure_channel(st, mux, gain, datarate); if (ret) From c53bca092486809d266b5921b9e6f9df2688fc26 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 27 Feb 2026 21:54:29 +0800 Subject: [PATCH 029/712] iio: pressure: abp2030pa: Remove IRQF_ONESHOT from devm_request_irq() Since commit aef30c8d569c ("genirq: Warn about using IRQF_ONESHOT without a threaded handler"), the IRQ core checks IRQF_ONESHOT flag in IRQ request and gives a warning if there is no threaded handler. Remove IRQF_ONESHOT from devm_request_irq(). Fixes: 47d323ce1e89 ("iio: pressure: add Honeywell ABP2 driver") Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: Felix Gu Tested-by: Petre Rodan Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/abp2030pa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c index 4ca056a73cef..b44f1bf4c633 100644 --- a/drivers/iio/pressure/abp2030pa.c +++ b/drivers/iio/pressure/abp2030pa.c @@ -520,7 +520,7 @@ int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq) data->p_offset = div_s64(odelta * data->pmin, pdelta) - data->outmin; if (data->irq > 0) { - ret = devm_request_irq(dev, irq, abp2_eoc_handler, IRQF_ONESHOT, + ret = devm_request_irq(dev, irq, abp2_eoc_handler, 0, dev_name(dev), data); if (ret) return ret; From 0206dd36418c104c0b3dea4ed7047e21eccb30b0 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 15:33:30 +0200 Subject: [PATCH 030/712] iio: adc: ade9000: move mutex init before IRQ registration Move devm_mutex_init() before ade9000_request_irq() calls so that st->lock is initialized before any handler that depends on it can run. Fixes: 81de7b4619fc ("iio: adc: add ade9000 support") Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index db085dc5e526..c62c6480dd0a 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1706,6 +1706,10 @@ static int ade9000_probe(struct spi_device *spi) init_completion(&st->reset_completion); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev); if (ret) return ret; @@ -1718,10 +1722,6 @@ static int ade9000_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_mutex_init(dev, &st->lock); - if (ret) - return ret; - /* External CMOS clock input (optional - crystal can be used instead) */ st->clkin = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(st->clkin)) From bd66aa1c8b8cabf459064a46d3430a5ec5138418 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 14:43:05 +0200 Subject: [PATCH 031/712] iio: accel: adxl380: fix FIFO watermark bit 8 always written as 0 FIELD_PREP(BIT(0), fifo_samples & BIT(8)) produces either 0 or 256, and since FIELD_PREP masks to bit 0, 256 & 1 evaluates to 0. Use !! to convert the result to a proper 0-or-1 value. Fixes: df36de13677a ("iio: accel: add ADXL380 driver") Signed-off-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl380.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index 8fab2fdbe147..a51d1d61c412 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -877,7 +877,7 @@ static int adxl380_set_fifo_samples(struct adxl380_state *st) ret = regmap_update_bits(st->regmap, ADXL380_FIFO_CONFIG_0_REG, ADXL380_FIFO_SAMPLES_8_MSK, FIELD_PREP(ADXL380_FIFO_SAMPLES_8_MSK, - (fifo_samples & BIT(8)))); + !!(fifo_samples & BIT(8)))); if (ret) return ret; From 86133fb1ec36b2f5cec29d71fbae84877c3a1358 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Thu, 26 Feb 2026 18:07:02 +0400 Subject: [PATCH 032/712] iio: adc: ade9000: fix wrong register in CALIBBIAS case for active power The switch statement in ade9000_write_raw() attempts to match chan->address against ADE9000_REG_AWATTOS (0x00F) to dispatch the calibration offset write for active power channels. However, chan->address is set via ADE9000_ADDR_ADJUST(ADE9000_REG_AWATT, num), so after masking the phase bits, tmp holds ADE9000_REG_AWATT (0x210), which never matches 0x00F. As a result, writing IIO_CHAN_INFO_CALIBBIAS for IIO_POWER always falls through to the default case and returns -EINVAL, making active power offset calibration silently broken. Fix this by matching against ADE9000_REG_AWATT instead, which is the actual base address stored in chan->address for watt channels. Reference:ADE9000 datasheet (Rev. B), AWATTOS is the offset correction register at 0x00F (p. 44), while AWATT is the total active power register at 0x210 (p. 48). Fixes: 81de7b4619fc ("iio: adc: add ade9000 support") Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index c62c6480dd0a..945a159e5de6 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1123,7 +1123,7 @@ static int ade9000_write_raw(struct iio_dev *indio_dev, tmp &= ~ADE9000_PHASE_C_POS_BIT; switch (tmp) { - case ADE9000_REG_AWATTOS: + case ADE9000_REG_AWATT: return regmap_write(st->regmap, ADE9000_ADDR_ADJUST(ADE9000_REG_AWATTOS, chan->channel), val); From 57b207e38d414a27fda9fff638a0d3e7ef16b917 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Tue, 24 Feb 2026 17:23:55 +0400 Subject: [PATCH 033/712] iio: adc: ade9000: fix wrong return type in streaming push The else branch of ade9000_iio_push_streaming() incorrectly returns IRQ_HANDLED on regmap_write failure. This function returns int (0 on success, negative errno on failure), so IRQ_HANDLED (1) would be misinterpreted as a non-error by callers. Return ret instead, consistent with every other error path in the function. Fixes: 81de7b4619fc ("iio: adc: add ade9000 support") Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index 945a159e5de6..1abbfdfcd554 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -787,7 +787,7 @@ static int ade9000_iio_push_streaming(struct iio_dev *indio_dev) ADE9000_MIDDLE_PAGE_BIT); if (ret) { dev_err_ratelimited(dev, "IRQ0 WFB write fail"); - return IRQ_HANDLED; + return ret; } ade9000_configure_scan(indio_dev, ADE9000_REG_WF_BUFF); From b403981da8a26f57f07859a9704392b1183e2a6e Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 25 Feb 2026 22:48:57 +0800 Subject: [PATCH 034/712] iio: dac: mcp47feb02: Fix mutex used before initialization The mcp47feb02_parse_fw() function uses data->lock, but the mutex was initialized after this function in probe path. Since mcp47feb02_parse_fw() is only called from probe(), remove the lock. Fixes: bf394cc80369 ("iio: dac: adding support for Microchip MCP47FEB02") Signed-off-by: Felix Gu Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp47feb02.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c index b218f0c3a0bd..08fb85359697 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02.c @@ -955,8 +955,6 @@ static int mcp47feb02_parse_fw(struct iio_dev *indio_dev, u32 num_channels; u8 chan_idx = 0; - guard(mutex)(&data->lock); - num_channels = device_get_child_node_count(dev); if (num_channels > chip_features->phys_channels) return dev_err_probe(dev, -EINVAL, "More channels than the chip supports\n"); From 630748afa7030b272b7bee5df857e7bcf132ed51 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Wed, 25 Feb 2026 11:06:00 +0100 Subject: [PATCH 035/712] iio: imu: st_lsm6dsx: Set FIFO ODR for accelerometer and gyroscope only The st_lsm6dsx_set_fifo_odr() function, which is called when enabling and disabling the hardware FIFO, checks the contents of the hw->settings->batch array at index sensor->id, and then sets the current ODR value in sensor registers that depend on whether the register address is set in the above array element. This logic is valid for internal sensors only, i.e. the accelerometer and gyroscope; however, since commit c91c1c844ebd ("iio: imu: st_lsm6dsx: add i2c embedded controller support"), this function is called also when configuring the hardware FIFO for external sensors (i.e. sensors accessed through the sensor hub functionality), which can result in unrelated device registers being written. Add a check to the beginning of st_lsm6dsx_set_fifo_odr() so that it does not touch any registers unless it is called for internal sensors. Fixes: c91c1c844ebd ("iio: imu: st_lsm6dsx: add i2c embedded controller support") Signed-off-by: Francesco Lavra Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 55d877745575..1ee2fc5f5f1f 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -225,6 +225,10 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, const struct st_lsm6dsx_reg *batch_reg; u8 data; + /* Only internal sensors have a FIFO ODR configuration register. */ + if (sensor->id >= ARRAY_SIZE(hw->settings->batch)) + return 0; + batch_reg = &hw->settings->batch[sensor->id]; if (batch_reg->addr) { int val; From 679c04c10d65d32a3f269e696b22912ff0a001b9 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Wed, 25 Feb 2026 11:06:01 +0100 Subject: [PATCH 036/712] iio: imu: st_lsm6dsx: Set buffer sampling frequency for accelerometer only The st_lsm6dsx_hwfifo_odr_store() function, which is called when userspace writes the buffer sampling frequency sysfs attribute, calls st_lsm6dsx_check_odr(), which accesses the odr_table array at index `sensor->id`; since this array is only 2 entries long, an access for any sensor type other than accelerometer or gyroscope is an out-of-bounds access. The motivation for being able to set a buffer frequency different from the sensor sampling frequency is to support use cases that need accurate event detection (which requires a high sampling frequency) while retrieving sensor data at low frequency. Since all the supported event types are generated from acceleration data only, do not create the buffer sampling frequency attribute for sensor types other than the accelerometer. Fixes: 6b648a36c200 ("iio: imu: st_lsm6dsx: Decouple sensor ODR from FIFO batch data rate") Signed-off-by: Francesco Lavra Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 1ee2fc5f5f1f..5b28a3ffcc3d 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -862,12 +862,21 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) int i, ret; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + const struct iio_dev_attr **attrs; + if (!hw->iio_devs[i]) continue; + /* + * For the accelerometer, allow setting FIFO sampling frequency + * values different from the sensor sampling frequency, which + * may be needed to keep FIFO data rate low while sampling + * acceleration data at high rates for accurate event detection. + */ + attrs = i == ST_LSM6DSX_ID_ACC ? st_lsm6dsx_buffer_attrs : NULL; ret = devm_iio_kfifo_buffer_setup_ext(hw->dev, hw->iio_devs[i], &st_lsm6dsx_buffer_ops, - st_lsm6dsx_buffer_attrs); + attrs); if (ret) return ret; } From edb11a1aef4011a4b7b22cc3c3396c6fe371f4a6 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:15 -0600 Subject: [PATCH 037/712] iio: gyro: mpu3050: Fix incorrect free_irq() variable The handler for the IRQ part of this driver is mpu3050->trig but, in the teardown free_irq() is called with handler mpu3050. Use correct IRQ handler when calling free_irq(). Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 317e7b217ec6..8df1f524d342 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1269,7 +1269,7 @@ void mpu3050_common_remove(struct device *dev) pm_runtime_disable(dev); iio_triggered_buffer_cleanup(indio_dev); if (mpu3050->irq) - free_irq(mpu3050->irq, mpu3050); + free_irq(mpu3050->irq, mpu3050->trig); iio_device_unregister(indio_dev); mpu3050_power_down(mpu3050); } From 4216db1043a3be72ef9c2b7b9f393d7fa72496e6 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:16 -0600 Subject: [PATCH 038/712] iio: gyro: mpu3050: Fix irq resource leak The interrupt handler is setup but only a few lines down if iio_trigger_register() fails the function returns without properly releasing the handler. Add cleanup goto to resolve resource leak. Detected by Smatch: drivers/iio/gyro/mpu3050-core.c:1128 mpu3050_trigger_probe() warn: 'irq' from request_threaded_irq() not released on lines: 1124. Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 8df1f524d342..d2f0899ac46b 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1129,11 +1129,16 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq) ret = iio_trigger_register(mpu3050->trig); if (ret) - return ret; + goto err_iio_trigger; indio_dev->trig = iio_trigger_get(mpu3050->trig); return 0; + +err_iio_trigger: + free_irq(mpu3050->irq, mpu3050->trig); + + return ret; } int mpu3050_common_probe(struct device *dev, From 4c05799449108fb0e0a6bd30e65fffc71e60db4d Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:17 -0600 Subject: [PATCH 039/712] iio: gyro: mpu3050: Move iio_device_register() to correct location iio_device_register() should be at the end of the probe function to prevent race conditions. Place iio_device_register() at the end of the probe function and place iio_device_unregister() accordingly. Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Suggested-by: Jonathan Cameron Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index d2f0899ac46b..2e92daf047bd 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1226,12 +1226,6 @@ int mpu3050_common_probe(struct device *dev, goto err_power_down; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "device register failed\n"); - goto err_cleanup_buffer; - } - dev_set_drvdata(dev, indio_dev); /* Check if we have an assigned IRQ to use as trigger */ @@ -1254,9 +1248,20 @@ int mpu3050_common_probe(struct device *dev, pm_runtime_use_autosuspend(dev); pm_runtime_put(dev); + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "device register failed\n"); + goto err_iio_device_register; + } + return 0; -err_cleanup_buffer: +err_iio_device_register: + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + if (irq) + free_irq(mpu3050->irq, mpu3050->trig); iio_triggered_buffer_cleanup(indio_dev); err_power_down: mpu3050_power_down(mpu3050); @@ -1269,13 +1274,13 @@ void mpu3050_common_remove(struct device *dev) struct iio_dev *indio_dev = dev_get_drvdata(dev); struct mpu3050 *mpu3050 = iio_priv(indio_dev); + iio_device_unregister(indio_dev); pm_runtime_get_sync(dev); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); iio_triggered_buffer_cleanup(indio_dev); if (mpu3050->irq) free_irq(mpu3050->irq, mpu3050->trig); - iio_device_unregister(indio_dev); mpu3050_power_down(mpu3050); } From d14116f6529fa085b1a1b1f224dc9604e4d2a29c Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:18 -0600 Subject: [PATCH 040/712] iio: gyro: mpu3050: Fix out-of-sequence free_irq() The triggered buffer is initialized before the IRQ is requested. The removal path currently calls iio_triggered_buffer_cleanup() before free_irq(). This violates the expected LIFO. Place free_irq() in the correct location relative to iio_triggered_buffer_cleanup(). Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Suggested-by: Jonathan Cameron Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 2e92daf047bd..d84e04e4b431 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1278,9 +1278,9 @@ void mpu3050_common_remove(struct device *dev) pm_runtime_get_sync(dev); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); - iio_triggered_buffer_cleanup(indio_dev); if (mpu3050->irq) free_irq(mpu3050->irq, mpu3050->trig); + iio_triggered_buffer_cleanup(indio_dev); mpu3050_power_down(mpu3050); } From 20c2a46da4c65ac9c84f2876e2369a260b50e95b Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 23 Feb 2026 14:45:13 +0800 Subject: [PATCH 041/712] iio: adc: ad4062: Replace IRQF_ONESHOT with IRQF_NO_THREAD In ad4062_request_irq(), when request irq for "gp1", the code uses IRQF_ONESHOT flag, which is not appropriate for a primary handler that does not have a secondary threaded handler. And since commit aef30c8d569c ("genirq: Warn about using IRQF_ONESHOT without a threaded handler"), the IRQ core checks IRQF_ONESHOT flag in IRQ request and gives a warning if there is no threaded handler. Since there is no threaded handler, replace devm_request_threaded_irq with devm_request_irq, and replace IRQF_ONESHOT with IRQF_NO_THREAD. Also remove an extraneous semicolon at the end of ad4062_write_raw_dispatch(). Found by code review, compile pass. Fixes: d5284402d28f ("iio: adc: Add support for ad4062") Signed-off-by: Felix Gu Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4062.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c index dd4ad32aa6f5..8b03736d55fc 100644 --- a/drivers/iio/adc/ad4062.c +++ b/drivers/iio/adc/ad4062.c @@ -719,10 +719,8 @@ static int ad4062_request_irq(struct iio_dev *indio_dev) } st->gpo_irq[1] = true; - return devm_request_threaded_irq(dev, ret, - ad4062_irq_handler_drdy, - NULL, IRQF_ONESHOT, indio_dev->name, - indio_dev); + return devm_request_irq(dev, ret, ad4062_irq_handler_drdy, + IRQF_NO_THREAD, indio_dev->name, indio_dev); } static const struct iio_trigger_ops ad4062_trigger_ops = { @@ -955,7 +953,7 @@ static int ad4062_write_raw_dispatch(struct ad4062_state *st, int val, int val2, default: return -EINVAL; } -}; +} static int ad4062_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, From c47ac75f5f24020cc0c8b835457a7637ad450939 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 20 Feb 2026 14:45:14 -0800 Subject: [PATCH 042/712] iio: hid-sensors: Use software trigger Recent changes linux mainline resulted in warning: "genirq: Warn about using IRQF_ONESHOT without a threaded handler" when HID sensor hub is used. When INDIO_BUFFER_TRIGGERED is used, the core attaches a poll function when enabling the buffer. This poll function uses request_threaded_irq() with both bottom half and top half handlers. But when using HID sensor hub, bottom half (thread handler) is not registered. In HID sensors, once a sensor is powered on, the hub collects samples and pushes data to the host when programmed thresholds are met. When this data is received for a sensor, it is pushed using iio_push_to_buffers_with_ts(). The sensor is powered ON or OFF based on the trigger callback set_trigger_state() when the poll function is attached. During the call to iio_triggered_buffer_setup_ext(), the HID sensor specifies only a handler function but provides no thread handler, as there is no data to read from the hub in thread context. Internally, this results in calling request_threaded_irq(). Recent kernel changes now warn when request_threaded_irq() is called without a thread handler. To address this issue, fundamental changes are required to avoid using iio_triggered_buffer_setup_ext(). HID sensors can use INDIO_BUFFER_SOFTWARE instead of INDIO_BUFFER_TRIGGERED, as this can work in trigger-less mode. In this approach, when user space opens the buffer, the sensor is powered on, and when the buffer is closed, the sensor is powered off using iio_buffer_setup_ops callbacks. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- .../common/hid-sensors/hid-sensor-trigger.c | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 5540e2d28f4a..417c4ab8c1b2 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "hid-sensor-trigger.h" static ssize_t _hid_sensor_set_report_latency(struct device *dev, @@ -202,12 +203,21 @@ static void hid_sensor_set_power_work(struct work_struct *work) _hid_sensor_power_state(attrb, true); } -static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) +static int buffer_postenable(struct iio_dev *indio_dev) { - return hid_sensor_power_state(iio_trigger_get_drvdata(trig), state); + return hid_sensor_power_state(iio_device_get_drvdata(indio_dev), 1); } +static int buffer_predisable(struct iio_dev *indio_dev) +{ + return hid_sensor_power_state(iio_device_get_drvdata(indio_dev), 0); +} + +static const struct iio_buffer_setup_ops hid_sensor_buffer_ops = { + .postenable = buffer_postenable, + .predisable = buffer_predisable, +}; + void hid_sensor_remove_trigger(struct iio_dev *indio_dev, struct hid_sensor_common *attrb) { @@ -219,14 +229,9 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, cancel_work_sync(&attrb->work); iio_trigger_unregister(attrb->trigger); iio_trigger_free(attrb->trigger); - iio_triggered_buffer_cleanup(indio_dev); } EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, "IIO_HID"); -static const struct iio_trigger_ops hid_sensor_trigger_ops = { - .set_trigger_state = &hid_sensor_data_rdy_trigger_set_state, -}; - int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, struct hid_sensor_common *attrb) { @@ -239,25 +244,34 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, else fifo_attrs = NULL; - ret = iio_triggered_buffer_setup_ext(indio_dev, - &iio_pollfunc_store_time, NULL, - IIO_BUFFER_DIRECTION_IN, - NULL, fifo_attrs); + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED; + + ret = devm_iio_kfifo_buffer_setup_ext(&indio_dev->dev, indio_dev, + &hid_sensor_buffer_ops, + fifo_attrs); if (ret) { - dev_err(&indio_dev->dev, "Triggered Buffer Setup Failed\n"); + dev_err(&indio_dev->dev, "Kfifo Buffer Setup Failed\n"); return ret; } + /* + * The current user space in distro "iio-sensor-proxy" is not working in + * trigerless mode and it expects + * /sys/bus/iio/devices/iio:device0/trigger/current_trigger. + * The change replacing iio_triggered_buffer_setup_ext() with + * devm_iio_kfifo_buffer_setup_ext() will not create attribute without + * registering a trigger with INDIO_HARDWARE_TRIGGERED. + * So the below code fragment is still required. + */ + trig = iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", name, iio_device_id(indio_dev)); if (trig == NULL) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); - ret = -ENOMEM; - goto error_triggered_buffer_cleanup; + return -ENOMEM; } iio_trigger_set_drvdata(trig, attrb); - trig->ops = &hid_sensor_trigger_ops; ret = iio_trigger_register(trig); if (ret) { @@ -284,8 +298,6 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, iio_trigger_unregister(trig); error_free_trig: iio_trigger_free(trig); -error_triggered_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); return ret; } EXPORT_SYMBOL_NS(hid_sensor_setup_trigger, "IIO_HID"); From 773ef9f95385bae52dcb7fd129fefba3a71a04db Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Feb 2026 16:33:54 -0600 Subject: [PATCH 043/712] iio: imu: bno055: fix BNO055_SCAN_CH_COUNT off by one Fix an off-by-one error in the BNO055_SCAN_CH_COUNT macro. The count is derived by taking the difference of the last and first register addresses, dividing by the size of each channel (2 bytes). It needs to also add 1 to account for the fact that the count is inclusive of both the first and last channels. Thanks to the aligned_s64 timestamp field, there was already extra padding in the buffer, so there were no runtime issues caused by this bug. Fixes: 4aefe1c2bd0c ("iio: imu: add Bosch Sensortec BNO055 core driver") Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bno055/bno055.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index 303bc308f80a..c96fec2ebb3e 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -64,7 +64,7 @@ #define BNO055_GRAVITY_DATA_X_LSB_REG 0x2E #define BNO055_GRAVITY_DATA_Y_LSB_REG 0x30 #define BNO055_GRAVITY_DATA_Z_LSB_REG 0x32 -#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2) +#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2 + 1) #define BNO055_TEMP_REG 0x34 #define BNO055_CALIB_STAT_REG 0x35 #define BNO055_CALIB_STAT_MAGN_SHIFT 0 From 6ca4bcc23ae86a1330e5347ae9eb6c5d0cb690ab Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Tue, 24 Feb 2026 17:53:00 -0500 Subject: [PATCH 044/712] gpu: nova-core: Kconfig: Sort select statements alphabetically Reorder the select statements in NOVA_CORE Kconfig to be in alphabetical order. Suggested-by: Danilo Krummrich Signed-off-by: Joel Fernandes Link: https://patch.msgid.link/20260224225323.3312204-3-joelagnelf@nvidia.com [acourbot@nvidia.com: fix conflict due to patch reordering.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig index 527920f9c4d3..a4f2380654e2 100644 --- a/drivers/gpu/nova-core/Kconfig +++ b/drivers/gpu/nova-core/Kconfig @@ -3,8 +3,8 @@ config NOVA_CORE depends on 64BIT depends on PCI depends on RUST - select RUST_FW_LOADER_ABSTRACTIONS select AUXILIARY_BUS + select RUST_FW_LOADER_ABSTRACTIONS default n help Choose this if you want to build the Nova Core driver for Nvidia From 8a9ebe8c3ca4c5bdad8f010656f4c2155da589fd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 28 Feb 2026 17:48:22 -0800 Subject: [PATCH 045/712] gpio: timberdale: repair kernel-doc comments Use a ':' after struct member names to avoid kernel-doc warnings: Warning: include/linux/timb_gpio.h:22 struct member 'gpio_base' not described in 'timbgpio_platform_data' Warning: include/linux/timb_gpio.h:22 struct member 'nr_pins' not described in 'timbgpio_platform_data' Warning: include/linux/timb_gpio.h:22 struct member 'irq_base' not described in 'timbgpio_platform_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260301014822.3133268-1-rdunlap@infradead.org Signed-off-by: Bartosz Golaszewski --- include/linux/timb_gpio.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/timb_gpio.h b/include/linux/timb_gpio.h index 3faf5a6bb13e..74f5e73bf6db 100644 --- a/include/linux/timb_gpio.h +++ b/include/linux/timb_gpio.h @@ -9,10 +9,10 @@ /** * struct timbgpio_platform_data - Platform data of the Timberdale GPIO driver - * @gpio_base The number of the first GPIO pin, set to -1 for + * @gpio_base: The number of the first GPIO pin, set to -1 for * dynamic number allocation. - * @nr_pins Number of pins that is supported by the hardware (1-32) - * @irq_base If IRQ is supported by the hardware, this is the base + * @nr_pins: Number of pins that is supported by the hardware (1-32) + * @irq_base: If IRQ is supported by the hardware, this is the base * number of IRQ:s. One IRQ per pin will be used. Set to * -1 if IRQ:s is not supported. */ From 189645ba9cd9c1eed45151aacaae4347c1eb86a7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 28 Feb 2026 17:48:11 -0800 Subject: [PATCH 046/712] gpio: nomadik: repair some kernel-doc comments Avoid these kernel-doc warnings by: - adding short descriptions for enums - using correct (matching) struct names in kernel-doc short descriptions - using the correct struct member name for @nfunctions Warning: include/linux/gpio/gpio-nomadik.h:116 missing initial short description on line: * enum prcm_gpiocr_reg_index Warning: include/linux/gpio/gpio-nomadik.h:125 missing initial short description on line: * enum prcm_gpiocr_altcx_index Warning: include/linux/gpio/gpio-nomadik.h:146 expecting prototype for struct prcm_gpio_altcx. Prototype was for struct prcm_gpiocr_altcx instead Warning: include/linux/gpio/gpio-nomadik.h:156 expecting prototype for struct prcm_gpio_altcx_pin_desc. Prototype was for struct prcm_gpiocr_altcx_pin_desc instead Warning: include/linux/gpio/gpio-nomadik.h:212 struct member 'nfunctions' not described in 'nmk_pinctrl_soc_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260301014811.3133250-1-rdunlap@infradead.org Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/gpio-nomadik.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/linux/gpio/gpio-nomadik.h b/include/linux/gpio/gpio-nomadik.h index 592a774a53cd..8061b9826361 100644 --- a/include/linux/gpio/gpio-nomadik.h +++ b/include/linux/gpio/gpio-nomadik.h @@ -114,8 +114,7 @@ struct nmk_gpio_chip { } /** - * enum prcm_gpiocr_reg_index - * Used to reference an PRCM GPIOCR register address. + * enum prcm_gpiocr_reg_index - Used to reference a PRCM GPIOCR register address. */ enum prcm_gpiocr_reg_index { PRCM_IDX_GPIOCR1, @@ -123,8 +122,7 @@ enum prcm_gpiocr_reg_index { PRCM_IDX_GPIOCR3 }; /** - * enum prcm_gpiocr_altcx_index - * Used to reference an Other alternate-C function. + * enum prcm_gpiocr_altcx_index - Used to reference an Other alternate-C function. */ enum prcm_gpiocr_altcx_index { PRCM_IDX_GPIOCR_ALTC1, @@ -135,7 +133,7 @@ enum prcm_gpiocr_altcx_index { }; /** - * struct prcm_gpio_altcx - Other alternate-C function + * struct prcm_gpiocr_altcx - Other alternate-C function * @used: other alternate-C function availability * @reg_index: PRCM GPIOCR register index used to control the function * @control_bit: PRCM GPIOCR bit used to control the function @@ -147,7 +145,7 @@ struct prcm_gpiocr_altcx { } __packed; /** - * struct prcm_gpio_altcx_pin_desc - Other alternate-C pin + * struct prcm_gpiocr_altcx_pin_desc - Other alternate-C pin * @pin: The pin number * @altcx: array of other alternate-C[1-4] functions */ @@ -193,7 +191,7 @@ struct nmk_pingroup { * numbering. * @npins: The number of entries in @pins. * @functions: The functions supported on this SoC. - * @nfunction: The number of entries in @functions. + * @nfunctions: The number of entries in @functions. * @groups: An array describing all pin groups the pin SoC supports. * @ngroups: The number of entries in @groups. * @altcx_pins: The pins that support Other alternate-C function on this SoC From 15da5bc9f3adab7242867db0251fe451ac3ddb72 Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Mon, 23 Feb 2026 16:23:14 -0800 Subject: [PATCH 047/712] drm/tyr: Clarify driver/device type names Currently the `TyrDriver` struct implements both `platform::Driver` and `drm::Driver`. For clarity, split up these two roles: - Introduce `TyrPlatformDriverData` to implement `platform::Driver`, and - Introduce `TyrDrmDriver` to implement `drm::Driver`. Also rename other variables to reflect their roles in the DRM context: - Rename `TyrDevice` to `TyrDrmDevice` - Rename `TyrData` to `TyrDrmDeviceData` - Rename `File` to `TyrDrmFileData` - Rename `DrmFile` to `TyrDrmFile` No functional changes are intended. Co-developed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Deborah Brouwer Link: https://patch.msgid.link/20260224002314.344675-1-deborah.brouwer@collabora.com Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/driver.rs | 40 ++++++++++++++++++----------------- drivers/gpu/drm/tyr/file.rs | 23 +++++++++----------- drivers/gpu/drm/tyr/gem.rs | 9 +++----- drivers/gpu/drm/tyr/tyr.rs | 4 ++-- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 259a5157eb47..611434641580 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -30,7 +30,7 @@ }; use crate::{ - file::File, + file::TyrDrmFileData, gem::TyrObject, gpu, gpu::GpuInfo, @@ -39,16 +39,18 @@ pub(crate) type IoMem = kernel::io::mem::IoMem; +pub(crate) struct TyrDrmDriver; + /// Convenience type alias for the DRM device type for this driver. -pub(crate) type TyrDevice = drm::Device; +pub(crate) type TyrDrmDevice = drm::Device; #[pin_data(PinnedDrop)] -pub(crate) struct TyrDriver { - _device: ARef, +pub(crate) struct TyrPlatformDriverData { + _device: ARef, } #[pin_data(PinnedDrop)] -pub(crate) struct TyrData { +pub(crate) struct TyrDrmDeviceData { pub(crate) pdev: ARef, #[pin] @@ -71,9 +73,9 @@ pub(crate) struct TyrData { // that it will be removed in a future patch. // // SAFETY: This will be removed in a future patch. -unsafe impl Send for TyrData {} +unsafe impl Send for TyrDrmDeviceData {} // SAFETY: This will be removed in a future patch. -unsafe impl Sync for TyrData {} +unsafe impl Sync for TyrDrmDeviceData {} fn issue_soft_reset(dev: &Device, iomem: &Devres) -> Result { regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?; @@ -92,14 +94,14 @@ fn issue_soft_reset(dev: &Device, iomem: &Devres) -> Result { kernel::of_device_table!( OF_TABLE, MODULE_OF_TABLE, - ::IdInfo, + ::IdInfo, [ (of::DeviceId::new(c"rockchip,rk3588-mali"), ()), (of::DeviceId::new(c"arm,mali-valhall-csf"), ()) ] ); -impl platform::Driver for TyrDriver { +impl platform::Driver for TyrPlatformDriverData { type IdInfo = (); const OF_ID_TABLE: Option> = Some(&OF_TABLE); @@ -129,7 +131,7 @@ fn probe( let platform: ARef = pdev.into(); - let data = try_pin_init!(TyrData { + let data = try_pin_init!(TyrDrmDeviceData { pdev: platform.clone(), clks <- new_mutex!(Clocks { core: core_clk, @@ -143,10 +145,10 @@ fn probe( gpu_info, }); - let tdev: ARef = drm::Device::new(pdev.as_ref(), data)?; - drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?; + let ddev: ARef = drm::Device::new(pdev.as_ref(), data)?; + drm::driver::Registration::new_foreign_owned(&ddev, pdev.as_ref(), 0)?; - let driver = TyrDriver { _device: tdev }; + let driver = TyrPlatformDriverData { _device: ddev }; // We need this to be dev_info!() because dev_dbg!() does not work at // all in Rust for now, and we need to see whether probe succeeded. @@ -156,12 +158,12 @@ fn probe( } #[pinned_drop] -impl PinnedDrop for TyrDriver { +impl PinnedDrop for TyrPlatformDriverData { fn drop(self: Pin<&mut Self>) {} } #[pinned_drop] -impl PinnedDrop for TyrData { +impl PinnedDrop for TyrDrmDeviceData { fn drop(self: Pin<&mut Self>) { // TODO: the type-state pattern for Clks will fix this. let clks = self.clks.lock(); @@ -182,15 +184,15 @@ fn drop(self: Pin<&mut Self>) { }; #[vtable] -impl drm::Driver for TyrDriver { - type Data = TyrData; - type File = File; +impl drm::Driver for TyrDrmDriver { + type Data = TyrDrmDeviceData; + type File = TyrDrmFileData; type Object = drm::gem::Object; const INFO: drm::DriverInfo = INFO; kernel::declare_drm_ioctls! { - (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, File::dev_query), + (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query), } } diff --git a/drivers/gpu/drm/tyr/file.rs b/drivers/gpu/drm/tyr/file.rs index 48bff4476d74..450be5ab9aaf 100644 --- a/drivers/gpu/drm/tyr/file.rs +++ b/drivers/gpu/drm/tyr/file.rs @@ -7,35 +7,32 @@ uapi, // }; -use crate::{ - driver::TyrDevice, - TyrDriver, // -}; +use crate::driver::TyrDrmDriver; #[pin_data] -pub(crate) struct File {} +pub(crate) struct TyrDrmFileData {} /// Convenience type alias for our DRM `File` type -pub(crate) type DrmFile = drm::file::File; +pub(crate) type TyrDrmFile = drm::file::File; -impl drm::file::DriverFile for File { - type Driver = TyrDriver; +impl drm::file::DriverFile for TyrDrmFileData { + type Driver = TyrDrmDriver; fn open(_dev: &drm::Device) -> Result>> { KBox::try_pin_init(try_pin_init!(Self {}), GFP_KERNEL) } } -impl File { +impl TyrDrmFileData { pub(crate) fn dev_query( - tdev: &TyrDevice, + ddev: &drm::Device, devquery: &mut uapi::drm_panthor_dev_query, - _file: &DrmFile, + _file: &TyrDrmFile, ) -> Result { if devquery.pointer == 0 { match devquery.type_ { uapi::drm_panthor_dev_query_type_DRM_PANTHOR_DEV_QUERY_GPU_INFO => { - devquery.size = core::mem::size_of_val(&tdev.gpu_info) as u32; + devquery.size = core::mem::size_of_val(&ddev.gpu_info) as u32; Ok(0) } _ => Err(EINVAL), @@ -49,7 +46,7 @@ pub(crate) fn dev_query( ) .writer(); - writer.write(&tdev.gpu_info)?; + writer.write(&ddev.gpu_info)?; Ok(0) } diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index 8f2d23e3c093..514524ae07ef 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -5,19 +5,16 @@ prelude::*, // }; -use crate::driver::{ - TyrDevice, - TyrDriver, // -}; +use crate::driver::TyrDrmDriver; /// GEM Object inner driver data #[pin_data] pub(crate) struct TyrObject {} impl gem::DriverObject for TyrObject { - type Driver = TyrDriver; + type Driver = TyrDrmDriver; - fn new(_dev: &TyrDevice, _size: usize) -> impl PinInit { + fn new(_dev: &kernel::drm::Device, _size: usize) -> impl PinInit { try_pin_init!(TyrObject {}) } } diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs index 861d1db43072..9432ddd6b5b8 100644 --- a/drivers/gpu/drm/tyr/tyr.rs +++ b/drivers/gpu/drm/tyr/tyr.rs @@ -5,7 +5,7 @@ //! The name "Tyr" is inspired by Norse mythology, reflecting Arm's tradition of //! naming their GPUs after Nordic mythological figures and places. -use crate::driver::TyrDriver; +use crate::driver::TyrPlatformDriverData; mod driver; mod file; @@ -14,7 +14,7 @@ mod regs; kernel::module_platform_driver! { - type: TyrDriver, + type: TyrPlatformDriverData, name: "tyr", authors: ["The Tyr driver authors"], description: "Arm Mali Tyr DRM driver", From 36f6d4db3c5cb0f58fb02b1f54f9e86522d2f918 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 3 Mar 2026 00:00:04 +0800 Subject: [PATCH 048/712] iio: adc: ti-ads1119: Replace IRQF_ONESHOT with IRQF_NO_THREAD As there is no threaded handler, replace devm_request_threaded_irq() with devm_request_irq(), and as the handler calls iio_trigger_poll() which may not be called from a threaded handler replace IRQF_ONESHOT with IRQF_NO_THREAD. Since commit aef30c8d569c ("genirq: Warn about using IRQF_ONESHOT without a threaded handler"), the IRQ core checks IRQF_ONESHOT flag in IRQ request and gives a warning if there is no threaded handler. Fixes: a9306887eba4 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Felix Gu Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 4454f28b2b58..7f771c7023b8 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -735,10 +735,8 @@ static int ads1119_probe(struct i2c_client *client) return dev_err_probe(dev, ret, "Failed to setup IIO buffer\n"); if (client->irq > 0) { - ret = devm_request_threaded_irq(dev, client->irq, - ads1119_irq_handler, - NULL, IRQF_ONESHOT, - "ads1119", indio_dev); + ret = devm_request_irq(dev, client->irq, ads1119_irq_handler, + IRQF_NO_THREAD, "ads1119", indio_dev); if (ret) return dev_err_probe(dev, ret, "Failed to allocate irq\n"); From 56bd57e7b161f75535df91b229b0b2c64c6e5581 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 28 Feb 2026 14:02:22 -0600 Subject: [PATCH 049/712] iio: add IIO_DECLARE_QUATERNION() macro Add a new IIO_DECLARE_QUATERNION() macro that is used to declare the field in an IIO buffer struct that contains a quaternion vector. Quaternions are currently the only IIO data type that uses the .repeat feature of struct iio_scan_type. This has an implicit rule that the element in the buffer must be aligned to the entire size of the repeated element. This macro will make that requirement explicit. Since this is the only user, we just call the macro IIO_DECLARE_QUATERNION() instead of something more generic. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index a9ecff191bd9..2c91b7659ce9 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -931,6 +931,18 @@ static inline void *iio_device_get_drvdata(const struct iio_dev *indio_dev) #define IIO_DECLARE_DMA_BUFFER_WITH_TS(type, name, count) \ __IIO_DECLARE_BUFFER_WITH_TS(type, name, count) __aligned(IIO_DMA_MINALIGN) +/** + * IIO_DECLARE_QUATERNION() - Declare a quaternion element + * @type: element type of the individual vectors + * @name: identifier name + * + * Quaternions are a vector composed of 4 elements (W, X, Y, Z). Use this macro + * to declare a quaternion element in a struct to ensure proper alignment in + * an IIO buffer. + */ +#define IIO_DECLARE_QUATERNION(type, name) \ + type name[4] __aligned(sizeof(type) * 4) + struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv); /* The information at the returned address is guaranteed to be cacheline aligned */ From 50d4cc74b8a720a9682a9c94f7e62a5de6b2ed3a Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 28 Feb 2026 14:02:23 -0600 Subject: [PATCH 050/712] iio: orientation: hid-sensor-rotation: fix quaternion alignment Restore the alignment of sampled_vals to 16 bytes by using IIO_DECLARE_QUATERNION(). This field contains a quaternion value which has scan_type.repeat = 4 and storagebits = 32. So the alignment must be 16 bytes to match the assumptions of iio_storage_bytes_for_si() and also to not break userspace. Reported-by: Lixu Zhang Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221077 Fixes: b31a74075cb4 ("iio: orientation: hid-sensor-rotation: remove unnecessary alignment") Tested-by: Lixu Zhang Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index e759f91a710a..6806481873be 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -19,7 +19,7 @@ struct dev_rot_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info quaternion; struct { - s32 sampled_vals[4]; + IIO_DECLARE_QUATERNION(s32, sampled_vals); aligned_s64 timestamp; } scan; int scale_pre_decml; From 398c0c8bbc8f5a9d2f43863275a427a9d3720b6f Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 2 Mar 2026 16:59:55 -0500 Subject: [PATCH 051/712] dt-bindings: auxdisplay: ht16k33: Use unevaluatedProperties to fix common property warning Change additionalProperties to unevaluatedProperties because it refs to /schemas/input/matrix-keymap.yaml. Fix below CHECK_DTBS warnings: arch/arm/boot/dts/nxp/imx/imx6dl-victgo.dtb: keypad@70 (holtek,ht16k33): 'keypad,num-columns', 'keypad,num-rows' do not match any of the regexes: '^pinctrl-[0-9]+$' from schema $id: http://devicetree.org/schemas/auxdisplay/holtek,ht16k33.yaml# Fixes: f12b457c6b25c ("dt-bindings: auxdisplay: ht16k33: Convert to json-schema") Acked-by: Rob Herring (Arm) Signed-off-by: Frank Li Signed-off-by: Andy Shevchenko --- .../devicetree/bindings/auxdisplay/holtek,ht16k33.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml index b90eec2077b4..fe1272e86467 100644 --- a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml +++ b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml @@ -66,7 +66,7 @@ then: required: - refresh-rate-hz -additionalProperties: false +unevaluatedProperties: false examples: - | From e2fa075d5ce1963e7cb7b0ac708ba567e5af66db Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 5 Mar 2026 11:21:52 -0800 Subject: [PATCH 052/712] iio: adc: ti-ads7950: normalize return value of gpio_get The GPIO get callback is expected to return 0 or 1 (or a negative error code). Ensure that the value returned by ti_ads7950_get() for output pins is normalized to the [0, 1] range. Fixes: 86ef402d805d ("gpiolib: sanitize the return value of gpio_chip::get()") Reviewed-by: Andy Shevchenko Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Dmitry Torokhov Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index bbe1ce577789..b8cc39fc39fb 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -433,7 +433,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) /* If set as output, return the output */ if (st->gpio_cmd_settings_bitmask & BIT(offset)) { - ret = st->cmd_settings_bitmask & BIT(offset); + ret = (st->cmd_settings_bitmask & BIT(offset)) ? 1 : 0; goto out; } From d20bbae6e5d408a8a7c2a4344d76dd1ac557a149 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 5 Mar 2026 11:21:53 -0800 Subject: [PATCH 053/712] iio: adc: ti-ads7950: do not clobber gpio state in ti_ads7950_get() GPIO state was inadvertently overwritten by the result of spi_sync(), resulting in ti_ads7950_get() only returning 0 as GPIO state (or error). Fix this by introducing a separate variable to hold the state. Fixes: c97dce792dc8 ("iio: adc: ti-ads7950: add GPIO support") Reported-by: David Lechner Signed-off-by: Dmitry Torokhov Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index b8cc39fc39fb..cdc624889559 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -427,13 +427,15 @@ static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) { struct ti_ads7950_state *st = gpiochip_get_data(chip); + bool state; int ret; mutex_lock(&st->slock); /* If set as output, return the output */ if (st->gpio_cmd_settings_bitmask & BIT(offset)) { - ret = (st->cmd_settings_bitmask & BIT(offset)) ? 1 : 0; + state = st->cmd_settings_bitmask & BIT(offset); + ret = 0; goto out; } @@ -444,7 +446,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) if (ret) goto out; - ret = ((st->single_rx >> 12) & BIT(offset)) ? 1 : 0; + state = (st->single_rx >> 12) & BIT(offset); /* Revert back to original settings */ st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA; @@ -456,7 +458,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) out: mutex_unlock(&st->slock); - return ret; + return ret ?: state; } static int ti_ads7950_get_direction(struct gpio_chip *chip, From 6f658d19a5a29a602c372e8cfe8d4a623367d211 Mon Sep 17 00:00:00 2001 From: Chunyang Chen Date: Thu, 5 Mar 2026 20:43:02 +0800 Subject: [PATCH 054/712] iio: adc: ti-ads1018: fix type overflow for data rate The variable 'drate' is currently defined as u8. However, the data rate values in ads1018 can reach up to 3300 Hz, which exceeds the maximum value of 255 that a u8 can hold. Change the type of 'drate' to u32 to match the data_rate_mode_to_hz array definition and ensure the data rate is handled correctly. Fixes: bf0bba486b5b ("iio: adc: Add ti-ads1018 driver") Signed-off-by: Chunyang Chen Reviewed-by: Andy Shevchenko Reviewed-by: Kurt Borja Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1018.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c index 6246b3cab71f..0780abd0d0db 100644 --- a/drivers/iio/adc/ti-ads1018.c +++ b/drivers/iio/adc/ti-ads1018.c @@ -249,7 +249,7 @@ static int ads1018_single_shot(struct ads1018 *ads1018, struct iio_chan_spec const *chan, u16 *cnv) { u8 max_drate_mode = ads1018->chip_info->num_data_rate_mode_to_hz - 1; - u8 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode]; + u32 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode]; u8 pga_mode = ads1018->chan_data[chan->scan_index].pga_mode; struct spi_transfer xfer[2] = { { From 2f168094177f8553a36046afce139001801ca917 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 3 Mar 2026 21:47:33 +0800 Subject: [PATCH 055/712] iio: adc: ti-ads1119: Reinit completion before wait_for_completion_timeout() The completion is not reinit before wait_for_completion_timeout(), so wait_for_completion_timeout() will return immediately after the first successful completion. Fixes: a9306887eba4 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Felix Gu Reviewed-by: Francesco Dolcini Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 7f771c7023b8..79be71b4de96 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -280,6 +280,9 @@ static int ads1119_single_conversion(struct ads1119_state *st, if (ret) goto pdown; + if (st->client->irq) + reinit_completion(&st->completion); + ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); if (ret) goto pdown; From 7cf2f6ed8e7a3bf481ef70b6b4a2edb8abfa5c57 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Tue, 3 Mar 2026 10:38:26 +0800 Subject: [PATCH 056/712] iio: adc: aspeed: clear reference voltage bits before configuring vref Ensures the reference voltage bits are cleared in the ADC engine control register before configuring the voltage reference. This avoids potential misconfigurations caused by residual bits. Fixes: 1b5ceb55fec2 ("iio: adc: aspeed: Support ast2600 adc.") Signed-off-by: Billy Tsai Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 4be44c524b4d..83a9885b9ae4 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -415,6 +415,7 @@ static int aspeed_adc_vref_config(struct iio_dev *indio_dev) } adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_REF_VOLTAGE; ret = devm_regulator_get_enable_read_voltage(data->dev, "vref"); if (ret < 0 && ret != -ENODEV) From ea7e2e43d768102e2601dbbda42041c78d7a99f9 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 14:20:46 +0200 Subject: [PATCH 057/712] iio: imu: adis16550: fix swapped gyro/accel filter functions The low-pass filter handlers for IIO_ANGL_VEL and IIO_ACCEL call each other's filter functions in both read_raw and write_raw. Swap them so each channel type uses its correct filter accessor. Fixes: bac4368fab62 ("iio: imu: adis16550: add adis16550 support") Signed-off-by: Antoniu Miclaus Acked-by: Robert Budai Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16550.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 28f0dbd0226c..1f2af506f4bd 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -643,12 +643,12 @@ static int adis16550_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: switch (chan->type) { case IIO_ANGL_VEL: - ret = adis16550_get_accl_filter_freq(st, val); + ret = adis16550_get_gyro_filter_freq(st, val); if (ret) return ret; return IIO_VAL_INT; case IIO_ACCEL: - ret = adis16550_get_gyro_filter_freq(st, val); + ret = adis16550_get_accl_filter_freq(st, val); if (ret) return ret; return IIO_VAL_INT; @@ -681,9 +681,9 @@ static int adis16550_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: switch (chan->type) { case IIO_ANGL_VEL: - return adis16550_set_accl_filter_freq(st, val); - case IIO_ACCEL: return adis16550_set_gyro_filter_freq(st, val); + case IIO_ACCEL: + return adis16550_set_accl_filter_freq(st, val); default: return -EINVAL; } From bc9de9e1af2f05461460e1b215a6d209ee62d65a Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:38 +0900 Subject: [PATCH 058/712] gpu: nova-core: create falcon firmware DMA objects lazily When DMA was the only loading option for falcon firmwares, we decided to store them in DMA objects as soon as they were loaded from disk and patch them in-place to avoid having to do an extra copy. This decision complicates the PIO loading patch considerably, and actually does not even stand on its own when put into perspective with the fact that it requires 8 unsafe statements in the code that wouldn't exist if we stored the firmware into a `KVVec` and copied it into a DMA object at the last minute. The cost of the copy is, as can be expected, imperceptible at runtime. Thus, switch to a lazy DMA object creation model and simplify our code a bit. This will also have the nice side-effect of being more fit for PIO loading. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-1-8f0042c5d026@nvidia.com [acourbot@nvidia.com: add TODO item to switch back to a coherent allocation when it becomes convenient to do so.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 57 ++++++++----- drivers/gpu/nova-core/firmware.rs | 40 ++++----- drivers/gpu/nova-core/firmware/booter.rs | 33 +++----- drivers/gpu/nova-core/firmware/fwsec.rs | 103 ++++++++--------------- drivers/gpu/nova-core/gsp/boot.rs | 2 +- 5 files changed, 108 insertions(+), 127 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 37bfee1d0949..8d444cf9d55c 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -2,12 +2,13 @@ //! Falcon microprocessor base support -use core::ops::Deref; - use hal::FalconHal; use kernel::{ - device, + device::{ + self, + Device, // + }, dma::{ DmaAddress, DmaMask, // @@ -15,9 +16,7 @@ io::poll::read_poll_timeout, prelude::*, sync::aref::ARef, - time::{ - Delta, // - }, + time::Delta, }; use crate::{ @@ -351,6 +350,9 @@ pub(crate) struct FalconBromParams { /// Trait for providing load parameters of falcon firmwares. pub(crate) trait FalconLoadParams { + /// Returns the firmware data as a slice of bytes. + fn as_slice(&self) -> &[u8]; + /// Returns the load parameters for Secure `IMEM`. fn imem_sec_load_params(&self) -> FalconLoadTarget; @@ -370,9 +372,8 @@ pub(crate) trait FalconLoadParams { /// Trait for a falcon firmware. /// -/// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA -/// object. -pub(crate) trait FalconFirmware: FalconLoadParams + Deref { +/// A falcon firmware can be loaded on a given engine. +pub(crate) trait FalconFirmware: FalconLoadParams { /// Engine on which this firmware is to be loaded. type Target: FalconEngine; } @@ -415,10 +416,10 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result { /// `target_mem`. /// /// `sec` is set if the loaded firmware is expected to run in secure mode. - fn dma_wr>( + fn dma_wr( &self, bar: &Bar0, - fw: &F, + dma_obj: &DmaObject, target_mem: FalconMem, load_offsets: FalconLoadTarget, ) -> Result { @@ -430,11 +431,11 @@ fn dma_wr>( // For DMEM we can fold the start offset into the DMA handle. let (src_start, dma_start) = match target_mem { FalconMem::ImemSecure | FalconMem::ImemNonSecure => { - (load_offsets.src_start, fw.dma_handle()) + (load_offsets.src_start, dma_obj.dma_handle()) } FalconMem::Dmem => ( 0, - fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, + dma_obj.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, ), }; if dma_start % DmaAddress::from(DMA_LEN) > 0 { @@ -466,7 +467,7 @@ fn dma_wr>( dev_err!(self.dev, "DMA transfer length overflow\n"); return Err(EOVERFLOW); } - Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => { + Some(upper_bound) if usize::from_safe_cast(upper_bound) > dma_obj.size() => { dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n"); return Err(EINVAL); } @@ -515,7 +516,12 @@ fn dma_wr>( } /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. - fn dma_load>(&self, bar: &Bar0, fw: &F) -> Result { + fn dma_load>( + &self, + dev: &Device, + bar: &Bar0, + fw: &F, + ) -> Result { // The Non-Secure section only exists on firmware used by Turing and GA100, and // those platforms do not use DMA. if fw.imem_ns_load_params().is_some() { @@ -523,14 +529,22 @@ fn dma_load>(&self, bar: &Bar0, fw: &F) -> Result return Err(EINVAL); } + // Create DMA object with firmware content as the source of the DMA engine. + let dma_obj = DmaObject::from_data(dev, fw.as_slice())?; + self.dma_reset(bar); regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| { v.set_target(FalconFbifTarget::CoherentSysmem) .set_mem_type(FalconFbifMemType::Physical) }); - self.dma_wr(bar, fw, FalconMem::ImemSecure, fw.imem_sec_load_params())?; - self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params())?; + self.dma_wr( + bar, + &dma_obj, + FalconMem::ImemSecure, + fw.imem_sec_load_params(), + )?; + self.dma_wr(bar, &dma_obj, FalconMem::Dmem, fw.dmem_load_params())?; self.hal.program_brom(self, bar, &fw.brom_params())?; @@ -641,9 +655,14 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { } // Load a firmware image into Falcon memory - pub(crate) fn load>(&self, bar: &Bar0, fw: &F) -> Result { + pub(crate) fn load>( + &self, + dev: &Device, + bar: &Bar0, + fw: &F, + ) -> Result { match self.hal.load_method() { - LoadMethod::Dma => self.dma_load(bar, fw), + LoadMethod::Dma => self.dma_load(dev, bar, fw), LoadMethod::Pio => Err(ENOTSUPP), } } diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 815e8000bf81..5166c1f5972f 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -15,7 +15,6 @@ }; use crate::{ - dma::DmaObject, falcon::{ FalconFirmware, FalconLoadTarget, // @@ -292,7 +291,7 @@ impl SignedState for Unsigned {} struct Signed; impl SignedState for Signed {} -/// A [`DmaObject`] containing a specific microcode ready to be loaded into a falcon. +/// Microcode to be loaded into a specific falcon. /// /// This is module-local and meant for sub-modules to use internally. /// @@ -300,34 +299,35 @@ impl SignedState for Signed {} /// before it can be loaded (with an exception for development hardware). The /// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the /// firmware to its [`Signed`] state. -struct FirmwareDmaObject(DmaObject, PhantomData<(F, S)>); +// TODO: Consider replacing this with a coherent memory object once `CoherentAllocation` supports +// temporary CPU-exclusive access to the object without unsafe methods. +struct FirmwareObject(KVVec, PhantomData<(F, S)>); /// Trait for signatures to be patched directly into a given firmware. /// /// This is module-local and meant for sub-modules to use internally. trait FirmwareSignature: AsRef<[u8]> {} -impl FirmwareDmaObject { - /// Patches the firmware at offset `sig_base_img` with `signature`. +impl FirmwareObject { + /// Patches the firmware at offset `signature_start` with `signature`. fn patch_signature>( mut self, signature: &S, - sig_base_img: usize, - ) -> Result> { + signature_start: usize, + ) -> Result> { let signature_bytes = signature.as_ref(); - if sig_base_img + signature_bytes.len() > self.0.size() { - return Err(EINVAL); - } + let signature_end = signature_start + .checked_add(signature_bytes.len()) + .ok_or(EOVERFLOW)?; + let dst = self + .0 + .get_mut(signature_start..signature_end) + .ok_or(EINVAL)?; - // SAFETY: We are the only user of this object, so there cannot be any race. - let dst = unsafe { self.0.start_ptr_mut().add(sig_base_img) }; + // PANIC: `dst` and `signature_bytes` have the same length. + dst.copy_from_slice(signature_bytes); - // SAFETY: `signature` and `dst` are valid, properly aligned, and do not overlap. - unsafe { - core::ptr::copy_nonoverlapping(signature_bytes.as_ptr(), dst, signature_bytes.len()) - }; - - Ok(FirmwareDmaObject(self.0, PhantomData)) + Ok(FirmwareObject(self.0, PhantomData)) } /// Mark the firmware as signed without patching it. @@ -335,8 +335,8 @@ fn patch_signature>( /// This method is used to explicitly confirm that we do not need to sign the firmware, while /// allowing us to continue as if it was. This is typically only needed for development /// hardware. - fn no_patch_signature(self) -> FirmwareDmaObject { - FirmwareDmaObject(self.0, PhantomData) + fn no_patch_signature(self) -> FirmwareObject { + FirmwareObject(self.0, PhantomData) } } diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index ab374026b1f4..2b7166eaf283 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -4,10 +4,7 @@ //! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon //! (and optionally unload it through a separate firmware image). -use core::{ - marker::PhantomData, - ops::Deref, // -}; +use core::marker::PhantomData; use kernel::{ device, @@ -16,7 +13,6 @@ }; use crate::{ - dma::DmaObject, driver::Bar0, falcon::{ sec2::Sec2, @@ -28,7 +24,7 @@ }, firmware::{ BinFirmware, - FirmwareDmaObject, + FirmwareObject, FirmwareSignature, Signed, Unsigned, // @@ -269,12 +265,15 @@ pub(crate) struct BooterFirmware { // BROM falcon parameters. brom_params: FalconBromParams, // Device-mapped firmware image. - ucode: FirmwareDmaObject, + ucode: FirmwareObject, } -impl FirmwareDmaObject { - fn new_booter(dev: &device::Device, data: &[u8]) -> Result { - DmaObject::from_data(dev, data).map(|ucode| Self(ucode, PhantomData)) +impl FirmwareObject { + fn new_booter(data: &[u8]) -> Result { + let mut ucode = KVVec::new(); + ucode.extend_from_slice(data, GFP_KERNEL)?; + + Ok(Self(ucode, PhantomData)) } } @@ -328,7 +327,7 @@ pub(crate) fn new( let ucode = bin_fw .data() .ok_or(EINVAL) - .and_then(|data| FirmwareDmaObject::::new_booter(dev, data))?; + .and_then(FirmwareObject::::new_booter)?; let ucode_signed = { let mut signatures = hs_fw.signatures_iter()?.peekable(); @@ -400,6 +399,10 @@ pub(crate) fn new( } impl FalconLoadParams for BooterFirmware { + fn as_slice(&self) -> &[u8] { + self.ucode.0.as_slice() + } + fn imem_sec_load_params(&self) -> FalconLoadTarget { self.imem_sec_load_target.clone() } @@ -425,14 +428,6 @@ fn boot_addr(&self) -> u32 { } } -impl Deref for BooterFirmware { - type Target = DmaObject; - - fn deref(&self) -> &Self::Target { - &self.ucode.0 - } -} - impl FalconFirmware for BooterFirmware { type Target = Sec2; } diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index df3d8de14ca1..7fff3acdaa73 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -10,10 +10,7 @@ //! - The command to be run, as this firmware can perform several tasks ; //! - The ucode signature, so the GSP falcon can run FWSEC in HS mode. -use core::{ - marker::PhantomData, - ops::Deref, // -}; +use core::marker::PhantomData; use kernel::{ device::{ @@ -28,7 +25,6 @@ }; use crate::{ - dma::DmaObject, driver::Bar0, falcon::{ gsp::Gsp, @@ -40,7 +36,7 @@ }, firmware::{ FalconUCodeDesc, - FirmwareDmaObject, + FirmwareObject, FirmwareSignature, Signed, Unsigned, // @@ -174,52 +170,21 @@ fn as_ref(&self) -> &[u8] { impl FirmwareSignature for Bcrt30Rsa3kSignature {} -/// Reinterpret the area starting from `offset` in `fw` as an instance of `T` (which must implement -/// [`FromBytes`]) and return a reference to it. -/// -/// # Safety -/// -/// * Callers must ensure that the device does not read/write to/from memory while the returned -/// reference is live. -/// * Callers must ensure that this call does not race with a write to the same region while -/// the returned reference is live. -unsafe fn transmute(fw: &DmaObject, offset: usize) -> Result<&T> { - // SAFETY: The safety requirements of the function guarantee the device won't read - // or write to memory while the reference is alive and that this call won't race - // with writes to the same memory region. - T::from_bytes(unsafe { fw.as_slice(offset, size_of::())? }).ok_or(EINVAL) -} - -/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must -/// implement [`FromBytes`]) and return a reference to it. -/// -/// # Safety -/// -/// * Callers must ensure that the device does not read/write to/from memory while the returned -/// slice is live. -/// * Callers must ensure that this call does not race with a read or write to the same region -/// while the returned slice is live. -unsafe fn transmute_mut( - fw: &mut DmaObject, - offset: usize, -) -> Result<&mut T> { - // SAFETY: The safety requirements of the function guarantee the device won't read - // or write to memory while the reference is alive and that this call won't race - // with writes or reads to the same memory region. - T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::())? }).ok_or(EINVAL) -} - /// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon. /// /// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow. pub(crate) struct FwsecFirmware { /// Descriptor of the firmware. desc: FalconUCodeDesc, - /// GPU-accessible DMA object containing the firmware. - ucode: FirmwareDmaObject, + /// Object containing the firmware binary. + ucode: FirmwareObject, } impl FalconLoadParams for FwsecFirmware { + fn as_slice(&self) -> &[u8] { + self.ucode.0.as_slice() + } + fn imem_sec_load_params(&self) -> FalconLoadTarget { self.desc.imem_sec_load_params() } @@ -245,23 +210,15 @@ fn boot_addr(&self) -> u32 { } } -impl Deref for FwsecFirmware { - type Target = DmaObject; - - fn deref(&self) -> &Self::Target { - &self.ucode.0 - } -} - impl FalconFirmware for FwsecFirmware { type Target = Gsp; } -impl FirmwareDmaObject { - fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Result { +impl FirmwareObject { + fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result { let desc = bios.fwsec_image().header()?; - let ucode = bios.fwsec_image().ucode(&desc)?; - let mut dma_object = DmaObject::from_data(dev, ucode)?; + let mut ucode = KVVec::new(); + ucode.extend_from_slice(bios.fwsec_image().ucode(&desc)?, GFP_KERNEL)?; let hdr_offset = desc .imem_load_size() @@ -269,8 +226,11 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re .map(usize::from_safe_cast) .ok_or(EINVAL)?; - // SAFETY: we have exclusive access to `dma_object`. - let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?; + let hdr = ucode + .get(hdr_offset..) + .and_then(FalconAppifHdrV1::from_bytes_prefix) + .ok_or(EINVAL)? + .0; if hdr.version != 1 { return Err(EINVAL); @@ -284,8 +244,11 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re .and_then(|o| o.checked_add(i.checked_mul(usize::from(hdr.entry_size))?)) .ok_or(EINVAL)?; - // SAFETY: we have exclusive access to `dma_object`. - let app: &FalconAppifV1 = unsafe { transmute(&dma_object, entry_offset) }?; + let app = ucode + .get(entry_offset..) + .and_then(FalconAppifV1::from_bytes_prefix) + .ok_or(EINVAL)? + .0; if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER { continue; @@ -298,9 +261,11 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re .map(usize::from_safe_cast) .ok_or(EINVAL)?; - let dmem_mapper: &mut FalconAppifDmemmapperV3 = - // SAFETY: we have exclusive access to `dma_object`. - unsafe { transmute_mut(&mut dma_object, dmem_mapper_offset) }?; + let dmem_mapper = ucode + .get_mut(dmem_mapper_offset..) + .and_then(FalconAppifDmemmapperV3::from_bytes_mut_prefix) + .ok_or(EINVAL)? + .0; dmem_mapper.init_cmd = match cmd { FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS, @@ -314,9 +279,11 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re .map(usize::from_safe_cast) .ok_or(EINVAL)?; - let frts_cmd: &mut FrtsCmd = - // SAFETY: we have exclusive access to `dma_object`. - unsafe { transmute_mut(&mut dma_object, frts_cmd_offset) }?; + let frts_cmd = ucode + .get_mut(frts_cmd_offset..) + .and_then(FrtsCmd::from_bytes_mut_prefix) + .ok_or(EINVAL)? + .0; frts_cmd.read_vbios = ReadVbios { ver: 1, @@ -340,7 +307,7 @@ fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Re } // Return early as we found and patched the DMEMMAPPER region. - return Ok(Self(dma_object, PhantomData)); + return Ok(Self(ucode, PhantomData)); } Err(ENOTSUPP) @@ -357,7 +324,7 @@ pub(crate) fn new( bios: &Vbios, cmd: FwsecCommand, ) -> Result { - let ucode_dma = FirmwareDmaObject::::new_fwsec(dev, bios, cmd)?; + let ucode_dma = FirmwareObject::::new_fwsec(bios, cmd)?; // Patch signature if needed. let desc = bios.fwsec_image().header()?; @@ -429,7 +396,7 @@ pub(crate) fn run( .reset(bar) .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?; falcon - .load(bar, self) + .load(dev, bar, self) .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; let (mbox0, _) = falcon .boot(bar, Some(0), None) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index c56029f444cb..78957ed8814f 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -178,7 +178,7 @@ pub(crate) fn boot( ); sec2_falcon.reset(bar)?; - sec2_falcon.load(bar, &booter_loader)?; + sec2_falcon.load(dev, bar, &booter_loader)?; let wpr_handle = wpr_meta.dma_handle(); let (mbox0, mbox1) = sec2_falcon.boot( bar, From 3b97ec9fdef49932505cf4f99cd7074a04806240 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:39 +0900 Subject: [PATCH 059/712] gpu: nova-core: falcon: add constant for memory block alignment Falcon memory blocks are 256 bytes in size. This is a hard constant on all models. This value was hardcoded, so turn it into a documented constant. It will also become useful with the PIO loading code. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-2-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 8d444cf9d55c..31217cd3a795 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -25,6 +25,7 @@ falcon::hal::LoadMethod, gpu::Chipset, num::{ + self, FromSafeCast, IntoSafeCast, // }, @@ -36,6 +37,9 @@ mod hal; pub(crate) mod sec2; +/// Alignment (in bytes) of falcon memory blocks. +pub(crate) const MEM_BLOCK_ALIGNMENT: usize = 256; + // TODO[FPRI]: Replace with `ToPrimitive`. macro_rules! impl_from_enum_to_u8 { ($enum_type:ty) => { @@ -423,7 +427,7 @@ fn dma_wr( target_mem: FalconMem, load_offsets: FalconLoadTarget, ) -> Result { - const DMA_LEN: u32 = 256; + const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>(); // For IMEM, we want to use the start offset as a virtual address tag for each page, since // code addresses in the firmware (and the boot vector) are virtual. From 8a623869b8269dbf52d52711cd7b9355044b6b53 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:40 +0900 Subject: [PATCH 060/712] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency The current `FalconLoadParams` and `FalconLoadTarget` types are fit for DMA loading, but not so much for PIO loading which will require its own types. Start by renaming them to something that indicates that they are indeed DMA-related. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-3-8f0042c5d026@nvidia.com [acourbot@nvidia.com: fixup order of import items.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 19 +++++++------- drivers/gpu/nova-core/firmware.rs | 32 ++++++++++++------------ drivers/gpu/nova-core/firmware/booter.rs | 26 +++++++++---------- drivers/gpu/nova-core/firmware/fwsec.rs | 14 +++++------ 4 files changed, 46 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 31217cd3a795..9eb827477e5e 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -330,9 +330,10 @@ pub(crate) trait FalconEngine: const ID: Self; } -/// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM). +/// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM) +/// using DMA. #[derive(Debug, Clone)] -pub(crate) struct FalconLoadTarget { +pub(crate) struct FalconDmaLoadTarget { /// Offset from the start of the source object to copy from. pub(crate) src_start: u32, /// Offset from the start of the destination memory to copy into. @@ -352,20 +353,20 @@ pub(crate) struct FalconBromParams { pub(crate) ucode_id: u8, } -/// Trait for providing load parameters of falcon firmwares. -pub(crate) trait FalconLoadParams { +/// Trait implemented by falcon firmwares that can be loaded using DMA. +pub(crate) trait FalconDmaLoadable { /// Returns the firmware data as a slice of bytes. fn as_slice(&self) -> &[u8]; /// Returns the load parameters for Secure `IMEM`. - fn imem_sec_load_params(&self) -> FalconLoadTarget; + fn imem_sec_load_params(&self) -> FalconDmaLoadTarget; /// Returns the load parameters for Non-Secure `IMEM`, /// used only on Turing and GA100. - fn imem_ns_load_params(&self) -> Option; + fn imem_ns_load_params(&self) -> Option; /// Returns the load parameters for `DMEM`. - fn dmem_load_params(&self) -> FalconLoadTarget; + fn dmem_load_params(&self) -> FalconDmaLoadTarget; /// Returns the parameters to write into the BROM registers. fn brom_params(&self) -> FalconBromParams; @@ -377,7 +378,7 @@ pub(crate) trait FalconLoadParams { /// Trait for a falcon firmware. /// /// A falcon firmware can be loaded on a given engine. -pub(crate) trait FalconFirmware: FalconLoadParams { +pub(crate) trait FalconFirmware: FalconDmaLoadable { /// Engine on which this firmware is to be loaded. type Target: FalconEngine; } @@ -425,7 +426,7 @@ fn dma_wr( bar: &Bar0, dma_obj: &DmaObject, target_mem: FalconMem, - load_offsets: FalconLoadTarget, + load_offsets: FalconDmaLoadTarget, ) -> Result { const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>(); diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 5166c1f5972f..6d874753fe67 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -16,8 +16,8 @@ use crate::{ falcon::{ - FalconFirmware, - FalconLoadTarget, // + FalconDmaLoadTarget, + FalconFirmware, // }, gpu, num::{ @@ -170,9 +170,9 @@ fn size(&self) -> usize { ((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast() } - fn imem_sec_load_params(&self) -> FalconLoadTarget; - fn imem_ns_load_params(&self) -> Option; - fn dmem_load_params(&self) -> FalconLoadTarget; + fn imem_sec_load_params(&self) -> FalconDmaLoadTarget; + fn imem_ns_load_params(&self) -> Option; + fn dmem_load_params(&self) -> FalconDmaLoadTarget; } impl FalconUCodeDescriptor for FalconUCodeDescV2 { @@ -204,24 +204,24 @@ fn signature_versions(&self) -> u16 { 0 } - fn imem_sec_load_params(&self) -> FalconLoadTarget { - FalconLoadTarget { + fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { + FalconDmaLoadTarget { src_start: 0, dst_start: self.imem_sec_base, len: self.imem_sec_size, } } - fn imem_ns_load_params(&self) -> Option { - Some(FalconLoadTarget { + fn imem_ns_load_params(&self) -> Option { + Some(FalconDmaLoadTarget { src_start: 0, dst_start: self.imem_phys_base, len: self.imem_load_size.checked_sub(self.imem_sec_size)?, }) } - fn dmem_load_params(&self) -> FalconLoadTarget { - FalconLoadTarget { + fn dmem_load_params(&self) -> FalconDmaLoadTarget { + FalconDmaLoadTarget { src_start: self.dmem_offset, dst_start: self.dmem_phys_base, len: self.dmem_load_size, @@ -258,21 +258,21 @@ fn signature_versions(&self) -> u16 { self.signature_versions } - fn imem_sec_load_params(&self) -> FalconLoadTarget { - FalconLoadTarget { + fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { + FalconDmaLoadTarget { src_start: 0, dst_start: self.imem_phys_base, len: self.imem_load_size, } } - fn imem_ns_load_params(&self) -> Option { + fn imem_ns_load_params(&self) -> Option { // Not used on V3 platforms None } - fn dmem_load_params(&self) -> FalconLoadTarget { - FalconLoadTarget { + fn dmem_load_params(&self) -> FalconDmaLoadTarget { + FalconDmaLoadTarget { src_start: self.imem_load_size, dst_start: self.dmem_phys_base, len: self.dmem_load_size, diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index 2b7166eaf283..97b2776db5a3 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -18,9 +18,9 @@ sec2::Sec2, Falcon, FalconBromParams, - FalconFirmware, - FalconLoadParams, - FalconLoadTarget, // + FalconDmaLoadTarget, + FalconDmaLoadable, + FalconFirmware, // }, firmware::{ BinFirmware, @@ -256,12 +256,12 @@ impl<'a> FirmwareSignature for BooterSignature<'a> {} /// The `Booter` loader firmware, responsible for loading the GSP. pub(crate) struct BooterFirmware { // Load parameters for Secure `IMEM` falcon memory. - imem_sec_load_target: FalconLoadTarget, + imem_sec_load_target: FalconDmaLoadTarget, // Load parameters for Non-Secure `IMEM` falcon memory, // used only on Turing and GA100 - imem_ns_load_target: Option, + imem_ns_load_target: Option, // Load parameters for `DMEM` falcon memory. - dmem_load_target: FalconLoadTarget, + dmem_load_target: FalconDmaLoadTarget, // BROM falcon parameters. brom_params: FalconBromParams, // Device-mapped firmware image. @@ -370,7 +370,7 @@ pub(crate) fn new( let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 { ( app0.offset, - Some(FalconLoadTarget { + Some(FalconDmaLoadTarget { src_start: 0, dst_start: load_hdr.os_code_offset, len: load_hdr.os_code_size, @@ -381,13 +381,13 @@ pub(crate) fn new( }; Ok(Self { - imem_sec_load_target: FalconLoadTarget { + imem_sec_load_target: FalconDmaLoadTarget { src_start: app0.offset, dst_start: imem_sec_dst_start, len: app0.len, }, imem_ns_load_target, - dmem_load_target: FalconLoadTarget { + dmem_load_target: FalconDmaLoadTarget { src_start: load_hdr.os_data_offset, dst_start: 0, len: load_hdr.os_data_size, @@ -398,20 +398,20 @@ pub(crate) fn new( } } -impl FalconLoadParams for BooterFirmware { +impl FalconDmaLoadable for BooterFirmware { fn as_slice(&self) -> &[u8] { self.ucode.0.as_slice() } - fn imem_sec_load_params(&self) -> FalconLoadTarget { + fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { self.imem_sec_load_target.clone() } - fn imem_ns_load_params(&self) -> Option { + fn imem_ns_load_params(&self) -> Option { self.imem_ns_load_target.clone() } - fn dmem_load_params(&self) -> FalconLoadTarget { + fn dmem_load_params(&self) -> FalconDmaLoadTarget { self.dmem_load_target.clone() } diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index 7fff3acdaa73..7ac5cfeb594d 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -30,9 +30,9 @@ gsp::Gsp, Falcon, FalconBromParams, - FalconFirmware, - FalconLoadParams, - FalconLoadTarget, // + FalconDmaLoadTarget, + FalconDmaLoadable, + FalconFirmware, // }, firmware::{ FalconUCodeDesc, @@ -180,20 +180,20 @@ pub(crate) struct FwsecFirmware { ucode: FirmwareObject, } -impl FalconLoadParams for FwsecFirmware { +impl FalconDmaLoadable for FwsecFirmware { fn as_slice(&self) -> &[u8] { self.ucode.0.as_slice() } - fn imem_sec_load_params(&self) -> FalconLoadTarget { + fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { self.desc.imem_sec_load_params() } - fn imem_ns_load_params(&self) -> Option { + fn imem_ns_load_params(&self) -> Option { self.desc.imem_ns_load_params() } - fn dmem_load_params(&self) -> FalconLoadTarget { + fn dmem_load_params(&self) -> FalconDmaLoadTarget { self.desc.dmem_load_params() } From 99d451cb8e76bd299b036003f067959816975b9c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:41 +0900 Subject: [PATCH 061/712] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable Not all firmware is necessarily loaded by DMA. Remove the requirement for `FalconFirmware` to implement `FalconDmaLoadable`, and adapt `Falcon`'s methods constraints accordingly. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-4-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 9eb827477e5e..450431804e1c 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -378,7 +378,7 @@ pub(crate) trait FalconDmaLoadable { /// Trait for a falcon firmware. /// /// A falcon firmware can be loaded on a given engine. -pub(crate) trait FalconFirmware: FalconDmaLoadable { +pub(crate) trait FalconFirmware { /// Engine on which this firmware is to be loaded. type Target: FalconEngine; } @@ -521,7 +521,7 @@ fn dma_wr( } /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. - fn dma_load>( + fn dma_load + FalconDmaLoadable>( &self, dev: &Device, bar: &Bar0, @@ -660,7 +660,7 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { } // Load a firmware image into Falcon memory - pub(crate) fn load>( + pub(crate) fn load + FalconDmaLoadable>( &self, dev: &Device, bar: &Bar0, From 9725005e2b4bac2f490bef2165eab18fc36b5b67 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:42 +0900 Subject: [PATCH 062/712] gpu: nova-core: move brom_params and boot_addr to FalconFirmware These methods are relevant no matter the loading method used, thus move them to the common `FalconFirmware` trait. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-5-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 12 ++++++------ drivers/gpu/nova-core/firmware/booter.rs | 8 ++++---- drivers/gpu/nova-core/firmware/fwsec.rs | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 450431804e1c..c90664efb0c5 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -367,12 +367,6 @@ pub(crate) trait FalconDmaLoadable { /// Returns the load parameters for `DMEM`. fn dmem_load_params(&self) -> FalconDmaLoadTarget; - - /// Returns the parameters to write into the BROM registers. - fn brom_params(&self) -> FalconBromParams; - - /// Returns the start address of the firmware. - fn boot_addr(&self) -> u32; } /// Trait for a falcon firmware. @@ -381,6 +375,12 @@ pub(crate) trait FalconDmaLoadable { pub(crate) trait FalconFirmware { /// Engine on which this firmware is to be loaded. type Target: FalconEngine; + + /// Returns the parameters to write into the BROM registers. + fn brom_params(&self) -> FalconBromParams; + + /// Returns the start address of the firmware. + fn boot_addr(&self) -> u32; } /// Contains the base parameters common to all Falcon instances. diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index 97b2776db5a3..de2a4536b532 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -414,6 +414,10 @@ fn imem_ns_load_params(&self) -> Option { fn dmem_load_params(&self) -> FalconDmaLoadTarget { self.dmem_load_target.clone() } +} + +impl FalconFirmware for BooterFirmware { + type Target = Sec2; fn brom_params(&self) -> FalconBromParams { self.brom_params.clone() @@ -427,7 +431,3 @@ fn boot_addr(&self) -> u32 { } } } - -impl FalconFirmware for BooterFirmware { - type Target = Sec2; -} diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index 7ac5cfeb594d..ca51d7c5be13 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -196,6 +196,10 @@ fn imem_ns_load_params(&self) -> Option { fn dmem_load_params(&self) -> FalconDmaLoadTarget { self.desc.dmem_load_params() } +} + +impl FalconFirmware for FwsecFirmware { + type Target = Gsp; fn brom_params(&self) -> FalconBromParams { FalconBromParams { @@ -210,10 +214,6 @@ fn boot_addr(&self) -> u32 { } } -impl FalconFirmware for FwsecFirmware { - type Target = Gsp; -} - impl FirmwareObject { fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result { let desc = bios.fwsec_image().header()?; From c1d2f7471ba7a21eb3c68b8405365f7e1eac5c9d Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 6 Mar 2026 13:52:43 +0900 Subject: [PATCH 063/712] gpu: nova-core: add PIO support for loading firmware images Turing and GA100 use programmed I/O (PIO) instead of DMA to upload firmware images into Falcon memory. Signed-off-by: Timur Tabi Co-developed-by: Alexandre Courbot Signed-off-by: Alexandre Courbot Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-6-8f0042c5d026@nvidia.com --- drivers/gpu/nova-core/falcon.rs | 218 +++++++++++++++++++++++++++- drivers/gpu/nova-core/falcon/hal.rs | 6 +- drivers/gpu/nova-core/regs.rs | 30 ++++ 3 files changed, 251 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index c90664efb0c5..2168ef2c5148 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -367,6 +367,127 @@ pub(crate) trait FalconDmaLoadable { /// Returns the load parameters for `DMEM`. fn dmem_load_params(&self) -> FalconDmaLoadTarget; + + /// Returns an adapter that provides the required parameter to load this firmware using PIO. + /// + /// This can only fail if some `u32` fields cannot be converted to `u16`, or if the indices in + /// the headers are invalid. + fn try_as_pio_loadable(&self) -> Result> { + let new_pio_imem = |params: FalconDmaLoadTarget, secure| { + let start = usize::from_safe_cast(params.src_start); + let end = start + usize::from_safe_cast(params.len); + let data = self.as_slice().get(start..end).ok_or(EINVAL)?; + + let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?; + + Ok::<_, Error>(FalconPioImemLoadTarget { + data, + dst_start, + secure, + start_tag: dst_start >> 8, + }) + }; + + let imem_sec = new_pio_imem(self.imem_sec_load_params(), true)?; + + let imem_ns = if let Some(params) = self.imem_ns_load_params() { + Some(new_pio_imem(params, false)?) + } else { + None + }; + + let dmem = { + let params = self.dmem_load_params(); + let start = usize::from_safe_cast(params.src_start); + let end = start + usize::from_safe_cast(params.len); + let data = self.as_slice().get(start..end).ok_or(EINVAL)?; + + let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?; + + FalconPioDmemLoadTarget { data, dst_start } + }; + + Ok(FalconDmaFirmwarePioAdapter { + fw: self, + imem_sec, + imem_ns, + dmem, + }) + } +} + +/// Represents a portion of the firmware to be loaded into IMEM using PIO. +#[derive(Clone)] +pub(crate) struct FalconPioImemLoadTarget<'a> { + pub(crate) data: &'a [u8], + pub(crate) dst_start: u16, + pub(crate) secure: bool, + pub(crate) start_tag: u16, +} + +/// Represents a portion of the firmware to be loaded into DMEM using PIO. +#[derive(Clone)] +pub(crate) struct FalconPioDmemLoadTarget<'a> { + pub(crate) data: &'a [u8], + pub(crate) dst_start: u16, +} + +/// Trait for providing PIO load parameters of falcon firmwares. +pub(crate) trait FalconPioLoadable { + /// Returns the load parameters for Secure `IMEM`, if any. + fn imem_sec_load_params(&self) -> Option>; + + /// Returns the load parameters for Non-Secure `IMEM`, if any. + fn imem_ns_load_params(&self) -> Option>; + + /// Returns the load parameters for `DMEM`. + fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_>; +} + +/// Adapter type that makes any DMA-loadable firmware also loadable via PIO. +/// +/// Created using [`FalconDmaLoadable::try_as_pio_loadable`]. +pub(crate) struct FalconDmaFirmwarePioAdapter<'a, T: FalconDmaLoadable + ?Sized> { + /// Reference to the DMA firmware. + fw: &'a T, + /// Validated secure IMEM parameters. + imem_sec: FalconPioImemLoadTarget<'a>, + /// Validated non-secure IMEM parameters. + imem_ns: Option>, + /// Validated DMEM parameters. + dmem: FalconPioDmemLoadTarget<'a>, +} + +impl<'a, T> FalconPioLoadable for FalconDmaFirmwarePioAdapter<'a, T> +where + T: FalconDmaLoadable + ?Sized, +{ + fn imem_sec_load_params(&self) -> Option> { + Some(self.imem_sec.clone()) + } + + fn imem_ns_load_params(&self) -> Option> { + self.imem_ns.clone() + } + + fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> { + self.dmem.clone() + } +} + +impl<'a, T> FalconFirmware for FalconDmaFirmwarePioAdapter<'a, T> +where + T: FalconDmaLoadable + FalconFirmware + ?Sized, +{ + type Target = ::Target; + + fn brom_params(&self) -> FalconBromParams { + self.fw.brom_params() + } + + fn boot_addr(&self) -> u32 { + self.fw.boot_addr() + } } /// Trait for a falcon firmware. @@ -417,6 +538,98 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result { Ok(()) } + /// Falcons supports up to four ports, but we only ever use one, so just hard-code it. + const PIO_PORT: usize = 0; + + /// Write a slice to Falcon IMEM memory using programmed I/O (PIO). + /// + /// Returns `EINVAL` if `img.len()` is not a multiple of 4. + fn pio_wr_imem_slice(&self, bar: &Bar0, load_offsets: FalconPioImemLoadTarget<'_>) -> Result { + // Rejecting misaligned images here allows us to avoid checking + // inside the loops. + if load_offsets.data.len() % 4 != 0 { + return Err(EINVAL); + } + + regs::NV_PFALCON_FALCON_IMEMC::default() + .set_secure(load_offsets.secure) + .set_aincw(true) + .set_offs(load_offsets.dst_start) + .write(bar, &E::ID, Self::PIO_PORT); + + for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() { + let n = u16::try_from(n)?; + let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?; + regs::NV_PFALCON_FALCON_IMEMT::default().set_tag(tag).write( + bar, + &E::ID, + Self::PIO_PORT, + ); + for word in block.chunks_exact(4) { + let w = [word[0], word[1], word[2], word[3]]; + regs::NV_PFALCON_FALCON_IMEMD::default() + .set_data(u32::from_le_bytes(w)) + .write(bar, &E::ID, Self::PIO_PORT); + } + } + + Ok(()) + } + + /// Write a slice to Falcon DMEM memory using programmed I/O (PIO). + /// + /// Returns `EINVAL` if `img.len()` is not a multiple of 4. + fn pio_wr_dmem_slice(&self, bar: &Bar0, load_offsets: FalconPioDmemLoadTarget<'_>) -> Result { + // Rejecting misaligned images here allows us to avoid checking + // inside the loops. + if load_offsets.data.len() % 4 != 0 { + return Err(EINVAL); + } + + regs::NV_PFALCON_FALCON_DMEMC::default() + .set_aincw(true) + .set_offs(load_offsets.dst_start) + .write(bar, &E::ID, Self::PIO_PORT); + + for word in load_offsets.data.chunks_exact(4) { + let w = [word[0], word[1], word[2], word[3]]; + regs::NV_PFALCON_FALCON_DMEMD::default() + .set_data(u32::from_le_bytes(w)) + .write(bar, &E::ID, Self::PIO_PORT); + } + + Ok(()) + } + + /// Perform a PIO copy into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. + pub(crate) fn pio_load + FalconPioLoadable>( + &self, + bar: &Bar0, + fw: &F, + ) -> Result { + regs::NV_PFALCON_FBIF_CTL::read(bar, &E::ID) + .set_allow_phys_no_ctx(true) + .write(bar, &E::ID); + + regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); + + if let Some(imem_ns) = fw.imem_ns_load_params() { + self.pio_wr_imem_slice(bar, imem_ns)?; + } + if let Some(imem_sec) = fw.imem_sec_load_params() { + self.pio_wr_imem_slice(bar, imem_sec)?; + } + self.pio_wr_dmem_slice(bar, fw.dmem_load_params())?; + + self.hal.program_brom(self, bar, &fw.brom_params())?; + + regs::NV_PFALCON_FALCON_BOOTVEC::default() + .set_value(fw.boot_addr()) + .write(bar, &E::ID); + + Ok(()) + } + /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's /// `target_mem`. /// @@ -659,7 +872,8 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { self.hal.is_riscv_active(bar) } - // Load a firmware image into Falcon memory + /// Load a firmware image into Falcon memory, using the preferred method for the current + /// chipset. pub(crate) fn load + FalconDmaLoadable>( &self, dev: &Device, @@ -668,7 +882,7 @@ pub(crate) fn load + FalconDmaLoadable>( ) -> Result { match self.hal.load_method() { LoadMethod::Dma => self.dma_load(dev, bar, fw), - LoadMethod::Pio => Err(ENOTSUPP), + LoadMethod::Pio => self.pio_load(bar, &fw.try_as_pio_loadable()?), } } diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs index 89babd5f9325..a7e5ea8d0272 100644 --- a/drivers/gpu/nova-core/falcon/hal.rs +++ b/drivers/gpu/nova-core/falcon/hal.rs @@ -58,7 +58,11 @@ fn signature_reg_fuse_version( /// Reset the falcon engine. fn reset_eng(&self, bar: &Bar0) -> Result; - /// returns the method needed to load data into Falcon memory + /// Returns the method used to load data into the falcon's memory. + /// + /// The only chipsets supporting PIO are those < GA102, and PIO is the preferred method for + /// these. For anything above, the PIO registers appear to be masked to the CPU, so DMA is the + /// only usable method. fn load_method(&self) -> LoadMethod; } diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index ea0d32f5396c..53f412f0ca32 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -364,6 +364,36 @@ pub(crate) fn with_falcon_mem(self, mem: FalconMem) -> Self { 1:1 startcpu as bool; }); +// IMEM access control register. Up to 4 ports are available for IMEM access. +register!(NV_PFALCON_FALCON_IMEMC @ PFalconBase[0x00000180[4; 16]] { + 15:0 offs as u16, "IMEM block and word offset"; + 24:24 aincw as bool, "Auto-increment on write"; + 28:28 secure as bool, "Access secure IMEM"; +}); + +// IMEM data register. Reading/writing this register accesses IMEM at the address +// specified by the corresponding IMEMC register. +register!(NV_PFALCON_FALCON_IMEMD @ PFalconBase[0x00000184[4; 16]] { + 31:0 data as u32; +}); + +// IMEM tag register. Used to set the tag for the current IMEM block. +register!(NV_PFALCON_FALCON_IMEMT @ PFalconBase[0x00000188[4; 16]] { + 15:0 tag as u16; +}); + +// DMEM access control register. Up to 8 ports are available for DMEM access. +register!(NV_PFALCON_FALCON_DMEMC @ PFalconBase[0x000001c0[8; 8]] { + 15:0 offs as u16, "DMEM block and word offset"; + 24:24 aincw as bool, "Auto-increment on write"; +}); + +// DMEM data register. Reading/writing this register accesses DMEM at the address +// specified by the corresponding DMEMC register. +register!(NV_PFALCON_FALCON_DMEMD @ PFalconBase[0x000001c4[8; 8]] { + 31:0 data as u32; +}); + // Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE` depending on the falcon // instance. register!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] { From 192125e0909e106ae37c2447ec43ee2653909d17 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:44 +0900 Subject: [PATCH 064/712] gpu: nova-core: falcon: remove unwarranted safety check in dma_load This safety check was an assumption based on the firmwares we work with - it is not based on an actual hardware limitation. Thus, remove it. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-7-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 2168ef2c5148..7097a206ec3c 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -740,13 +740,6 @@ fn dma_load + FalconDmaLoadable>( bar: &Bar0, fw: &F, ) -> Result { - // The Non-Secure section only exists on firmware used by Turing and GA100, and - // those platforms do not use DMA. - if fw.imem_ns_load_params().is_some() { - debug_assert!(false); - return Err(EINVAL); - } - // Create DMA object with firmware content as the source of the DMA engine. let dma_obj = DmaObject::from_data(dev, fw.as_slice())?; From 349b6dbca0acd8a6a27969f712227c36d681b1d0 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:47 +0900 Subject: [PATCH 065/712] gpu: nova-core: make Chipset::arch() const We will use this method from const context. Also take `self` by value since it is the size of a primitive type and implements `Copy`. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-10-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 60c85fffaeaf..c14d411c6759 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -92,7 +92,7 @@ fn try_from(value: u32) -> Result { }); impl Chipset { - pub(crate) fn arch(&self) -> Architecture { + pub(crate) const fn arch(self) -> Architecture { match self { Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => { Architecture::Turing From e92241683a2a28ec224f2b99fcac56f2c46750ab Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:48 +0900 Subject: [PATCH 066/712] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder Turing GPUs need an additional firmware file (the FWSEC generic bootloader) in order to initialize. Add it to `ModInfoBuilder`. Reviewed-by: Eliot Courtney Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-11-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 21 +++++++++++++++------ drivers/gpu/nova-core/gpu.rs | 7 +++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 6d874753fe67..5eaa63ee3dfc 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -417,11 +417,20 @@ const fn make_entry_file(self, chipset: &str, fw: &str) -> Self { ) } - const fn make_entry_chipset(self, chipset: &str) -> Self { - self.make_entry_file(chipset, "booter_load") - .make_entry_file(chipset, "booter_unload") - .make_entry_file(chipset, "bootloader") - .make_entry_file(chipset, "gsp") + const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self { + let name = chipset.name(); + + let this = self + .make_entry_file(name, "booter_load") + .make_entry_file(name, "booter_unload") + .make_entry_file(name, "bootloader") + .make_entry_file(name, "gsp"); + + if chipset.needs_fwsec_bootloader() { + this.make_entry_file(name, "gen_bootloader") + } else { + this + } } pub(crate) const fn create( @@ -431,7 +440,7 @@ pub(crate) const fn create( let mut i = 0; while i < gpu::Chipset::ALL.len() { - this = this.make_entry_chipset(gpu::Chipset::ALL[i].name()); + this = this.make_entry_chipset(gpu::Chipset::ALL[i]); i += 1; } diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index c14d411c6759..8579d632e717 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -105,6 +105,13 @@ pub(crate) const fn arch(self) -> Architecture { } } } + + /// Returns `true` if this chipset requires the PIO-loaded bootloader in order to boot FWSEC. + /// + /// This includes all chipsets < GA102. + pub(crate) const fn needs_fwsec_bootloader(self) -> bool { + matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100) + } } // TODO From 50b3e0c7c82f32e6ac3ead30f0e0ba96d36a4ff6 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 6 Mar 2026 13:52:49 +0900 Subject: [PATCH 067/712] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing On Turing and GA100, a new firmware image called the Generic Bootloader (gen_bootloader) must be used to load FWSEC into Falcon memory. The driver loads the generic bootloader into Falcon IMEM, passes a descriptor that points to FWSEC using DMEM, and then boots the generic bootloader. The bootloader will then load FWSEC into IMEM and boot it. Signed-off-by: Timur Tabi Co-developed-by: Alexandre Courbot Signed-off-by: Alexandre Courbot Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260306-turing_prep-v11-12-8f0042c5d026@nvidia.com --- drivers/gpu/nova-core/firmware/fwsec.rs | 6 + .../nova-core/firmware/fwsec/bootloader.rs | 347 ++++++++++++++++++ drivers/gpu/nova-core/gsp/boot.rs | 15 +- 3 files changed, 365 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/nova-core/firmware/fwsec/bootloader.rs diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index ca51d7c5be13..8810cb49db67 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -10,6 +10,8 @@ //! - The command to be run, as this firmware can perform several tasks ; //! - The ucode signature, so the GSP falcon can run FWSEC in HS mode. +pub(crate) mod bootloader; + use core::marker::PhantomData; use kernel::{ @@ -385,6 +387,10 @@ pub(crate) fn new( } /// Loads the FWSEC firmware into `falcon` and execute it. + /// + /// This must only be called on chipsets that do not need the FWSEC bootloader (i.e., where + /// [`Chipset::needs_fwsec_bootloader()`](crate::gpu::Chipset::needs_fwsec_bootloader) returns + /// `false`). On chipsets that do, use [`bootloader::FwsecFirmwareWithBl`] instead. pub(crate) fn run( &self, dev: &Device, diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs new file mode 100644 index 000000000000..342dba59b2f9 --- /dev/null +++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Bootloader support for the FWSEC firmware. +//! +//! On Turing, the FWSEC firmware is not loaded directly, but is instead loaded through a small +//! bootloader program that performs the required DMA operations. This bootloader itself needs to +//! be loaded using PIO. + +use kernel::{ + alloc::KVec, + device::{ + self, + Device, // + }, + prelude::*, + ptr::{ + Alignable, + Alignment, // + }, + sizes, + transmute::{ + AsBytes, + FromBytes, // + }, +}; + +use crate::{ + dma::DmaObject, + driver::Bar0, + falcon::{ + self, + gsp::Gsp, + Falcon, + FalconBromParams, + FalconDmaLoadable, + FalconEngine, + FalconFbifMemType, + FalconFbifTarget, + FalconFirmware, + FalconPioDmemLoadTarget, + FalconPioImemLoadTarget, + FalconPioLoadable, // + }, + firmware::{ + fwsec::FwsecFirmware, + request_firmware, + BinHdr, + FIRMWARE_VERSION, // + }, + gpu::Chipset, + num::FromSafeCast, + regs, +}; + +/// Descriptor used by RM to figure out the requirements of the boot loader. +/// +/// Most of its fields appear to be legacy and carry incorrect values, so they are left unused. +#[repr(C)] +#[derive(Debug, Clone)] +struct BootloaderDesc { + /// Starting tag of bootloader. + start_tag: u32, + /// DMEM load offset - unused here as we always load at offset `0`. + _dmem_load_off: u32, + /// Offset of code section in the image. Unused as there is only one section in the bootloader + /// binary. + _code_off: u32, + /// Size of code section in the image. + code_size: u32, + /// Offset of data section in the image. Unused as we build the data section ourselves. + _data_off: u32, + /// Size of data section in the image. Unused as we build the data section ourselves. + _data_size: u32, +} +// SAFETY: any byte sequence is valid for this struct. +unsafe impl FromBytes for BootloaderDesc {} + +/// Structure used by the boot-loader to load the rest of the code. +/// +/// This has to be filled by the GPU driver and copied into DMEM at offset +/// [`BootloaderDesc.dmem_load_off`]. +#[repr(C, packed)] +#[derive(Debug, Clone)] +struct BootloaderDmemDescV2 { + /// Reserved, should always be first element. + reserved: [u32; 4], + /// 16B signature for secure code, 0s if no secure code. + signature: [u32; 4], + /// DMA context used by the bootloader while loading code/data. + ctx_dma: u32, + /// 256B-aligned physical FB address where code is located. + code_dma_base: u64, + /// Offset from `code_dma_base` where the non-secure code is located. + /// + /// Also used as destination IMEM offset of non-secure code as the DMA firmware object is + /// expected to be a mirror image of its loaded state. + /// + /// Must be multiple of 256. + non_sec_code_off: u32, + /// Size of the non-secure code part. + non_sec_code_size: u32, + /// Offset from `code_dma_base` where the secure code is located (must be multiple of 256). + /// + /// Also used as destination IMEM offset of secure code as the DMA firmware object is expected + /// to be a mirror image of its loaded state. + /// + /// Must be multiple of 256. + sec_code_off: u32, + /// Size of the secure code part. + sec_code_size: u32, + /// Code entry point invoked by the bootloader after code is loaded. + code_entry_point: u32, + /// 256B-aligned physical FB address where data is located. + data_dma_base: u64, + /// Size of data block (should be multiple of 256B). + data_size: u32, + /// Number of arguments to be passed to the target firmware being loaded. + argc: u32, + /// Arguments to be passed to the target firmware being loaded. + argv: u32, +} +// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. +unsafe impl AsBytes for BootloaderDmemDescV2 {} + +/// Wrapper for [`FwsecFirmware`] that includes the bootloader performing the actual load +/// operation. +pub(crate) struct FwsecFirmwareWithBl { + /// DMA object the bootloader will copy the firmware from. + _firmware_dma: DmaObject, + /// Code of the bootloader to be loaded into non-secure IMEM. + ucode: KVec, + /// Descriptor to be loaded into DMEM for the bootloader to read. + dmem_desc: BootloaderDmemDescV2, + /// Range-validated start offset of the firmware code in IMEM. + imem_dst_start: u16, + /// BROM parameters of the loaded firmware. + brom_params: FalconBromParams, + /// Range-validated `desc.start_tag`. + start_tag: u16, +} + +impl FwsecFirmwareWithBl { + /// Loads the bootloader firmware for `dev` and `chipset`, and wrap `firmware` so it can be + /// loaded using it. + pub(crate) fn new( + firmware: FwsecFirmware, + dev: &Device, + chipset: Chipset, + ) -> Result { + let fw = request_firmware(dev, chipset, "gen_bootloader", FIRMWARE_VERSION)?; + let hdr = fw + .data() + .get(0..size_of::()) + .and_then(BinHdr::from_bytes_copy) + .ok_or(EINVAL)?; + + let desc = { + let desc_offset = usize::from_safe_cast(hdr.header_offset); + + fw.data() + .get(desc_offset..) + .and_then(BootloaderDesc::from_bytes_copy_prefix) + .ok_or(EINVAL)? + .0 + }; + + let ucode = { + let ucode_start = usize::from_safe_cast(hdr.data_offset); + let code_size = usize::from_safe_cast(desc.code_size); + // Align to falcon block size (256 bytes). + let aligned_code_size = code_size + .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>()) + .ok_or(EINVAL)?; + + let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?; + ucode.extend_from_slice( + fw.data() + .get(ucode_start..ucode_start + code_size) + .ok_or(EINVAL)?, + GFP_KERNEL, + )?; + ucode.resize(aligned_code_size, 0, GFP_KERNEL)?; + + ucode + }; + + // `BootloaderDmemDescV2` expects the source to be a mirror image of the destination and + // uses the same offset parameter for both. + // + // Thus, the start of the source object needs to be padded with the difference between the + // destination and source offsets. + // + // In practice, this is expected to always be zero but is required for code correctness. + let (align_padding, firmware_dma) = { + let align_padding = { + let imem_sec = firmware.imem_sec_load_params(); + + imem_sec + .dst_start + .checked_sub(imem_sec.src_start) + .map(usize::from_safe_cast) + .ok_or(EOVERFLOW)? + }; + + let mut firmware_obj = KVVec::new(); + firmware_obj.extend_with(align_padding, 0u8, GFP_KERNEL)?; + firmware_obj.extend_from_slice(firmware.ucode.0.as_slice(), GFP_KERNEL)?; + + ( + align_padding, + DmaObject::from_data(dev, firmware_obj.as_slice())?, + ) + }; + + let dmem_desc = { + // Bootloader payload is in non-coherent system memory. + const FALCON_DMAIDX_PHYS_SYS_NCOH: u32 = 4; + + let imem_sec = firmware.imem_sec_load_params(); + let imem_ns = firmware.imem_ns_load_params().ok_or(EINVAL)?; + let dmem = firmware.dmem_load_params(); + + // The bootloader does not have a data destination offset field and copies the data at + // the start of DMEM, so it can only be used if the destination offset of the firmware + // is 0. + if dmem.dst_start != 0 { + return Err(EINVAL); + } + + BootloaderDmemDescV2 { + reserved: [0; 4], + signature: [0; 4], + ctx_dma: FALCON_DMAIDX_PHYS_SYS_NCOH, + code_dma_base: firmware_dma.dma_handle(), + // `dst_start` is also valid as the source offset since the firmware DMA object is + // a mirror image of the target IMEM layout. + non_sec_code_off: imem_ns.dst_start, + non_sec_code_size: imem_ns.len, + // `dst_start` is also valid as the source offset since the firmware DMA object is + // a mirror image of the target IMEM layout. + sec_code_off: imem_sec.dst_start, + sec_code_size: imem_sec.len, + code_entry_point: 0, + // Start of data section is the added padding + the DMEM `src_start` field. + data_dma_base: firmware_dma + .dma_handle() + .checked_add(u64::from_safe_cast(align_padding)) + .and_then(|offset| offset.checked_add(dmem.src_start.into())) + .ok_or(EOVERFLOW)?, + data_size: dmem.len, + argc: 0, + argv: 0, + } + }; + + // The bootloader's code must be loaded in the area right below the first 64K of IMEM. + const BOOTLOADER_LOAD_CEILING: usize = sizes::SZ_64K; + let imem_dst_start = BOOTLOADER_LOAD_CEILING + .checked_sub(ucode.len()) + .ok_or(EOVERFLOW)?; + + Ok(Self { + _firmware_dma: firmware_dma, + ucode, + dmem_desc, + brom_params: firmware.brom_params(), + imem_dst_start: u16::try_from(imem_dst_start)?, + start_tag: u16::try_from(desc.start_tag)?, + }) + } + + /// Loads the bootloader into `falcon` and execute it. + /// + /// The bootloader will load the FWSEC firmware and then execute it. This function returns + /// after FWSEC has reached completion. + pub(crate) fn run( + &self, + dev: &Device, + falcon: &Falcon, + bar: &Bar0, + ) -> Result<()> { + // Reset falcon, load the firmware, and run it. + falcon + .reset(bar) + .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?; + falcon + .pio_load(bar, self) + .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; + + // Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory. + regs::NV_PFALCON_FBIF_TRANSCFG::try_update( + bar, + &Gsp::ID, + usize::from_safe_cast(self.dmem_desc.ctx_dma), + |v| { + v.set_target(FalconFbifTarget::CoherentSysmem) + .set_mem_type(FalconFbifMemType::Physical) + }, + )?; + + let (mbox0, _) = falcon + .boot(bar, Some(0), None) + .inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?; + if mbox0 != 0 { + dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0); + Err(EIO) + } else { + Ok(()) + } + } +} + +impl FalconFirmware for FwsecFirmwareWithBl { + type Target = Gsp; + + fn brom_params(&self) -> FalconBromParams { + self.brom_params.clone() + } + + fn boot_addr(&self) -> u32 { + // On V2 platforms, the boot address is extracted from the generic bootloader, because the + // gbl is what actually copies FWSEC into memory, so that is what needs to be booted. + u32::from(self.start_tag) << 8 + } +} + +impl FalconPioLoadable for FwsecFirmwareWithBl { + fn imem_sec_load_params(&self) -> Option> { + None + } + + fn imem_ns_load_params(&self) -> Option> { + Some(FalconPioImemLoadTarget { + data: self.ucode.as_ref(), + dst_start: self.imem_dst_start, + secure: false, + start_tag: self.start_tag, + }) + } + + fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> { + FalconPioDmemLoadTarget { + data: self.dmem_desc.as_bytes(), + dst_start: 0, + } + } +} diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 78957ed8814f..9a00ddb922ac 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -24,6 +24,7 @@ BooterKind, // }, fwsec::{ + bootloader::FwsecFirmwareWithBl, FwsecCommand, FwsecFirmware, // }, @@ -48,6 +49,7 @@ impl super::Gsp { /// created the WPR2 region. fn run_fwsec_frts( dev: &device::Device, + chipset: Chipset, falcon: &Falcon, bar: &Bar0, bios: &Vbios, @@ -63,6 +65,7 @@ fn run_fwsec_frts( return Err(EBUSY); } + // FWSEC-FRTS will create the WPR2 region. let fwsec_frts = FwsecFirmware::new( dev, falcon, @@ -74,8 +77,14 @@ fn run_fwsec_frts( }, )?; - // Run FWSEC-FRTS to create the WPR2 region. - fwsec_frts.run(dev, falcon, bar)?; + if chipset.needs_fwsec_bootloader() { + let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?; + // Load and run the bootloader, which will load FWSEC-FRTS and run it. + fwsec_frts_bl.run(dev, falcon, bar)?; + } else { + // Load and run FWSEC-FRTS directly. + fwsec_frts.run(dev, falcon, bar)?; + } // SCRATCH_E contains the error code for FWSEC-FRTS. let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code(); @@ -144,7 +153,7 @@ pub(crate) fn boot( let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?; dev_dbg!(dev, "{:#x?}\n", fb_layout); - Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?; + Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?; let booter_loader = BooterFirmware::new( dev, From 6ef5141114a95746731a65bc384ff4b1c071a3f2 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:45 +0900 Subject: [PATCH 068/712] gpu: nova-core: firmware: add comments to justify v3 header values There is no member in `FalconUCodeDescV3` to describe the start offsets of the IMEM and DMEM section in the firmware object. Add comments to justify how they are computed. Reviewed-by: Eliot Courtney Link: https://patch.msgid.link/20260306-turing_prep-v11-8-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 5eaa63ee3dfc..fff5fa263c26 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -260,6 +260,7 @@ fn signature_versions(&self) -> u16 { fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { FalconDmaLoadTarget { + // IMEM segment always starts at offset 0. src_start: 0, dst_start: self.imem_phys_base, len: self.imem_load_size, @@ -273,6 +274,7 @@ fn imem_ns_load_params(&self) -> Option { fn dmem_load_params(&self) -> FalconDmaLoadTarget { FalconDmaLoadTarget { + // DMEM segment starts right after the IMEM one. src_start: self.imem_load_size, dst_start: self.dmem_phys_base, len: self.dmem_load_size, From 17d7c97f73c7a0bd90bd22cd7441269a6f8a1d72 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Mar 2026 13:52:46 +0900 Subject: [PATCH 069/712] gpu: nova-core: firmware: fix and explain v2 header offsets computations There are no offsets in `FalconUCodeDescV2` to give the non-secure and secure IMEM sections start offsets relative to the beginning of the firmware object. The start offsets for both sections were set to `0`, but that is obviously incorrect since two different sections cannot start at the same offset. Since these offsets were not used by the bootloader, this doesn't prevent proper function but is incorrect nonetheless. Fix this by computing the start of the secure IMEM section relatively to the start of the firmware object and setting it properly. Also add and improve comments to explain how the values are obtained. Fixes: dbfb5aa41f16 ("gpu: nova-core: add FalconUCodeDescV2 support") Reviewed-by: Eliot Courtney Link: https://patch.msgid.link/20260306-turing_prep-v11-9-8f0042c5d026@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index fff5fa263c26..2bb20081befd 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -63,7 +63,8 @@ pub(crate) struct FalconUCodeDescV2 { pub(crate) interface_offset: u32, /// Base address at which to load the code segment into 'IMEM'. pub(crate) imem_phys_base: u32, - /// Size in bytes of the code to copy into 'IMEM'. + /// Size in bytes of the code to copy into 'IMEM' (includes both secure and non-secure + /// segments). pub(crate) imem_load_size: u32, /// Virtual 'IMEM' address (i.e. 'tag') at which the code should start. pub(crate) imem_virt_base: u32, @@ -205,18 +206,25 @@ fn signature_versions(&self) -> u16 { } fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { + // `imem_sec_base` is the *virtual* start address of the secure IMEM segment, so subtract + // `imem_virt_base` to get its physical offset. + let imem_sec_start = self.imem_sec_base.saturating_sub(self.imem_virt_base); + FalconDmaLoadTarget { - src_start: 0, - dst_start: self.imem_sec_base, + src_start: imem_sec_start, + dst_start: self.imem_phys_base.saturating_add(imem_sec_start), len: self.imem_sec_size, } } fn imem_ns_load_params(&self) -> Option { Some(FalconDmaLoadTarget { + // Non-secure code always starts at offset 0. src_start: 0, dst_start: self.imem_phys_base, - len: self.imem_load_size.checked_sub(self.imem_sec_size)?, + // `imem_load_size` includes the size of the secure segment, so subtract it to + // get the correct amount of data to copy. + len: self.imem_load_size.saturating_sub(self.imem_sec_size), }) } From dd8a93dafe6ef50b49d2a7b44862264d74a7aafa Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Mon, 2 Mar 2026 12:23:31 -0800 Subject: [PATCH 070/712] drm/tyr: Use DRM device type alias across driver Currently Tyr defines a convenience type alias for its DRM device type, `TyrDrmDevice` but it does not use the alias outside of `tyr/driver.rs`. Replace `drm::Device` with the alias `TyrDrmDevice` across the driver. This change will ease future upstream Tyr development by reducing the diffs when multiple series are touching these files. No functional changes are intended. Signed-off-by: Deborah Brouwer Reviewed-by: Boris Brezillon Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260302202331.176140-1-deborah.brouwer@collabora.com Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/file.rs | 7 +++++-- drivers/gpu/drm/tyr/gem.rs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/tyr/file.rs b/drivers/gpu/drm/tyr/file.rs index 450be5ab9aaf..31411da203c5 100644 --- a/drivers/gpu/drm/tyr/file.rs +++ b/drivers/gpu/drm/tyr/file.rs @@ -7,7 +7,10 @@ uapi, // }; -use crate::driver::TyrDrmDriver; +use crate::driver::{ + TyrDrmDevice, + TyrDrmDriver, // +}; #[pin_data] pub(crate) struct TyrDrmFileData {} @@ -25,7 +28,7 @@ fn open(_dev: &drm::Device) -> Result>> { impl TyrDrmFileData { pub(crate) fn dev_query( - ddev: &drm::Device, + ddev: &TyrDrmDevice, devquery: &mut uapi::drm_panthor_dev_query, _file: &TyrDrmFile, ) -> Result { diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index 514524ae07ef..5cd0cd9585e8 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -5,7 +5,10 @@ prelude::*, // }; -use crate::driver::TyrDrmDriver; +use crate::driver::{ + TyrDrmDevice, + TyrDrmDriver, // +}; /// GEM Object inner driver data #[pin_data] @@ -14,7 +17,7 @@ pub(crate) struct TyrObject {} impl gem::DriverObject for TyrObject { type Driver = TyrDrmDriver; - fn new(_dev: &kernel::drm::Device, _size: usize) -> impl PinInit { + fn new(_dev: &TyrDrmDevice, _size: usize) -> impl PinInit { try_pin_init!(TyrObject {}) } } From 73a505dc48144ec72e25874e2b2a72487b02d3bc Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 9 Mar 2026 10:39:49 +0100 Subject: [PATCH 071/712] thunderbolt: Fix property read in nhi_wake_supported() device_property_read_foo() returns 0 on success and only then modifies 'val'. Currently, val is left uninitialized if the aforementioned function returns non-zero, making nhi_wake_supported() return true almost always (random != 0) if the property is not present in device firmware. Invert the check to make it make sense. Fixes: 3cdb9446a117 ("thunderbolt: Add support for Intel Ice Lake") Cc: stable@vger.kernel.org Signed-off-by: Konrad Dybcio Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index ccce020a2432..2bb2e79ca3cb 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1020,7 +1020,7 @@ static bool nhi_wake_supported(struct pci_dev *pdev) * If power rails are sustainable for wakeup from S4 this * property is set by the BIOS. */ - if (device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val)) + if (!device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val)) return !!val; return true; From 9a3e455927f3d7f06f445897626360220cf6a27b Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:21:58 +0900 Subject: [PATCH 072/712] gpu: nova-core: gsp: sort `MsgFunction` variants alphabetically There is no particular order required here and keeping them alphabetical will help preventing future mistakes. Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-1-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/fw.rs | 67 +++++++++++++++++---------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index f1797e1f0d9d..4b998485360b 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -191,34 +191,34 @@ pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self { #[repr(u32)] pub(crate) enum MsgFunction { // Common function codes - Nop = bindings::NV_VGPU_MSG_FUNCTION_NOP, - SetGuestSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO, - AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT, + AllocChannelDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA, + AllocCtxDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA, AllocDevice = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE, AllocMemory = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY, - AllocCtxDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA, - AllocChannelDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA, - MapMemory = bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY, - BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA, AllocObject = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT, + AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT, + BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA, Free = bindings::NV_VGPU_MSG_FUNCTION_FREE, - Log = bindings::NV_VGPU_MSG_FUNCTION_LOG, GetGspStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO, - SetRegistry = bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY, - GspSetSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO, + GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO, GspInitPostObjGpu = bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU, GspRmControl = bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL, - GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO, + GspSetSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO, + Log = bindings::NV_VGPU_MSG_FUNCTION_LOG, + MapMemory = bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY, + Nop = bindings::NV_VGPU_MSG_FUNCTION_NOP, + SetGuestSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO, + SetRegistry = bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY, // Event codes GspInitDone = bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE, + GspLockdownNotice = bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE, + GspPostNoCat = bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD, GspRunCpuSequencer = bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER, - PostEvent = bindings::NV_VGPU_MSG_EVENT_POST_EVENT, - RcTriggered = bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED, MmuFaultQueued = bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED, OsErrorLog = bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG, - GspPostNoCat = bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD, - GspLockdownNotice = bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE, + PostEvent = bindings::NV_VGPU_MSG_EVENT_POST_EVENT, + RcTriggered = bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED, UcodeLibOsPrint = bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT, } @@ -227,38 +227,41 @@ impl TryFrom for MsgFunction { fn try_from(value: u32) -> Result { match value { - bindings::NV_VGPU_MSG_FUNCTION_NOP => Ok(MsgFunction::Nop), - bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => { - Ok(MsgFunction::SetGuestSystemInfo) - } - bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot), + // Common function codes + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => Ok(MsgFunction::AllocChannelDma), + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => Ok(MsgFunction::AllocCtxDma), bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE => Ok(MsgFunction::AllocDevice), bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY => Ok(MsgFunction::AllocMemory), - bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => Ok(MsgFunction::AllocCtxDma), - bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => Ok(MsgFunction::AllocChannelDma), - bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY => Ok(MsgFunction::MapMemory), - bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma), bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => Ok(MsgFunction::AllocObject), + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot), + bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma), bindings::NV_VGPU_MSG_FUNCTION_FREE => Ok(MsgFunction::Free), - bindings::NV_VGPU_MSG_FUNCTION_LOG => Ok(MsgFunction::Log), bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => Ok(MsgFunction::GetGspStaticInfo), - bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY => Ok(MsgFunction::SetRegistry), - bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => Ok(MsgFunction::GspSetSystemInfo), + bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo), bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU => { Ok(MsgFunction::GspInitPostObjGpu) } bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL => Ok(MsgFunction::GspRmControl), - bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo), + bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => Ok(MsgFunction::GspSetSystemInfo), + bindings::NV_VGPU_MSG_FUNCTION_LOG => Ok(MsgFunction::Log), + bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY => Ok(MsgFunction::MapMemory), + bindings::NV_VGPU_MSG_FUNCTION_NOP => Ok(MsgFunction::Nop), + bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => { + Ok(MsgFunction::SetGuestSystemInfo) + } + bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY => Ok(MsgFunction::SetRegistry), + + // Event codes bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE => Ok(MsgFunction::GspInitDone), + bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => Ok(MsgFunction::GspLockdownNotice), + bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => Ok(MsgFunction::GspPostNoCat), bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER => { Ok(MsgFunction::GspRunCpuSequencer) } - bindings::NV_VGPU_MSG_EVENT_POST_EVENT => Ok(MsgFunction::PostEvent), - bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED => Ok(MsgFunction::RcTriggered), bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED => Ok(MsgFunction::MmuFaultQueued), bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG => Ok(MsgFunction::OsErrorLog), - bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => Ok(MsgFunction::GspPostNoCat), - bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => Ok(MsgFunction::GspLockdownNotice), + bindings::NV_VGPU_MSG_EVENT_POST_EVENT => Ok(MsgFunction::PostEvent), + bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED => Ok(MsgFunction::RcTriggered), bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT => Ok(MsgFunction::UcodeLibOsPrint), _ => Err(EINVAL), } From b4281ffb80d341c2c7cf0343784ec77dbd7f9189 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:21:59 +0900 Subject: [PATCH 073/712] gpu: nova-core: gsp: add mechanism to wait for space on command queue Add a timeout to `allocate_command` which waits for space on the GSP command queue. It uses a similar timeout to nouveau. This lets `send_command` wait for space to free up in the command queue. This is required to support continuation records which can fill up the queue. Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-2-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 42 ++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 87dbbd6d1be9..12849bc057f2 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -250,6 +250,19 @@ fn new(dev: &device::Device) -> Result { } } + /// Returns the size of the region of the CPU message queue that the driver is currently allowed + /// to write to, in bytes. + fn driver_write_area_size(&self) -> usize { + let tx = self.cpu_write_ptr(); + let rx = self.gsp_read_ptr(); + + // `rx` and `tx` are both in `0..MSGQ_NUM_PAGES` per the invariants of `gsp_read_ptr` and + // `cpu_write_ptr`. The minimum value case is where `rx == 0` and `tx == MSGQ_NUM_PAGES - + // 1`, which gives `0 + MSGQ_NUM_PAGES - (MSGQ_NUM_PAGES - 1) - 1 == 0`. + let slots = (rx + MSGQ_NUM_PAGES - tx - 1) % MSGQ_NUM_PAGES; + num::u32_as_usize(slots) * GSP_PAGE_SIZE + } + /// Returns the region of the GSP message queue that the driver is currently allowed to read /// from. /// @@ -281,15 +294,22 @@ fn new(dev: &device::Device) -> Result { } /// Allocates a region on the command queue that is large enough to send a command of `size` - /// bytes. + /// bytes, waiting for space to become available based on the provided timeout. /// /// This returns a [`GspCommand`] ready to be written to by the caller. /// /// # Errors /// - /// - `EAGAIN` if the driver area is too small to hold the requested command. + /// - `ETIMEDOUT` if space does not become available within the timeout. /// - `EIO` if the command header is not properly aligned. - fn allocate_command(&mut self, size: usize) -> Result> { + fn allocate_command(&mut self, size: usize, timeout: Delta) -> Result> { + read_poll_timeout( + || Ok(self.driver_write_area_size()), + |available_bytes| *available_bytes >= size_of::() + size, + Delta::from_micros(1), + timeout, + )?; + // Get the current writable area as an array of bytes. let (slice_1, slice_2) = { let (slice_1, slice_2) = self.driver_write_area(); @@ -298,13 +318,6 @@ fn allocate_command(&mut self, size: usize) -> Result> { (slice_1.as_flattened_mut(), slice_2.as_flattened_mut()) }; - // If the GSP is still processing previous messages the shared region - // may be full in which case we will have to retry once the GSP has - // processed the existing commands. - if size_of::() + size > slice_1.len() + slice_2.len() { - return Err(EAGAIN); - } - // Extract area for the `GspMsgElement`. let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?; @@ -462,6 +475,9 @@ impl Cmdq { /// Number of page table entries for the GSP shared region. pub(crate) const NUM_PTES: usize = size_of::() >> GSP_PAGE_SHIFT; + /// Timeout for waiting for space on the command queue. + const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1); + /// Creates a new command queue for `dev`. pub(crate) fn new(dev: &device::Device) -> Result { let gsp_mem = DmaGspMem::new(dev)?; @@ -497,7 +513,7 @@ fn notify_gsp(bar: &Bar0) { /// /// # Errors /// - /// - `EAGAIN` if there was not enough space in the command queue to send the command. + /// - `ETIMEDOUT` if space does not become available within the timeout. /// - `EIO` if the variable payload requested by the command has not been entirely /// written to by its [`CommandToGsp::init_variable_payload`] method. /// @@ -509,7 +525,9 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result Error: From, { let command_size = size_of::() + command.variable_payload_len(); - let dst = self.gsp_mem.allocate_command(command_size)?; + let dst = self + .gsp_mem + .allocate_command(command_size, Self::ALLOCATE_TIMEOUT)?; // Extract area for the command itself. let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?; From 59f237a0d17fb95f60de15386a6ab968af4bd4d4 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:22:00 +0900 Subject: [PATCH 074/712] rust: add EMSGSIZE error code Add the EMSGSIZE error code, which indicates that a message is too long. Tested-by: Zhi Wang Acked-by: Miguel Ojeda Reviewed-by: Gary Guo Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-3-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- rust/kernel/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 258b12afdcba..10fcf1f0404d 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -67,6 +67,7 @@ macro_rules! declare_err { declare_err!(EDOM, "Math argument out of domain of func."); declare_err!(ERANGE, "Math result not representable."); declare_err!(EOVERFLOW, "Value too large for defined data type."); + declare_err!(EMSGSIZE, "Message too long."); declare_err!(ETIMEDOUT, "Connection timed out."); declare_err!(ERESTARTSYS, "Restart the system call."); declare_err!(ERESTARTNOINTR, "System call was interrupted by a signal and will be restarted."); From 41584c71342e6046fc5af0bd7823e6c0c53ffb0c Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:22:01 +0900 Subject: [PATCH 075/712] gpu: nova-core: gsp: add checking oversized commands The limit is 16 pages for a single command sent to the GSP. Return an error if `allocate_command` is called with a too large size. Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-4-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 7 ++++++- drivers/gpu/nova-core/gsp/fw.rs | 4 ++++ drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 12849bc057f2..8b970523d789 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -32,7 +32,8 @@ GspMsgElement, MsgFunction, MsgqRxHeader, - MsgqTxHeader, // + MsgqTxHeader, + GSP_MSG_QUEUE_ELEMENT_SIZE_MAX, // }, PteArray, GSP_PAGE_SHIFT, @@ -300,9 +301,13 @@ fn driver_write_area_size(&self) -> usize { /// /// # Errors /// + /// - `EMSGSIZE` if the command is larger than [`GSP_MSG_QUEUE_ELEMENT_SIZE_MAX`]. /// - `ETIMEDOUT` if space does not become available within the timeout. /// - `EIO` if the command header is not properly aligned. fn allocate_command(&mut self, size: usize, timeout: Delta) -> Result> { + if size_of::() + size > GSP_MSG_QUEUE_ELEMENT_SIZE_MAX { + return Err(EMSGSIZE); + } read_poll_timeout( || Ok(self.driver_write_area_size()), |available_bytes| *available_bytes >= size_of::() + size, diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 4b998485360b..6005362450cb 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -39,6 +39,10 @@ }, }; +/// Maximum size of a single GSP message queue element in bytes. +pub(crate) const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: usize = + num::u32_as_usize(bindings::GSP_MSG_QUEUE_ELEMENT_SIZE_MAX); + /// Empty type to group methods related to heap parameters for running the GSP firmware. enum GspFwHeapParams {} diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs index 6d25fe0bffa9..334e8be5fde8 100644 --- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs +++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs @@ -43,6 +43,7 @@ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { pub const GSP_FW_WPR_META_REVISION: u32 = 1; pub const GSP_FW_WPR_META_MAGIC: i64 = -2577556379034558285; pub const REGISTRY_TABLE_ENTRY_TYPE_DWORD: u32 = 1; +pub const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: u32 = 65536; pub type __u8 = ffi::c_uchar; pub type __u16 = ffi::c_ushort; pub type __u32 = ffi::c_uint; From 1a0d4bc62b5d36a8ae4dca4413c2703b5fdd93f4 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:22:02 +0900 Subject: [PATCH 076/712] gpu: nova-core: gsp: clarify invariant on command queue Clarify why using only the first returned slice from allocate_command for the message headers is okay. Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-5-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 8b970523d789..806b1e02715e 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -534,7 +534,9 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result .gsp_mem .allocate_command(command_size, Self::ALLOCATE_TIMEOUT)?; - // Extract area for the command itself. + // Extract area for the command itself. The GSP message header and the command header + // together are guaranteed to fit entirely into a single page, so it's ok to only look + // at `dst.contents.0` here. let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?; // Fill the header and command in-place. From dcf1fdafe04095947f08db5a45d1994aa1d948fa Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:22:03 +0900 Subject: [PATCH 077/712] gpu: nova-core: gsp: unconditionally call variable payload handling Unconditionally call the variable length payload code, which is a no-op if there is no such payload but could defensively catch some coding errors by e.g. checking that the allocated size is completely filled. Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-6-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 806b1e02715e..b41a866e24da 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -548,16 +548,14 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result command.init().__init(core::ptr::from_mut(cmd))?; } - // Fill the variable-length payload. - if command_size > size_of::() { - let mut sbuffer = - SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]); - command.init_variable_payload(&mut sbuffer)?; + // Fill the variable-length payload, which may be empty. + let mut sbuffer = SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]); + command.init_variable_payload(&mut sbuffer)?; - if !sbuffer.is_empty() { - return Err(EIO); - } + if !sbuffer.is_empty() { + return Err(EIO); } + drop(sbuffer); // Compute checksum now that the whole message is ready. dst.header From adcb40c5fcf085b16327ab1eef11ec157c9f603b Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:22:04 +0900 Subject: [PATCH 078/712] gpu: nova-core: gsp: add `size` helper to `CommandToGsp` Add a default method to `CommandToGsp` which computes the size of a command. Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-7-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index b41a866e24da..861f5666fe7f 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -94,6 +94,12 @@ fn init_variable_payload( ) -> Result { Ok(()) } + + /// Total size of the command (including its variable-length payload) without the + /// [`GspMsgElement`] header. + fn size(&self) -> usize { + size_of::() + self.variable_payload_len() + } } /// Trait representing messages received from the GSP. @@ -529,10 +535,10 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result // This allows all error types, including `Infallible`, to be used for `M::InitError`. Error: From, { - let command_size = size_of::() + command.variable_payload_len(); + let size_in_bytes = command.size(); let dst = self .gsp_mem - .allocate_command(command_size, Self::ALLOCATE_TIMEOUT)?; + .allocate_command(size_in_bytes, Self::ALLOCATE_TIMEOUT)?; // Extract area for the command itself. The GSP message header and the command header // together are guaranteed to fit entirely into a single page, so it's ok to only look @@ -540,7 +546,7 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?; // Fill the header and command in-place. - let msg_element = GspMsgElement::init(self.seq, command_size, M::FUNCTION); + let msg_element = GspMsgElement::init(self.seq, size_in_bytes, M::FUNCTION); // SAFETY: `msg_header` and `cmd` are valid references, and not touched if the initializer // fails. unsafe { From e8f4f9ae86a4636c16cf90208c5794e92090bd6b Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:22:05 +0900 Subject: [PATCH 079/712] gpu: nova-core: gsp: support large RPCs via continuation record Splits large RPCs if necessary and sends the remaining parts using continuation records. RPCs that do not need continuation records continue to write directly into the command buffer. Ones that do write into a staging buffer first, so there is one copy. Continuation record for receive is not necessary to support at the moment because those replies do not need to be read and are currently drained by retrying `receive_msg` on `ERANGE`. Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-8-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 42 ++++- .../gpu/nova-core/gsp/cmdq/continuation.rs | 163 ++++++++++++++++++ drivers/gpu/nova-core/gsp/fw.rs | 4 + 3 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/nova-core/gsp/cmdq/continuation.rs diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 861f5666fe7f..e0b096546d23 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 +mod continuation; + use core::{ mem, sync::atomic::{ @@ -25,6 +27,11 @@ }, }; +use continuation::{ + ContinuationRecord, + SplitState, // +}; + use crate::{ driver::Bar0, gsp::{ @@ -520,7 +527,7 @@ fn notify_gsp(bar: &Bar0) { .write(bar); } - /// Sends `command` to the GSP. + /// Sends `command` to the GSP, without splitting it. /// /// # Errors /// @@ -529,7 +536,7 @@ fn notify_gsp(bar: &Bar0) { /// written to by its [`CommandToGsp::init_variable_payload`] method. /// /// Error codes returned by the command initializers are propagated as-is. - pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result + fn send_single_command(&mut self, bar: &Bar0, command: M) -> Result where M: CommandToGsp, // This allows all error types, including `Infallible`, to be used for `M::InitError`. @@ -588,6 +595,37 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result Ok(()) } + /// Sends `command` to the GSP. + /// + /// The command may be split into multiple messages if it is large. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available within the timeout. + /// - `EIO` if the variable payload requested by the command has not been entirely + /// written to by its [`CommandToGsp::init_variable_payload`] method. + /// + /// Error codes returned by the command initializers are propagated as-is. + pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp, + Error: From, + { + match SplitState::new(command)? { + SplitState::Single(command) => self.send_single_command(bar, command), + SplitState::Split(command, mut continuations) => { + self.send_single_command(bar, command)?; + + while let Some(continuation) = continuations.next() { + // Turbofish needed because the compiler cannot infer M here. + self.send_single_command::>(bar, continuation)?; + } + + Ok(()) + } + } + } + /// Wait for a message to become available on the message queue. /// /// This works purely at the transport layer and does not interpret or validate the message diff --git a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs new file mode 100644 index 000000000000..67b3e03fd8ea --- /dev/null +++ b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Support for splitting large GSP commands across continuation records. + +use core::convert::Infallible; + +use kernel::prelude::*; + +use super::CommandToGsp; + +use crate::{ + gsp::fw::{ + GspMsgElement, + MsgFunction, + GSP_MSG_QUEUE_ELEMENT_SIZE_MAX, // + }, + sbuffer::SBufferIter, +}; + +/// Maximum command size that fits in a single queue element. +const MAX_CMD_SIZE: usize = GSP_MSG_QUEUE_ELEMENT_SIZE_MAX - size_of::(); + +/// Acts as an iterator over the continuation records for a split command. +pub(super) struct ContinuationRecords { + payload: KVVec, + offset: usize, +} + +impl ContinuationRecords { + /// Creates a new iterator over continuation records for the given payload. + fn new(payload: KVVec) -> Self { + Self { payload, offset: 0 } + } + + /// Returns the next continuation record, or [`None`] if there are no more. + pub(super) fn next(&mut self) -> Option> { + let remaining = self.payload.len() - self.offset; + + if remaining > 0 { + let chunk_size = remaining.min(MAX_CMD_SIZE); + let record = + ContinuationRecord::new(&self.payload[self.offset..(self.offset + chunk_size)]); + self.offset += chunk_size; + Some(record) + } else { + None + } + } +} + +/// The [`ContinuationRecord`] command. +pub(super) struct ContinuationRecord<'a> { + data: &'a [u8], +} + +impl<'a> ContinuationRecord<'a> { + /// Creates a new [`ContinuationRecord`] command with the given data. + fn new(data: &'a [u8]) -> Self { + Self { data } + } +} + +impl<'a> CommandToGsp for ContinuationRecord<'a> { + const FUNCTION: MsgFunction = MsgFunction::ContinuationRecord; + type Command = (); + type InitError = Infallible; + + fn init(&self) -> impl Init { + <()>::init_zeroed() + } + + fn variable_payload_len(&self) -> usize { + self.data.len() + } + + fn init_variable_payload( + &self, + dst: &mut SBufferIter>, + ) -> Result { + dst.write_all(self.data) + } +} + +/// Whether a command needs to be split across continuation records or not. +pub(super) enum SplitState { + /// A command that fits in a single queue element. + Single(C), + /// A command split across continuation records. + Split(SplitCommand, ContinuationRecords), +} + +impl SplitState { + /// Maximum variable payload size that fits in the first command alongside the command header. + const MAX_FIRST_PAYLOAD: usize = MAX_CMD_SIZE - size_of::(); + + /// Creates a new [`SplitState`] for the given command. + /// + /// If the command is too large, it will be split into a main command and some number of + /// continuation records. + pub(super) fn new(command: C) -> Result { + let payload_len = command.variable_payload_len(); + + if command.size() > MAX_CMD_SIZE { + let mut command_payload = + KVVec::::from_elem(0u8, payload_len.min(Self::MAX_FIRST_PAYLOAD), GFP_KERNEL)?; + let mut continuation_payload = + KVVec::::from_elem(0u8, payload_len - command_payload.len(), GFP_KERNEL)?; + let mut sbuffer = SBufferIter::new_writer([ + command_payload.as_mut_slice(), + continuation_payload.as_mut_slice(), + ]); + + command.init_variable_payload(&mut sbuffer)?; + if !sbuffer.is_empty() { + return Err(EIO); + } + drop(sbuffer); + + Ok(Self::Split( + SplitCommand::new(command, command_payload), + ContinuationRecords::new(continuation_payload), + )) + } else { + Ok(Self::Single(command)) + } + } +} + +/// A command that has been truncated to maximum accepted length of the command queue. +/// +/// The remainder of its payload is expected to be sent using [`ContinuationRecords`]. +pub(super) struct SplitCommand { + command: C, + payload: KVVec, +} + +impl SplitCommand { + /// Creates a new [`SplitCommand`] wrapping `command` with the given truncated payload. + fn new(command: C, payload: KVVec) -> Self { + Self { command, payload } + } +} + +impl CommandToGsp for SplitCommand { + const FUNCTION: MsgFunction = C::FUNCTION; + type Command = C::Command; + type InitError = C::InitError; + + fn init(&self) -> impl Init { + self.command.init() + } + + fn variable_payload_len(&self) -> usize { + self.payload.len() + } + + fn init_variable_payload( + &self, + dst: &mut SBufferIter>, + ) -> Result { + dst.write_all(&self.payload) + } +} diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 6005362450cb..25fca1f6db2c 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -202,6 +202,7 @@ pub(crate) enum MsgFunction { AllocObject = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT, AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT, BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA, + ContinuationRecord = bindings::NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD, Free = bindings::NV_VGPU_MSG_FUNCTION_FREE, GetGspStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO, GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO, @@ -239,6 +240,9 @@ fn try_from(value: u32) -> Result { bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => Ok(MsgFunction::AllocObject), bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot), bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma), + bindings::NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD => { + Ok(MsgFunction::ContinuationRecord) + } bindings::NV_VGPU_MSG_FUNCTION_FREE => Ok(MsgFunction::Free), bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => Ok(MsgFunction::GetGspStaticInfo), bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo), From 0499a3826c2f8c768cc5948154ab317052947697 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Fri, 6 Mar 2026 16:22:06 +0900 Subject: [PATCH 080/712] gpu: nova-core: gsp: add tests for continuation records Add tests for continuation record splitting. They cover boundary conditions at the split points to make sure the right number of continuation records are made. They also check that the data concatenated is correct. Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260306-cmdq-continuation-v6-9-cc7b629200ee@nvidia.com Signed-off-by: Alexandre Courbot --- .../gpu/nova-core/gsp/cmdq/continuation.rs | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs index 67b3e03fd8ea..2aa17caac2e0 100644 --- a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs +++ b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs @@ -161,3 +161,141 @@ fn init_variable_payload( dst.write_all(&self.payload) } } + +#[kunit_tests(nova_core_gsp_continuation)] +mod tests { + use super::*; + + use kernel::transmute::{ + AsBytes, + FromBytes, // + }; + + /// Non-zero-sized command header for testing. + #[repr(C)] + #[derive(Clone, Copy, Zeroable)] + struct TestHeader([u8; 64]); + + // SAFETY: `TestHeader` is a plain array of bytes for which all bit patterns are valid. + unsafe impl FromBytes for TestHeader {} + + // SAFETY: `TestHeader` is a plain array of bytes for which all bit patterns are valid. + unsafe impl AsBytes for TestHeader {} + + struct TestPayload { + data: KVVec, + } + + impl TestPayload { + fn generate_pattern(len: usize) -> Result> { + let mut data = KVVec::with_capacity(len, GFP_KERNEL)?; + for i in 0..len { + // Mix in higher bits so the pattern does not repeat every 256 bytes. + data.push((i ^ (i >> 8)) as u8, GFP_KERNEL)?; + } + Ok(data) + } + + fn new(len: usize) -> Result { + Ok(Self { + data: Self::generate_pattern(len)?, + }) + } + } + + impl CommandToGsp for TestPayload { + const FUNCTION: MsgFunction = MsgFunction::Nop; + type Command = TestHeader; + type InitError = Infallible; + + fn init(&self) -> impl Init { + TestHeader::init_zeroed() + } + + fn variable_payload_len(&self) -> usize { + self.data.len() + } + + fn init_variable_payload( + &self, + dst: &mut SBufferIter>, + ) -> Result { + dst.write_all(self.data.as_slice()) + } + } + + /// Maximum variable payload size that fits in the first command alongside the header. + const MAX_FIRST_PAYLOAD: usize = SplitState::::MAX_FIRST_PAYLOAD; + + fn read_payload(cmd: impl CommandToGsp) -> Result> { + let len = cmd.variable_payload_len(); + let mut buf = KVVec::from_elem(0u8, len, GFP_KERNEL)?; + let mut sbuf = SBufferIter::new_writer([buf.as_mut_slice(), &mut []]); + cmd.init_variable_payload(&mut sbuf)?; + drop(sbuf); + Ok(buf) + } + + struct SplitTest { + payload_size: usize, + num_continuations: usize, + } + + fn check_split(t: SplitTest) -> Result { + let payload = TestPayload::new(t.payload_size)?; + let mut num_continuations = 0; + + let buf = match SplitState::new(payload)? { + SplitState::Single(cmd) => read_payload(cmd)?, + SplitState::Split(cmd, mut continuations) => { + let mut buf = read_payload(cmd)?; + assert!(size_of::() + buf.len() <= MAX_CMD_SIZE); + + while let Some(cont) = continuations.next() { + let payload = read_payload(cont)?; + assert!(payload.len() <= MAX_CMD_SIZE); + buf.extend_from_slice(&payload, GFP_KERNEL)?; + num_continuations += 1; + } + + buf + } + }; + + assert_eq!(num_continuations, t.num_continuations); + assert_eq!( + buf.as_slice(), + TestPayload::generate_pattern(t.payload_size)?.as_slice() + ); + Ok(()) + } + + #[test] + fn split_command() -> Result { + check_split(SplitTest { + payload_size: 0, + num_continuations: 0, + })?; + check_split(SplitTest { + payload_size: MAX_FIRST_PAYLOAD, + num_continuations: 0, + })?; + check_split(SplitTest { + payload_size: MAX_FIRST_PAYLOAD + 1, + num_continuations: 1, + })?; + check_split(SplitTest { + payload_size: MAX_FIRST_PAYLOAD + MAX_CMD_SIZE, + num_continuations: 1, + })?; + check_split(SplitTest { + payload_size: MAX_FIRST_PAYLOAD + MAX_CMD_SIZE + 1, + num_continuations: 2, + })?; + check_split(SplitTest { + payload_size: MAX_FIRST_PAYLOAD + MAX_CMD_SIZE * 3 + MAX_CMD_SIZE / 2, + num_continuations: 4, + })?; + Ok(()) + } +} From ba6e088ac6df02dfca2b90c54f8bb3559aab162c Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Mon, 9 Mar 2026 19:10:51 -0700 Subject: [PATCH 081/712] gpu: nova-core: print FB sizes, along with ranges For convenience of the reader: now you can directly see the sizes of each range. It is surprising just how much this helps. Sample output (using an Ampere GA104): NovaCore 0000:e1:00.0: FbLayout { fb: 0x0..0x3ff800000 (16376 MiB), vga_workspace: 0x3ff700000..0x3ff800000 (1 MiB), frts: 0x3ff600000..0x3ff700000 (1 MiB), boot: 0x3ff5fa000..0x3ff600000 (24 KiB), elf: 0x3fb960000..0x3ff5f9000 (60 MiB), wpr2_heap: 0x3f3900000..0x3fb900000 (128 MiB), wpr2: 0x3f3800000..0x3ff700000 (191 MiB), heap: 0x3f3700000..0x3f3800000 (1 MiB), vf_partition_count: 0x0, } Cc: Timur Tabi Reviewed-by: Gary Guo Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260310021125.117855-2-jhubbard@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/fb.rs | 83 +++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index c62abcaed547..6fb804c118c6 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -1,9 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 -use core::ops::Range; +use core::ops::{ + Deref, + Range, // +}; use kernel::{ device, + fmt, prelude::*, ptr::{ Alignable, @@ -94,26 +98,71 @@ pub(crate) fn unregister(&self, bar: &Bar0) { } } +pub(crate) struct FbRange(Range); + +impl From> for FbRange { + fn from(range: Range) -> Self { + Self(range) + } +} + +impl Deref for FbRange { + type Target = Range; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Debug for FbRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Use alternate format ({:#?}) to include size, compact format ({:?}) for just the range. + if f.alternate() { + let size = self.0.end - self.0.start; + + if size < usize_as_u64(SZ_1M) { + let size_kib = size / usize_as_u64(SZ_1K); + f.write_fmt(fmt!( + "{:#x}..{:#x} ({} KiB)", + self.0.start, + self.0.end, + size_kib + )) + } else { + let size_mib = size / usize_as_u64(SZ_1M); + f.write_fmt(fmt!( + "{:#x}..{:#x} ({} MiB)", + self.0.start, + self.0.end, + size_mib + )) + } + } else { + f.write_fmt(fmt!("{:#x}..{:#x}", self.0.start, self.0.end)) + } + } +} + /// Layout of the GPU framebuffer memory. /// /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. #[derive(Debug)] pub(crate) struct FbLayout { /// Range of the framebuffer. Starts at `0`. - pub(crate) fb: Range, + pub(crate) fb: FbRange, /// VGA workspace, small area of reserved memory at the end of the framebuffer. - pub(crate) vga_workspace: Range, + pub(crate) vga_workspace: FbRange, /// FRTS range. - pub(crate) frts: Range, + pub(crate) frts: FbRange, /// Memory area containing the GSP bootloader image. - pub(crate) boot: Range, + pub(crate) boot: FbRange, /// Memory area containing the GSP firmware image. - pub(crate) elf: Range, + pub(crate) elf: FbRange, /// WPR2 heap. - pub(crate) wpr2_heap: Range, + pub(crate) wpr2_heap: FbRange, /// WPR2 region range, starting with an instance of `GspFwWprMeta`. - pub(crate) wpr2: Range, - pub(crate) heap: Range, + pub(crate) wpr2: FbRange, + pub(crate) heap: FbRange, pub(crate) vf_partition_count: u8, } @@ -125,7 +174,7 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< let fb = { let fb_size = hal.vidmem_size(bar); - 0..fb_size + FbRange(0..fb_size) }; let vga_workspace = { @@ -152,7 +201,7 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< } }; - vga_base..fb.end + FbRange(vga_base..fb.end) }; let frts = { @@ -160,7 +209,7 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< const FRTS_SIZE: u64 = usize_as_u64(SZ_1M); let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE; - frts_base..frts_base + FRTS_SIZE + FbRange(frts_base..frts_base + FRTS_SIZE) }; let boot = { @@ -168,7 +217,7 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size()); let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN); - bootloader_base..bootloader_base + bootloader_size + FbRange(bootloader_base..bootloader_base + bootloader_size) }; let elf = { @@ -176,7 +225,7 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< let elf_size = u64::from_safe_cast(gsp_fw.size); let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN); - elf_addr..elf_addr + elf_size + FbRange(elf_addr..elf_addr + elf_size) }; let wpr2_heap = { @@ -185,7 +234,7 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end); let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN); - wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN) + FbRange(wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)) }; let wpr2 = { @@ -193,13 +242,13 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::())) .align_down(WPR2_DOWN_ALIGN); - wpr2_addr..frts.end + FbRange(wpr2_addr..frts.end) }; let heap = { const HEAP_SIZE: u64 = usize_as_u64(SZ_1M); - wpr2.start - HEAP_SIZE..wpr2.start + FbRange(wpr2.start - HEAP_SIZE..wpr2.start) }; Ok(Self { From a247f8a107b5ddbf21084599ad8d8190d1357de8 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Mon, 9 Mar 2026 19:10:52 -0700 Subject: [PATCH 082/712] gpu: nova-core: add FbRange.len() and use it in boot.rs A tiny simplification: now that FbLayout uses its own specific FbRange type, add an FbRange.len() method, and use that to (very slightly) simplify the calculation of Frts::frts_size initialization. Suggested-by: Alexandre Courbot Reviewed-by: Gary Guo Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260310021125.117855-3-jhubbard@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/fb.rs | 8 +++++++- drivers/gpu/nova-core/gsp/boot.rs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index 6fb804c118c6..6536d0035cb1 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -100,6 +100,12 @@ pub(crate) fn unregister(&self, bar: &Bar0) { pub(crate) struct FbRange(Range); +impl FbRange { + pub(crate) fn len(&self) -> u64 { + self.0.end - self.0.start + } +} + impl From> for FbRange { fn from(range: Range) -> Self { Self(range) @@ -118,7 +124,7 @@ impl fmt::Debug for FbRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Use alternate format ({:#?}) to include size, compact format ({:?}) for just the range. if f.alternate() { - let size = self.0.end - self.0.start; + let size = self.len(); if size < usize_as_u64(SZ_1M) { let size_kib = size / usize_as_u64(SZ_1K); diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 9a00ddb922ac..d278ce620c24 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -73,7 +73,7 @@ fn run_fwsec_frts( bios, FwsecCommand::Frts { frts_addr: fb_layout.frts.start, - frts_size: fb_layout.frts.end - fb_layout.frts.start, + frts_size: fb_layout.frts.len(), }, )?; From a544873ce0575b2fd8285a1364d3e09929d9a3ba Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Mon, 9 Mar 2026 19:10:56 -0700 Subject: [PATCH 083/712] gpu: nova-core: apply the one "use" item per line policy to commands.rs As per [1], we need one "use" item per line, in order to reduce merge conflicts. Furthermore, we need a trailing ", //" in order to tell rustfmt(1) to leave it alone. This does that for commands.rs, which is the only file in nova-core that has any remaining instances of the old style. [1] https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Gary Guo Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260310021125.117855-7-jhubbard@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/fw/commands.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs index 67f44421fcc3..db46276430be 100644 --- a/drivers/gpu/nova-core/gsp/fw/commands.rs +++ b/drivers/gpu/nova-core/gsp/fw/commands.rs @@ -1,8 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 -use kernel::prelude::*; -use kernel::transmute::{AsBytes, FromBytes}; -use kernel::{device, pci}; +use kernel::{ + device, + pci, + prelude::*, + transmute::{ + AsBytes, + FromBytes, // + }, // +}; use crate::gsp::GSP_PAGE_SIZE; From 6df6ea4b3d1567dbe6442f308735c23b63007c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Tue, 10 Mar 2026 20:44:03 +0000 Subject: [PATCH 084/712] gpiolib: clear requested flag if line is invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If `gpiochip_line_is_valid()` fails, then `-EINVAL` is returned, but `desc->flags` will have `GPIOD_FLAG_REQUESTED` set, which will result in subsequent calls misleadingly returning `-EBUSY`. Fix that by clearing the flag in case of failure. Fixes: a501624864f3 ("gpio: Respect valid_mask when requesting GPIOs") Signed-off-by: Barnabás Pőcze Reviewed-by: Matti Vaittinen Link: https://patch.msgid.link/20260310204359.1202451-1-pobrn@protonmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ada572aaebd6..9550500e1690 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2465,8 +2465,10 @@ int gpiod_request_commit(struct gpio_desc *desc, const char *label) return -EBUSY; offset = gpiod_hwgpio(desc); - if (!gpiochip_line_is_valid(guard.gc, offset)) - return -EINVAL; + if (!gpiochip_line_is_valid(guard.gc, offset)) { + ret = -EINVAL; + goto out_clear_bit; + } /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. From 995a418a6ca33e466e5e1527663ae3d5eee18304 Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Thu, 12 Mar 2026 22:51:36 +0800 Subject: [PATCH 085/712] auxdisplay: lcd2s: add error handling for i2c transfers The lcd2s_print() and lcd2s_gotoxy() functions currently ignore the return value of lcd2s_i2c_master_send(), which can fail. This can lead to silent data loss or incorrect cursor positioning. Add proper error checking: if the number of bytes sent does not match the expected length, return -EIO; otherwise propagate any error code from the I2C transfer. Signed-off-by: Wang Jun <1742789905@qq.com> Signed-off-by: Andy Shevchenko --- drivers/auxdisplay/lcd2s.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c index defb0573e43c..c7a962728752 100644 --- a/drivers/auxdisplay/lcd2s.c +++ b/drivers/auxdisplay/lcd2s.c @@ -99,8 +99,13 @@ static int lcd2s_print(struct charlcd *lcd, int c) { struct lcd2s_data *lcd2s = lcd->drvdata; u8 buf[2] = { LCD2S_CMD_WRITE, c }; + int ret; - lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + ret = lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; return 0; } @@ -108,9 +113,13 @@ static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y) { struct lcd2s_data *lcd2s = lcd->drvdata; u8 buf[3] = { LCD2S_CMD_CUR_POS, y + 1, x + 1 }; + int ret; - lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); - + ret = lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; return 0; } From dbbd550d7c8d90d3af9fe8a12a9caff077ddb8e3 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 14 Mar 2026 03:29:33 +0200 Subject: [PATCH 086/712] interconnect: qcom: sm8450: Fix NULL pointer dereference in icc_link_nodes() The change to dynamic IDs for SM8450 platform interconnects left two links unconverted, fix it to avoid the NULL pointer dereference in runtime, when a pointer to a destination interconnect is not valid: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008 <...> Call trace: icc_link_nodes+0x3c/0x100 (P) qcom_icc_rpmh_probe+0x1b4/0x528 platform_probe+0x64/0xc0 really_probe+0xc4/0x2a8 __driver_probe_device+0x80/0x140 driver_probe_device+0x48/0x170 __device_attach_driver+0xc0/0x148 bus_for_each_drv+0x88/0xf0 __device_attach+0xb0/0x1c0 device_initial_probe+0x58/0x68 bus_probe_device+0x40/0xb8 deferred_probe_work_func+0x90/0xd0 process_one_work+0x15c/0x3c0 worker_thread+0x2e8/0x400 kthread+0x150/0x208 ret_from_fork+0x10/0x20 Code: 900310f4 911d6294 91008280 94176078 (f94002a0) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception Fixes: 51513bec806f ("interconnect: qcom: sm8450: convert to dynamic IDs") Signed-off-by: Vladimir Zapolskiy Reviewed-by: Dmitry Baryshkov Link: https://msgid.link/20260314012933.350644-1-vladimir.zapolskiy@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sm8450.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/qcom/sm8450.c b/drivers/interconnect/qcom/sm8450.c index 669a638bf3ef..c88327d200ac 100644 --- a/drivers/interconnect/qcom/sm8450.c +++ b/drivers/interconnect/qcom/sm8450.c @@ -800,7 +800,7 @@ static struct qcom_icc_node qhs_compute_cfg = { .channels = 1, .buswidth = 4, .num_links = 1, - .link_nodes = { MASTER_CDSP_NOC_CFG }, + .link_nodes = { &qhm_nsp_noc_config }, }; static struct qcom_icc_node qhs_cpr_cx = { @@ -874,7 +874,7 @@ static struct qcom_icc_node qhs_lpass_cfg = { .channels = 1, .buswidth = 4, .num_links = 1, - .link_nodes = { MASTER_CNOC_LPASS_AG_NOC }, + .link_nodes = { &qhm_config_noc }, }; static struct qcom_icc_node qhs_mss_cfg = { From 9978d74031f25fde575bef3e4e3e35c5009091ce Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 4 Mar 2026 22:14:32 +0800 Subject: [PATCH 087/712] iio: proximity: rfd77402: Fix completion race condition in IRQ mode In IRQ mode, the completion was being reinitialized after the measurement command had already been sent to the hardware. This created a race condition where the IRQ handler could call complete() before reinit_completion() was executed. Consequently, wait_for_completion_timeout() would fail to see the signal and wait until it timed out. Move reinit_completion() to occur before the measurement command is triggered to ensure the synchronization primitive is ready to capture the interrupt. Fixes: dc81be96a73a ("iio: proximity: rfd77402: Add interrupt handling support") Signed-off-by: Felix Gu Reviewed-by: Shrikant Raskar Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/rfd77402.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c index 6afdbfca3e5a..81b8daf17a54 100644 --- a/drivers/iio/proximity/rfd77402.c +++ b/drivers/iio/proximity/rfd77402.c @@ -173,10 +173,8 @@ static int rfd77402_wait_for_result(struct rfd77402_data *data) struct i2c_client *client = data->client; int val, ret; - if (data->irq_en) { - reinit_completion(&data->completion); + if (data->irq_en) return rfd77402_wait_for_irq(data); - } /* * As per RFD77402 datasheet section '3.1.1 Single Measure', the @@ -204,6 +202,9 @@ static int rfd77402_measure(struct rfd77402_data *data) if (ret < 0) return ret; + if (data->irq_en) + reinit_completion(&data->completion); + ret = i2c_smbus_write_byte_data(client, RFD77402_CMD_R, RFD77402_CMD_SINGLE | RFD77402_CMD_VALID); From 79a86a6cc3669416a21fef32d0767d39ba84b3aa Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 7 Mar 2026 19:44:09 -0600 Subject: [PATCH 088/712] iio: orientation: hid-sensor-rotation: add timestamp hack to not break userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a hack to push two timestamps in the hid-sensor-rotation scan data to avoid breaking userspace applications that depend on the timestamp being at the incorrect location in the scan data due to unintentional misalignment in older kernels. When this driver was written, the timestamp was in the correct location because of the way iio_compute_scan_bytes() was implemented at the time. (Samples were 24 bytes each.) Then commit 883f61653069 ("iio: buffer: align the size of scan bytes to size of the largest element") changed the computed scan_bytes to be a different size (32 bytes), which caused iio_push_to_buffers_with_timestamp() to place the timestamp at an incorrect offset. There have been long periods of time (6 years each) where the timestamp was in either location, so to not break either case, we open-code the timestamps to be pushed to both locations in the scan data. Reported-by: Jonathan Cameron Closes: https://lore.kernel.org/linux-iio/20260215162351.79f40b32@jic23-huawei/ Fixes: 883f61653069 ("iio: buffer: align the size of scan bytes to size of the largest element") Signed-off-by: David Lechner Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 6806481873be..5a5e6e4fbe34 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -20,7 +20,12 @@ struct dev_rot_state { struct hid_sensor_hub_attribute_info quaternion; struct { IIO_DECLARE_QUATERNION(s32, sampled_vals); - aligned_s64 timestamp; + /* + * ABI regression avoidance: There are two copies of the same + * timestamp in case of userspace depending on broken alignment + * from older kernels. + */ + aligned_s64 timestamp[2]; } scan; int scale_pre_decml; int scale_post_decml; @@ -154,8 +159,19 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, if (!rot_state->timestamp) rot_state->timestamp = iio_get_time_ns(indio_dev); - iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan, - rot_state->timestamp); + /* + * ABI regression avoidance: IIO previously had an incorrect + * implementation of iio_push_to_buffers_with_timestamp() that + * put the timestamp in the last 8 bytes of the buffer, which + * was incorrect according to the IIO ABI. To avoid breaking + * userspace that may be depending on this broken behavior, we + * put the timestamp in both the correct place [0] and the old + * incorrect place [1]. + */ + rot_state->scan.timestamp[0] = rot_state->timestamp; + rot_state->scan.timestamp[1] = rot_state->timestamp; + + iio_push_to_buffers(indio_dev, &rot_state->scan); rot_state->timestamp = 0; } From c05a87d9ec3bf8727a5d746ce855003c6f2f8bb4 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 9 Mar 2026 20:45:45 -0700 Subject: [PATCH 089/712] iio: imu: bmi160: Remove potential undefined behavior in bmi160_config_pin() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If 'pin' is not one of its expected values, the value of 'int_out_ctrl_shift' is undefined. With UBSAN enabled, this causes Clang to generate undefined behavior, resulting in the following warning: drivers/iio/imu/bmi160/bmi160_core.o: warning: objtool: bmi160_setup_irq() falls through to next function __cfi_bmi160_core_runtime_resume() Prevent the UB and improve error handling by returning an error if 'pin' has an unexpected value. While at it, simplify the code a bit by moving the 'pin_name' assignment to the first switch statement. Fixes: 895bf81e6bbf ("iio:bmi160: add drdy interrupt support") Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/a426d669-58bb-4be1-9eaa-6f3d83109e2d@app.fastmail.com Signed-off-by: Josh Poimboeuf Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi160/bmi160_core.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 5f47708b4c5d..4abb83b75e2e 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -573,12 +573,16 @@ static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin, int_out_ctrl_shift = BMI160_INT1_OUT_CTRL_SHIFT; int_latch_mask = BMI160_INT1_LATCH_MASK; int_map_mask = BMI160_INT1_MAP_DRDY_EN; + pin_name = "INT1"; break; case BMI160_PIN_INT2: int_out_ctrl_shift = BMI160_INT2_OUT_CTRL_SHIFT; int_latch_mask = BMI160_INT2_LATCH_MASK; int_map_mask = BMI160_INT2_MAP_DRDY_EN; + pin_name = "INT2"; break; + default: + return -EINVAL; } int_out_ctrl_mask = BMI160_INT_OUT_CTRL_MASK << int_out_ctrl_shift; @@ -612,17 +616,8 @@ static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin, ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_MAP, int_map_mask, int_map_mask, write_usleep); - if (ret) { - switch (pin) { - case BMI160_PIN_INT1: - pin_name = "INT1"; - break; - case BMI160_PIN_INT2: - pin_name = "INT2"; - break; - } + if (ret) dev_err(dev, "Failed to configure %s IRQ pin", pin_name); - } return ret; } From dd154646d292cce7de952f216760c58c35cfecde Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Tue, 10 Mar 2026 13:56:44 +0200 Subject: [PATCH 090/712] iio: dac: mcp47feb02: Fix Vref validation [1-999] case Store reference voltages in uV instead of mV to avoid invalid error code in dev_err_probe() call. Vref variables store the actual value returned by devm_regulator_get_enable_read_voltage() function instead of the results of dividing it by MILLI. The corner case [1-999] divided by MILLI of the voltage reference variable value would become 0 is covered too. Fixes: bf394cc80369 ("iio: dac: adding support for Microchip MCP47FEB02") Link: https://lore.kernel.org/all/aYXvP5FLA5BvkoVX@stanley.mountain/ Signed-off-by: Ariana Lazar Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp47feb02.c | 49 ++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c index 08fb85359697..faccb804a5ed 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02.c @@ -65,7 +65,7 @@ #define MCP47FEB02_MAX_SCALES_CH 3 #define MCP47FEB02_DAC_WIPER_UNLOCKED 0 #define MCP47FEB02_NORMAL_OPERATION 0 -#define MCP47FEB02_INTERNAL_BAND_GAP_mV 2440 +#define MCP47FEB02_INTERNAL_BAND_GAP_uV 2440000 #define NV_DAC_ADDR_OFFSET 0x10 enum mcp47feb02_vref_mode { @@ -697,44 +697,40 @@ static const struct iio_chan_spec mcp47febxx_ch_template = { }; static void mcp47feb02_init_scale(struct mcp47feb02_data *data, enum mcp47feb02_scale scale, - int vref_mV, int scale_avail[]) + int vref_uV, int scale_avail[]) { u32 value_micro, value_int; u64 tmp; - /* vref_mV should not be negative */ - tmp = (u64)vref_mV * MICRO >> data->chip_features->resolution; + /* vref_uV should not be negative */ + tmp = (u64)vref_uV * MILLI >> data->chip_features->resolution; value_int = div_u64_rem(tmp, MICRO, &value_micro); scale_avail[scale * 2] = value_int; scale_avail[scale * 2 + 1] = value_micro; } -static int mcp47feb02_init_scales_avail(struct mcp47feb02_data *data, int vdd_mV, - int vref_mV, int vref1_mV) +static int mcp47feb02_init_scales_avail(struct mcp47feb02_data *data, int vdd_uV, + int vref_uV, int vref1_uV) { - struct device *dev = regmap_get_device(data->regmap); int tmp_vref; - mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale); + mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_uV, data->scale); if (data->use_vref) - tmp_vref = vref_mV; + tmp_vref = vref_uV; else - tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV; + tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_uV; mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1, tmp_vref, data->scale); mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X2, tmp_vref * 2, data->scale); if (data->phys_channels >= 4) { - mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale_1); - - if (data->use_vref1 && vref1_mV <= 0) - return dev_err_probe(dev, vref1_mV, "Invalid voltage for Vref1\n"); + mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_uV, data->scale_1); if (data->use_vref1) - tmp_vref = vref1_mV; + tmp_vref = vref1_uV; else - tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV; + tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_uV; mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1, tmp_vref, data->scale_1); @@ -1078,8 +1074,8 @@ static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data) return 0; } -static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_mV, - int vref_mV, int vref1_mV) +static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_uV, + int vref_uV, int vref1_uV) { unsigned int i; @@ -1087,7 +1083,7 @@ static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_mV, struct device *dev = regmap_get_device(data->regmap); int ret; - ret = mcp47feb02_init_scales_avail(data, vdd_mV, vref_mV, vref1_mV); + ret = mcp47feb02_init_scales_avail(data, vdd_uV, vref_uV, vref1_uV); if (ret) return dev_err_probe(dev, ret, "failed to init scales for ch %u\n", i); } @@ -1101,10 +1097,7 @@ static int mcp47feb02_probe(struct i2c_client *client) struct device *dev = &client->dev; struct mcp47feb02_data *data; struct iio_dev *indio_dev; - int vref1_mV = 0; - int vref_mV = 0; - int vdd_mV; - int ret; + int vref1_uV, vref_uV, vdd_uV, ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -1141,13 +1134,14 @@ static int mcp47feb02_probe(struct i2c_client *client) if (ret < 0) return ret; - vdd_mV = ret / MILLI; + vdd_uV = ret; ret = devm_regulator_get_enable_read_voltage(dev, "vref"); if (ret > 0) { - vref_mV = ret / MILLI; + vref_uV = ret; data->use_vref = true; } else { + vref_uV = 0; dev_dbg(dev, "using internal band gap as voltage reference.\n"); dev_dbg(dev, "Vref is unavailable.\n"); } @@ -1155,9 +1149,10 @@ static int mcp47feb02_probe(struct i2c_client *client) if (chip_features->have_ext_vref1) { ret = devm_regulator_get_enable_read_voltage(dev, "vref1"); if (ret > 0) { - vref1_mV = ret / MILLI; + vref1_uV = ret; data->use_vref1 = true; } else { + vref1_uV = 0; dev_dbg(dev, "using internal band gap as voltage reference 1.\n"); dev_dbg(dev, "Vref1 is unavailable.\n"); } @@ -1167,7 +1162,7 @@ static int mcp47feb02_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "Error initialising vref register\n"); - ret = mcp47feb02_init_ch_scales(data, vdd_mV, vref_mV, vref1_mV); + ret = mcp47feb02_init_ch_scales(data, vdd_uV, vref_uV, vref1_uV); if (ret) return ret; From 65852b56bfa929f99e28c96fd98b02058959da7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Thu, 12 Mar 2026 10:37:09 +0200 Subject: [PATCH 091/712] drm/i915/psr: Disable PSR on update_m_n and update_lrr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PSR/PR parameters might change based on update_m_n or update_lrr. Disable on update_m_n and update_lrr to ensure proper parameters are taken into use on next PSR enable in intel_psr_post_plane_update. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15771 Fixes: 2bc98c6f97af ("drm/i915/alpm: Compute ALPM parameters into crtc_state->alpm_state") Cc: # v6.19+ Signed-off-by: Jouni Högander Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312083710.1593781-2-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 5041a5a138d1..7e0e4c3bf985 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -3112,6 +3112,8 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, * - Display WA #1136: skl, bxt */ if (intel_crtc_needs_modeset(new_crtc_state) || + new_crtc_state->update_m_n || + new_crtc_state->update_lrr || !new_crtc_state->has_psr || !new_crtc_state->active_planes || new_crtc_state->has_sel_update != psr->sel_update_enabled || From 8c229b4aa00262c13787982e998c61c0783285e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Thu, 12 Mar 2026 10:37:10 +0200 Subject: [PATCH 092/712] drm/i915/psr: Compute PSR entry_setup_frames into intel_crtc_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PSR entry_setup_frames is currently computed directly into struct intel_dp:intel_psr:entry_setup_frames. This causes a problem if mode change gets rejected after PSR compute config: Psr_entry_setup_frames computed for this rejected state is in intel_dp:intel_psr:entry_setup_frame. Fix this by computing it into intel_crtc_state and copy the value into intel_dp:intel_psr:entry_setup_frames on PSR enable. Fixes: 2b981d57e480 ("drm/i915/display: Support PSR entry VSC packet to be transmitted one frame earlier") Cc: Mika Kahola Cc: # v6.8+ Signed-off-by: Jouni Högander Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312083710.1593781-3-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_psr.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index e189f8c39ccb..d3a9ace4c9d1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1188,6 +1188,7 @@ struct intel_crtc_state { u32 dc3co_exitline; u16 su_y_granularity; u8 active_non_psr_pipes; + u8 entry_setup_frames; const char *no_psr_reason; /* diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 7e0e4c3bf985..c13116e6f17f 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1716,7 +1716,7 @@ static bool _psr_compute_config(struct intel_dp *intel_dp, entry_setup_frames = intel_psr_entry_setup_frames(intel_dp, conn_state, adjusted_mode); if (entry_setup_frames >= 0) { - intel_dp->psr.entry_setup_frames = entry_setup_frames; + crtc_state->entry_setup_frames = entry_setup_frames; } else { crtc_state->no_psr_reason = "PSR setup timing not met"; drm_dbg_kms(display->drm, @@ -1814,7 +1814,7 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp, { struct intel_display *display = to_intel_display(intel_dp); - return (DISPLAY_VER(display) == 20 && intel_dp->psr.entry_setup_frames > 0 && + return (DISPLAY_VER(display) == 20 && crtc_state->entry_setup_frames > 0 && !crtc_state->has_sel_update); } @@ -2190,6 +2190,7 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, intel_dp->psr.pkg_c_latency_used = crtc_state->pkg_c_latency_used; intel_dp->psr.io_wake_lines = crtc_state->alpm_state.io_wake_lines; intel_dp->psr.fast_wake_lines = crtc_state->alpm_state.fast_wake_lines; + intel_dp->psr.entry_setup_frames = crtc_state->entry_setup_frames; if (!psr_interrupt_error_check(intel_dp)) return; From 33978364a2f3fc2989751bdabcaea0ec7e8d1ae8 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Fri, 23 Jan 2026 15:21:22 +0000 Subject: [PATCH 093/712] drm/i915/display: PORT_NONE is not valid Static analysis issue: In assert_port_valid, add a check to ensure port != PORT_NONE, as that is not a valid port. The check must be explicit to prevent a bad bit shift operation in the general case via short-circuiting. It's not likely this will ever come up in a real use case, but it's at least worth guarding against. It would probably also be pertinent to modify the behavior of the port_name function to correctly print PORT_NONE in this case, as currently the port would be reported as 'port @' by the debugger. But that should be done separately, and given port_name is mostly just a debug printing helper function anyways, fixing it is a low priority. v2: - Conditional check was backwards. Fix it. (Jani) Signed-off-by: Jonathan Cavitt Cc: Jani Nikula Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260123152121.7042-2-jonathan.cavitt@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index b18ce0c36a64..ee501009a251 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -7890,7 +7890,8 @@ static bool intel_ddi_crt_present(struct intel_display *display) bool assert_port_valid(struct intel_display *display, enum port port) { - return !drm_WARN(display->drm, !(DISPLAY_RUNTIME_INFO(display)->port_mask & BIT(port)), + return !drm_WARN(display->drm, + !(port >= 0 && DISPLAY_RUNTIME_INFO(display)->port_mask & BIT(port)), "Platform does not support port %c\n", port_name(port)); } From 1cabff0d18733f1f33bd6ab0505cbec430834469 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Wed, 4 Feb 2026 16:19:46 +0000 Subject: [PATCH 094/712] drm/i915/gvt: Swap read and write checks The function intel_gvt_i2c_handle_aux_ch_write currently does not support the DP_AUX_I2C_WRITE operation. Notably, we check if op & 0x1 == DP_AUX_I2C_WRITE (one), and if it does not, assert that op & 0x1 == DP_AUX_I2C_READ (zero). This is unnecessary because if op & 0x1 != 1, then op & 0x1 == 0. But beyond that, it probably makes more sense to check for the condition that is implemented, rather than check for the condition that is not. Swap the conditions. We can also get rid of the unnecessary drm_WARN_ON while we're here. Suggested-by: Jani Nikula Signed-off-by: Jonathan Cavitt Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260204161945.8127-2-jonathan.cavitt@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/gvt/edid.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 021afff1cd5d..ca5b54466a65 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -535,16 +535,7 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, i2c_edid->edid_available = true; } } - } else if ((op & 0x1) == DP_AUX_I2C_WRITE) { - /* TODO - * We only support EDID reading from I2C_over_AUX. And - * we do not expect the index mode to be used. Right now - * the WRITE operation is ignored. It is good enough to - * support the gfx driver to do EDID access. - */ - } else { - if (drm_WARN_ON(&i915->drm, (op & 0x1) != DP_AUX_I2C_READ)) - return; + } else if ((op & 0x1) == DP_AUX_I2C_READ) { if (drm_WARN_ON(&i915->drm, msg_length != 4)) return; if (i2c_edid->edid_available && i2c_edid->target_selected) { @@ -553,6 +544,13 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, aux_data_for_write = (val << 16); } else aux_data_for_write = (0xff << 16); + } else { + /* TODO + * We only support EDID reading from I2C_over_AUX. And + * we do not expect the index mode to be used. Right now + * the WRITE operation is ignored. It is good enough to + * support the gfx driver to do EDID access. + */ } /* write the return value in AUX_CH_DATA reg which includes: * ACK of I2C_WRITE From 01e8d0f742222f1e68f48180d5480097adf7ae9f Mon Sep 17 00:00:00 2001 From: Wanquan Zhong Date: Mon, 16 Mar 2026 19:55:12 +0800 Subject: [PATCH 095/712] USB: serial: option: add support for Rolling Wireless RW135R-GL Add VID/PID 33f8:1003 for the Rolling Wireless RW135R-GL M.2 module, which is used in laptop debug cards with MBIM interface for Linux/Chrome OS. The device supports mbim, pipe functionalities. Here are the outputs of usb-devices: T: Bus=04 Lev=01 Prnt=01 Port=02 Cnt=01 Dev#= 2 Spd=5000 MxCh= 0 D: Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev=05.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C: #Ifs= 3 Cfg#= 1 Atr=a0 MxPwr=896mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=40 Driver=option E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms Signed-off-by: Wanquan Zhong Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e349ed66d2ac..d222bf2c2539 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2461,6 +2461,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */ .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, From 0e01c3416eb863ee7f156a9d7e7421ec0a9f68a0 Mon Sep 17 00:00:00 2001 From: Frej Drejhammar Date: Sun, 22 Feb 2026 18:00:42 +0100 Subject: [PATCH 096/712] USB: serial: io_edgeport: add support for Blackbox IC135A The Blackbox 724-746-5500 USB Director USB-RS-232 HUB, part number IC135A, is a rebadged Edgeport/4 with its own USB device id. Signed-off-by: Frej Drejhammar Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 3 +++ drivers/usb/serial/io_usbvend.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 58694b8943d1..3f5889145e51 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -73,6 +73,7 @@ static const struct usb_device_id edgeport_4port_id_table[] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) }, { } }; @@ -121,6 +122,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, @@ -470,6 +472,7 @@ static void get_product_info(struct edgeport_serial *edge_serial) case ION_DEVICE_ID_EDGEPORT_2_DIN: case ION_DEVICE_ID_EDGEPORT_4_DIN: case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU: + case ION_DEVICE_ID_BLACKBOX_IC135A: product_info->IsRS232 = 1; break; diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index 9a6f742ad3ab..c82a275e8e76 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -211,6 +211,7 @@ // // Definitions for other product IDs +#define ION_DEVICE_ID_BLACKBOX_IC135A 0x0801 // OEM device (rebranded Edgeport/4) #define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device #define ION_DEVICE_ID_E5805A 0x1A01 // OEM device (rebranded Edgeport/4) From 902d174c6fd30d428337e90ed769a3aa81951983 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:14:58 +0200 Subject: [PATCH 097/712] drm/i915/dmc: simplify stepping info initialization Having intel_get_stepping_info() return the pointer that was passed in isn't necessary. Just use a pointer to the local variable instead. The initialization to ** didn't make a difference, because it was always overridden. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/c9affb82fd3e9fb464778013bb7c8fab06232bfd.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dmc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 90ba932d940a..41842ff7d90f 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -418,15 +418,12 @@ bool intel_dmc_has_payload(struct intel_display *display) return has_dmc_id_fw(display, DMC_FW_MAIN); } -static const struct stepping_info * -intel_get_stepping_info(struct intel_display *display, - struct stepping_info *si) +static void initialize_stepping_info(struct intel_display *display, struct stepping_info *si) { const char *step_name = intel_step_name(INTEL_DISPLAY_STEP(display)); si->stepping = step_name[0]; si->substepping = step_name[1]; - return si; } static void gen9_set_dc_state_debugmask(struct intel_display *display) @@ -1274,8 +1271,7 @@ static int parse_dmc_fw(struct intel_dmc *dmc, const struct firmware *fw) struct intel_css_header *css_header; struct intel_package_header *package_header; struct intel_dmc_header_base *dmc_header; - struct stepping_info display_info = { '*', '*'}; - const struct stepping_info *si = intel_get_stepping_info(display, &display_info); + struct stepping_info si = {}; enum intel_dmc_id dmc_id; u32 readcount = 0; u32 r, offset; @@ -1283,6 +1279,8 @@ static int parse_dmc_fw(struct intel_dmc *dmc, const struct firmware *fw) if (!fw) return -EINVAL; + initialize_stepping_info(display, &si); + /* Extract CSS Header information */ css_header = (struct intel_css_header *)fw->data; r = parse_dmc_fw_css(dmc, css_header, fw->size); @@ -1293,7 +1291,7 @@ static int parse_dmc_fw(struct intel_dmc *dmc, const struct firmware *fw) /* Extract Package Header information */ package_header = (struct intel_package_header *)&fw->data[readcount]; - r = parse_dmc_fw_package(dmc, package_header, si, fw->size - readcount); + r = parse_dmc_fw_package(dmc, package_header, &si, fw->size - readcount); if (!r) return -EINVAL; From 2ca0e7657f6f18838e396febaf8ca93a762e486a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:14:59 +0200 Subject: [PATCH 098/712] drm/i915/display: add step name in display runtime info Initialize the stepping name in display runtime info. This avoids having to use intel_step_name(). For display device info print at boot, debugfs and snapshot this changes the unknown step name from ** to N/A, which is more user friendly anyway. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/aab445dedb8235d9fdddfe2ee5bb624cdf453a18.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_device.c | 28 +++++++++++++++++-- .../drm/i915/display/intel_display_device.h | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c index 55689111c5d7..30d734a43211 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.c +++ b/drivers/gpu/drm/i915/display/intel_display_device.c @@ -1653,6 +1653,28 @@ static void display_platforms_or(struct intel_display_platforms *dst, bitmap_or(dst->bitmap, dst->bitmap, src->bitmap, display_platforms_num_bits()); } +#define __STEP_NAME(name) [STEP_##name] = #name, + +static void initialize_step(struct intel_display *display, enum intel_step step) +{ + static const char step_names[][3] = { + STEP_NAME_LIST(__STEP_NAME) + }; + + DISPLAY_RUNTIME_INFO(display)->step = step; + + /* Step name will remain an empty string if not applicable */ + if (step >= 0 && step < ARRAY_SIZE(step_names)) + strscpy(DISPLAY_RUNTIME_INFO(display)->step_name, step_names[step]); +} + +#undef __STEP_NAME + +static const char *step_name(const struct intel_display_runtime_info *runtime) +{ + return strlen(runtime->step_name) ? runtime->step_name : "N/A"; +} + struct intel_display *intel_display_device_probe(struct pci_dev *pdev, const struct intel_display_parent_interface *parent) { @@ -1730,14 +1752,14 @@ struct intel_display *intel_display_device_probe(struct pci_dev *pdev, subdesc ? &subdesc->step_info : NULL); } - DISPLAY_RUNTIME_INFO(display)->step = step; + initialize_step(display, step); drm_info(display->drm, "Found %s%s%s (device ID %04x) %s display version %u.%02u stepping %s\n", desc->name, subdesc ? "/" : "", subdesc ? subdesc->name : "", pdev->device, display->platform.dgfx ? "discrete" : "integrated", DISPLAY_RUNTIME_INFO(display)->ip.ver, DISPLAY_RUNTIME_INFO(display)->ip.rel, - step != STEP_NONE ? intel_step_name(step) : "N/A"); + step_name(DISPLAY_RUNTIME_INFO(display))); return display; @@ -1953,7 +1975,7 @@ void intel_display_device_info_print(const struct intel_display_device_info *inf drm_printf(p, "display version: %u\n", runtime->ip.ver); - drm_printf(p, "display stepping: %s\n", intel_step_name(runtime->step)); + drm_printf(p, "display stepping: %s\n", step_name(runtime)); #define PRINT_FLAG(name) drm_printf(p, "%s: %s\n", #name, str_yes_no(info->name)) DEV_INFO_DISPLAY_FOR_EACH_FLAG(PRINT_FLAG); diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index e84c190dcc4f..1170ac346615 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -287,6 +287,7 @@ struct intel_display_runtime_info { u16 step; /* hardware */ } ip; int step; /* symbolic */ + char step_name[3]; /* empty string if not applicable */ u32 rawclk_freq; From 58371a116845706ba2b90d3a1e0c9193794ebf06 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:15:00 +0200 Subject: [PATCH 099/712] drm/i915/dmc: use step name from runtime info Now that the step name is in runtime info, switch to using it instead of intel_step_name(). The ** are only relevant for DMC, so make their use explicit. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/395906e52e76bc726b9dac69a453583cc6e3f6c1.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dmc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 41842ff7d90f..1667a829e708 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -39,7 +39,6 @@ #include "intel_dmc.h" #include "intel_dmc_regs.h" #include "intel_flipq.h" -#include "intel_step.h" /** * DOC: DMC Firmware Support @@ -420,10 +419,10 @@ bool intel_dmc_has_payload(struct intel_display *display) static void initialize_stepping_info(struct intel_display *display, struct stepping_info *si) { - const char *step_name = intel_step_name(INTEL_DISPLAY_STEP(display)); + const char *step_name = DISPLAY_RUNTIME_INFO(display)->step_name; - si->stepping = step_name[0]; - si->substepping = step_name[1]; + si->stepping = step_name[0] ?: '*'; + si->substepping = step_name[1] ?: '*'; } static void gen9_set_dc_state_debugmask(struct intel_display *display) From 706d58da4c4b363b5dc3e690d97a05fdf47d2620 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:15:01 +0200 Subject: [PATCH 100/712] drm/xe/compat: remove intel_step_name macro As there are no more compat users left for intel_step_name(), remove the macro and use the more direct include for the enumerations. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/816e3f6dda0a112392e8f8ccff820a81aff63f32.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/compat-i915-headers/intel_step.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h index 2cf13a572ab0..0eabe2866f5f 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h @@ -6,9 +6,8 @@ #ifndef __INTEL_STEP_H__ #define __INTEL_STEP_H__ -#include "xe_step.h" +#include "xe_step_types.h" #define intel_step xe_step -#define intel_step_name xe_step_name #endif /* __INTEL_STEP_H__ */ From 3ccc8a922906703cd0efdf1bdd6186f18f7e23ec Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:15:02 +0200 Subject: [PATCH 101/712] drm/intel: add shared step.h and switch i915 to use it As the first step towards using shared definitions for step name enumerations, add shared include/drm/intel/step.h and switch i915 to use it. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/e76412a316ddff44dc46633d80e9caa5df54ed6b.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_step.h | 57 +--------------------------- include/drm/intel/step.h | 62 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 55 deletions(-) create mode 100644 include/drm/intel/step.h diff --git a/drivers/gpu/drm/i915/intel_step.h b/drivers/gpu/drm/i915/intel_step.h index 22f1d6905160..2ca36eae4b5a 100644 --- a/drivers/gpu/drm/i915/intel_step.h +++ b/drivers/gpu/drm/i915/intel_step.h @@ -8,6 +8,8 @@ #include +#include + struct drm_i915_private; struct intel_step_info { @@ -19,61 +21,6 @@ struct intel_step_info { u8 media_step; }; -#define STEP_ENUM_VAL(name) STEP_##name, - -#define STEP_NAME_LIST(func) \ - func(A0) \ - func(A1) \ - func(A2) \ - func(A3) \ - func(B0) \ - func(B1) \ - func(B2) \ - func(B3) \ - func(C0) \ - func(C1) \ - func(C2) \ - func(C3) \ - func(D0) \ - func(D1) \ - func(D2) \ - func(D3) \ - func(E0) \ - func(E1) \ - func(E2) \ - func(E3) \ - func(F0) \ - func(F1) \ - func(F2) \ - func(F3) \ - func(G0) \ - func(G1) \ - func(G2) \ - func(G3) \ - func(H0) \ - func(H1) \ - func(H2) \ - func(H3) \ - func(I0) \ - func(I1) \ - func(I2) \ - func(I3) \ - func(J0) \ - func(J1) \ - func(J2) \ - func(J3) - -/* - * Symbolic steppings that do not match the hardware. These are valid both as gt - * and display steppings as symbolic names. - */ -enum intel_step { - STEP_NONE = 0, - STEP_NAME_LIST(STEP_ENUM_VAL) - STEP_FUTURE, - STEP_FOREVER, -}; - void intel_step_init(struct drm_i915_private *i915); const char *intel_step_name(enum intel_step step); diff --git a/include/drm/intel/step.h b/include/drm/intel/step.h new file mode 100644 index 000000000000..4de7520109bc --- /dev/null +++ b/include/drm/intel/step.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __STEP_H__ +#define __STEP_H__ + +#define STEP_ENUM_VAL(name) STEP_##name, + +#define STEP_NAME_LIST(func) \ + func(A0) \ + func(A1) \ + func(A2) \ + func(A3) \ + func(B0) \ + func(B1) \ + func(B2) \ + func(B3) \ + func(C0) \ + func(C1) \ + func(C2) \ + func(C3) \ + func(D0) \ + func(D1) \ + func(D2) \ + func(D3) \ + func(E0) \ + func(E1) \ + func(E2) \ + func(E3) \ + func(F0) \ + func(F1) \ + func(F2) \ + func(F3) \ + func(G0) \ + func(G1) \ + func(G2) \ + func(G3) \ + func(H0) \ + func(H1) \ + func(H2) \ + func(H3) \ + func(I0) \ + func(I1) \ + func(I2) \ + func(I3) \ + func(J0) \ + func(J1) \ + func(J2) \ + func(J3) + +/* + * Symbolic steppings that do not match the hardware. These are valid both as gt + * and display steppings as symbolic names. + */ +enum intel_step { + STEP_NONE = 0, + STEP_NAME_LIST(STEP_ENUM_VAL) + STEP_FUTURE, + STEP_FOREVER, +}; + +#endif /* __STEP_H__ */ From e2d599021c843d97ee38ba351cb0117eb984e038 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Feb 2026 15:00:15 +0900 Subject: [PATCH 102/712] rust: io: turn IoCapable into a functional trait `IoCapable` is currently used as a marker trait to signal that the methods of the `Io` trait corresponding to `T` have been overridden by the implementor (the default implementations triggering a build-time error). This goes against the DRY principle and separates the signaling of the capability from its implementation, making it possible to forget a step while implementing a new `Io`. Another undesirable side-effect is that it makes the implementation of I/O backends boilerplate-y and convoluted: currently this is done using two levels of imbricated macros that generate unsafe code. Fix these issues by turning `IoCapable` into a functional trait that includes the raw implementation of the I/O access for `T` using unsafe methods that work with an arbitrary address. This allows us to turn the default methods of `Io` into regular methods that check the passed offset, turn it into an address, and call into the corresponding `IoCapable` functions, removing the need to overload them at all. `IoCapable` must still be implemented for all supported primitive types, which is still done more concisely using a macro, but this macro becomes much simpler and does not require calling into another one. Reviewed-by: Daniel Almeida Acked-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260206-io-v2-1-71dea20a06e6@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 169 ++++++++++++++++++++++++++++++++---------- rust/kernel/pci/io.rs | 37 ++++++++- 2 files changed, 163 insertions(+), 43 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index e5fba6bf6db0..ec78c614c959 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -320,14 +320,29 @@ const fn offset_valid(offset: usize, size: usize) -> bool { } } -/// Marker trait indicating that an I/O backend supports operations of a certain type. +/// Trait indicating that an I/O backend supports operations of a certain type and providing an +/// implementation for these operations. /// /// Different I/O backends can implement this trait to expose only the operations they support. /// /// For example, a PCI configuration space may implement `IoCapable`, `IoCapable`, /// and `IoCapable`, but not `IoCapable`, while an MMIO region on a 64-bit /// system might implement all four. -pub trait IoCapable {} +pub trait IoCapable { + /// Performs an I/O read of type `T` at `address` and returns the result. + /// + /// # Safety + /// + /// The range `[address..address + size_of::()]` must be within the bounds of `Self`. + unsafe fn io_read(&self, address: usize) -> T; + + /// Performs an I/O write of `value` at `address`. + /// + /// # Safety + /// + /// The range `[address..address + size_of::()]` must be within the bounds of `Self`. + unsafe fn io_write(&self, value: T, address: usize); +} /// Types implementing this trait (e.g. MMIO BARs or PCI config regions) /// can perform I/O operations on regions of memory. @@ -369,146 +384,198 @@ fn io_addr(&self, offset: usize) -> Result { /// Fallible 8-bit read with runtime bounds check. #[inline(always)] - fn try_read8(&self, _offset: usize) -> Result + fn try_read8(&self, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 8-bit read") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + Ok(unsafe { self.io_read(address) }) } /// Fallible 16-bit read with runtime bounds check. #[inline(always)] - fn try_read16(&self, _offset: usize) -> Result + fn try_read16(&self, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 16-bit read") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + Ok(unsafe { self.io_read(address) }) } /// Fallible 32-bit read with runtime bounds check. #[inline(always)] - fn try_read32(&self, _offset: usize) -> Result + fn try_read32(&self, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 32-bit read") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + Ok(unsafe { self.io_read(address) }) } /// Fallible 64-bit read with runtime bounds check. #[inline(always)] - fn try_read64(&self, _offset: usize) -> Result + fn try_read64(&self, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 64-bit read") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + Ok(unsafe { self.io_read(address) }) } /// Fallible 8-bit write with runtime bounds check. #[inline(always)] - fn try_write8(&self, _value: u8, _offset: usize) -> Result + fn try_write8(&self, value: u8, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 8-bit write") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + unsafe { self.io_write(value, address) }; + Ok(()) } /// Fallible 16-bit write with runtime bounds check. #[inline(always)] - fn try_write16(&self, _value: u16, _offset: usize) -> Result + fn try_write16(&self, value: u16, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 16-bit write") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + unsafe { self.io_write(value, address) }; + Ok(()) } /// Fallible 32-bit write with runtime bounds check. #[inline(always)] - fn try_write32(&self, _value: u32, _offset: usize) -> Result + fn try_write32(&self, value: u32, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 32-bit write") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + unsafe { self.io_write(value, address) }; + Ok(()) } /// Fallible 64-bit write with runtime bounds check. #[inline(always)] - fn try_write64(&self, _value: u64, _offset: usize) -> Result + fn try_write64(&self, value: u64, offset: usize) -> Result where Self: IoCapable, { - build_error!("Backend does not support fallible 64-bit write") + let address = self.io_addr::(offset)?; + + // SAFETY: `address` has been validated by `io_addr`. + unsafe { self.io_write(value, address) }; + Ok(()) } /// Infallible 8-bit read with compile-time bounds check. #[inline(always)] - fn read8(&self, _offset: usize) -> u8 + fn read8(&self, offset: usize) -> u8 where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 8-bit read") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_read(address) } } /// Infallible 16-bit read with compile-time bounds check. #[inline(always)] - fn read16(&self, _offset: usize) -> u16 + fn read16(&self, offset: usize) -> u16 where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 16-bit read") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_read(address) } } /// Infallible 32-bit read with compile-time bounds check. #[inline(always)] - fn read32(&self, _offset: usize) -> u32 + fn read32(&self, offset: usize) -> u32 where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 32-bit read") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_read(address) } } /// Infallible 64-bit read with compile-time bounds check. #[inline(always)] - fn read64(&self, _offset: usize) -> u64 + fn read64(&self, offset: usize) -> u64 where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 64-bit read") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_read(address) } } /// Infallible 8-bit write with compile-time bounds check. #[inline(always)] - fn write8(&self, _value: u8, _offset: usize) + fn write8(&self, value: u8, offset: usize) where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 8-bit write") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_write(value, address) } } /// Infallible 16-bit write with compile-time bounds check. #[inline(always)] - fn write16(&self, _value: u16, _offset: usize) + fn write16(&self, value: u16, offset: usize) where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 16-bit write") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_write(value, address) } } /// Infallible 32-bit write with compile-time bounds check. #[inline(always)] - fn write32(&self, _value: u32, _offset: usize) + fn write32(&self, value: u32, offset: usize) where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 32-bit write") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_write(value, address) } } /// Infallible 64-bit write with compile-time bounds check. #[inline(always)] - fn write64(&self, _value: u64, _offset: usize) + fn write64(&self, value: u64, offset: usize) where Self: IoKnownSize + IoCapable, { - build_error!("Backend does not support infallible 64-bit write") + let address = self.io_addr_assert::(offset); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_write(value, address) } } } @@ -534,14 +601,36 @@ fn io_addr_assert(&self, offset: usize) -> usize { } } -// MMIO regions support 8, 16, and 32-bit accesses. -impl IoCapable for Mmio {} -impl IoCapable for Mmio {} -impl IoCapable for Mmio {} +/// Implements [`IoCapable`] on `$mmio` for `$ty` using `$read_fn` and `$write_fn`. +macro_rules! impl_mmio_io_capable { + ($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) => { + $(#[$attr])* + impl IoCapable<$ty> for $mmio { + unsafe fn io_read(&self, address: usize) -> $ty { + // SAFETY: By the trait invariant `address` is a valid address for MMIO operations. + unsafe { bindings::$read_fn(address as *const c_void) } + } + unsafe fn io_write(&self, value: $ty, address: usize) { + // SAFETY: By the trait invariant `address` is a valid address for MMIO operations. + unsafe { bindings::$write_fn(value, address as *mut c_void) } + } + } + }; +} + +// MMIO regions support 8, 16, and 32-bit accesses. +impl_mmio_io_capable!(Mmio, u8, readb, writeb); +impl_mmio_io_capable!(Mmio, u16, readw, writew); +impl_mmio_io_capable!(Mmio, u32, readl, writel); // MMIO regions on 64-bit systems also support 64-bit accesses. -#[cfg(CONFIG_64BIT)] -impl IoCapable for Mmio {} +impl_mmio_io_capable!( + Mmio, + #[cfg(CONFIG_64BIT)] + u64, + readq, + writeq +); impl Io for Mmio { /// Returns the base address of this mapping. diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index fb6edab2aea7..4feca8033eb4 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -142,10 +142,41 @@ macro_rules! call_config_write { }; } +/// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`. +macro_rules! impl_config_space_io_capable { + ($ty:ty, $read_fn:ident, $write_fn:ident) => { + impl<'a, S: ConfigSpaceKind> IoCapable<$ty> for ConfigSpace<'a, S> { + unsafe fn io_read(&self, address: usize) -> $ty { + let mut val: $ty = 0; + + // Return value from C function is ignored in infallible accessors. + let _ret = + // SAFETY: By the type invariant `self.pdev` is a valid address. + // CAST: The offset is cast to `i32` because the C functions expect a 32-bit + // signed offset parameter. PCI configuration space size is at most 4096 bytes, + // so the value always fits within `i32` without truncation or sign change. + unsafe { bindings::$read_fn(self.pdev.as_raw(), address as i32, &mut val) }; + + val + } + + unsafe fn io_write(&self, value: $ty, address: usize) { + // Return value from C function is ignored in infallible accessors. + let _ret = + // SAFETY: By the type invariant `self.pdev` is a valid address. + // CAST: The offset is cast to `i32` because the C functions expect a 32-bit + // signed offset parameter. PCI configuration space size is at most 4096 bytes, + // so the value always fits within `i32` without truncation or sign change. + unsafe { bindings::$write_fn(self.pdev.as_raw(), address as i32, value) }; + } + } + }; +} + // PCI configuration space supports 8, 16, and 32-bit accesses. -impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} -impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} -impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} +impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte); +impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word); +impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword); impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> { /// Returns the base address of the I/O region. It is always 0 for configuration space. From 19103d4f93673c804ef82dd797cd2b935d0bf70f Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Feb 2026 15:00:16 +0900 Subject: [PATCH 103/712] rust: io: mem: use non-relaxed I/O ops in examples The `_relaxed` I/O variant methods are about to be replaced by a wrapper type exposing this access pattern with the regular methods of the `Io` trait. Thus replace the examples to use the regular I/O methods. Since these are examples, we want them to use the most standard ops anyway, and the relaxed variants were but an addition that was MMIO-specific. Reviewed-by: Daniel Almeida Acked-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260206-io-v2-2-71dea20a06e6@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/io/mem.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index 620022cff401..7dc78d547f7a 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -54,6 +54,7 @@ pub(crate) unsafe fn new(device: &'a Device, resource: &'a Resource) -> S /// use kernel::{ /// bindings, /// device::Core, + /// io::Io, /// of, /// platform, /// }; @@ -78,9 +79,9 @@ pub(crate) unsafe fn new(device: &'a Device, resource: &'a Resource) -> S /// let io = iomem.access(pdev.as_ref())?; /// /// // Read and write a 32-bit value at `offset`. - /// let data = io.read32_relaxed(offset); + /// let data = io.read32(offset); /// - /// io.write32_relaxed(data, offset); + /// io.write32(data, offset); /// /// # Ok(SampleDriver) /// } @@ -117,6 +118,7 @@ pub fn iomap_exclusive_sized( /// use kernel::{ /// bindings, /// device::Core, + /// io::Io, /// of, /// platform, /// }; @@ -141,9 +143,9 @@ pub fn iomap_exclusive_sized( /// /// let io = iomem.access(pdev.as_ref())?; /// - /// let data = io.try_read32_relaxed(offset)?; + /// let data = io.try_read32(offset)?; /// - /// io.try_write32_relaxed(data, offset)?; + /// io.try_write32(data, offset)?; /// /// # Ok(SampleDriver) /// } From 1d1c5c73d7e8f166b6b55ae06a3c509561b854cd Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Feb 2026 15:00:17 +0900 Subject: [PATCH 104/712] rust: io: provide Mmio relaxed ops through a wrapper type Relaxed I/O accessors for `Mmio` are currently implemented as an extra set of methods that mirror the ones defined in `Io`, but with the `_relaxed` suffix. This makes these methods impossible to use with generic code, which is a highly plausible proposition now that we have the `Io` trait. Address this by adding a new `RelaxedMmio` wrapper type for `Mmio` that provides its own `IoCapable` implementations relying on the relaxed C accessors. This makes it possible to use relaxed operations on a `Mmio` simply by wrapping it, and to use `RelaxedMmio` in code generic against `Io`. Acked-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Daniel Almeida Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260206-io-v2-3-71dea20a06e6@nvidia.com [ Use kernel import style in examples. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index ec78c614c959..8a0e35070153 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -742,3 +742,69 @@ pub unsafe fn from_raw(raw: &MmioRaw) -> &Self { call_mmio_write(writeq_relaxed) <- u64 ); } + +/// [`Mmio`] wrapper using relaxed accessors. +/// +/// This type provides an implementation of [`Io`] that uses relaxed I/O MMIO operands instead of +/// the regular ones. +/// +/// See [`Mmio::relaxed`] for a usage example. +#[repr(transparent)] +pub struct RelaxedMmio(Mmio); + +impl Io for RelaxedMmio { + #[inline] + fn addr(&self) -> usize { + self.0.addr() + } + + #[inline] + fn maxsize(&self) -> usize { + self.0.maxsize() + } +} + +impl IoKnownSize for RelaxedMmio { + const MIN_SIZE: usize = SIZE; +} + +impl Mmio { + /// Returns a [`RelaxedMmio`] reference that performs relaxed I/O operations. + /// + /// Relaxed accessors do not provide ordering guarantees with respect to DMA or memory accesses + /// and can be used when such ordering is not required. + /// + /// # Examples + /// + /// ```no_run + /// use kernel::io::{ + /// Io, + /// Mmio, + /// RelaxedMmio, + /// }; + /// + /// fn do_io(io: &Mmio<0x100>) { + /// // The access is performed using `readl_relaxed` instead of `readl`. + /// let v = io.relaxed().read32(0x10); + /// } + /// + /// ``` + pub fn relaxed(&self) -> &RelaxedMmio { + // SAFETY: `RelaxedMmio` is `#[repr(transparent)]` over `Mmio`, so `Mmio` and + // `RelaxedMmio` have identical layout. + unsafe { core::mem::transmute(self) } + } +} + +// MMIO regions support 8, 16, and 32-bit accesses. +impl_mmio_io_capable!(RelaxedMmio, u8, readb_relaxed, writeb_relaxed); +impl_mmio_io_capable!(RelaxedMmio, u16, readw_relaxed, writew_relaxed); +impl_mmio_io_capable!(RelaxedMmio, u32, readl_relaxed, writel_relaxed); +// MMIO regions on 64-bit systems also support 64-bit accesses. +impl_mmio_io_capable!( + RelaxedMmio, + #[cfg(CONFIG_64BIT)] + u64, + readq_relaxed, + writeq_relaxed +); From e385eb0d1c2c4d2dbc48d1bcbc44fd43cbb154a4 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Feb 2026 15:00:18 +0900 Subject: [PATCH 105/712] rust: io: remove legacy relaxed accessors of Mmio The relaxed access functionality is now provided by the `RelaxedMmio` wrapper type, and we don't have any user of the legacy methods left. Remove them. Reviewed-by: Daniel Almeida Acked-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260206-io-v2-4-71dea20a06e6@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 8a0e35070153..0d946e0c5d4f 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -701,46 +701,6 @@ pub unsafe fn from_raw(raw: &MmioRaw) -> &Self { // SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`. unsafe { &*core::ptr::from_ref(raw).cast() } } - - io_define_read!(infallible, pub read8_relaxed, call_mmio_read(readb_relaxed) -> u8); - io_define_read!(infallible, pub read16_relaxed, call_mmio_read(readw_relaxed) -> u16); - io_define_read!(infallible, pub read32_relaxed, call_mmio_read(readl_relaxed) -> u32); - io_define_read!( - infallible, - #[cfg(CONFIG_64BIT)] - pub read64_relaxed, - call_mmio_read(readq_relaxed) -> u64 - ); - - io_define_read!(fallible, pub try_read8_relaxed, call_mmio_read(readb_relaxed) -> u8); - io_define_read!(fallible, pub try_read16_relaxed, call_mmio_read(readw_relaxed) -> u16); - io_define_read!(fallible, pub try_read32_relaxed, call_mmio_read(readl_relaxed) -> u32); - io_define_read!( - fallible, - #[cfg(CONFIG_64BIT)] - pub try_read64_relaxed, - call_mmio_read(readq_relaxed) -> u64 - ); - - io_define_write!(infallible, pub write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); - io_define_write!(infallible, pub write16_relaxed, call_mmio_write(writew_relaxed) <- u16); - io_define_write!(infallible, pub write32_relaxed, call_mmio_write(writel_relaxed) <- u32); - io_define_write!( - infallible, - #[cfg(CONFIG_64BIT)] - pub write64_relaxed, - call_mmio_write(writeq_relaxed) <- u64 - ); - - io_define_write!(fallible, pub try_write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); - io_define_write!(fallible, pub try_write16_relaxed, call_mmio_write(writew_relaxed) <- u16); - io_define_write!(fallible, pub try_write32_relaxed, call_mmio_write(writel_relaxed) <- u32); - io_define_write!( - fallible, - #[cfg(CONFIG_64BIT)] - pub try_write64_relaxed, - call_mmio_write(writeq_relaxed) <- u64 - ); } /// [`Mmio`] wrapper using relaxed accessors. From 50aad5510fbbf8dd8f5f63380e1a1e7ae73216c4 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Feb 2026 15:00:19 +0900 Subject: [PATCH 106/712] rust: pci: io: remove overloaded Io methods of ConfigSpace Since `ConfigSpace` now has the relevant implementations of `IoCapable`, the default methods of `Io` can be used in place of the overloaded ones. Remove them as well as the macros generating them. Reviewed-by: Daniel Almeida Acked-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260206-io-v2-5-71dea20a06e6@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 4 --- rust/kernel/pci/io.rs | 70 ------------------------------------------- 2 files changed, 74 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 0d946e0c5d4f..2ae2420be344 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -215,7 +215,6 @@ macro_rules! call_mmio_write { /// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the /// `$call_macro`. /// * `$type_name:ty` - The Rust type of the value being read (e.g., `u8`, `u32`). -#[macro_export] macro_rules! io_define_read { (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) -> $type_name:ty) => { @@ -249,7 +248,6 @@ macro_rules! io_define_read { } }; } -pub use io_define_read; /// Generates an accessor method for writing to an I/O backend. /// @@ -274,7 +272,6 @@ macro_rules! io_define_read { /// `$call_macro`. /// * `$type_name:ty` - The Rust type of the value being written (e.g., `u8`, `u32`). Note the use /// of `<-` before the type to denote a write operation. -#[macro_export] macro_rules! io_define_write { (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) <- $type_name:ty) => { @@ -306,7 +303,6 @@ macro_rules! io_define_write { } }; } -pub use io_define_write; /// Checks whether an access of type `U` at the given `offset` /// is valid within this region. diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 4feca8033eb4..ae78676c927f 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -8,8 +8,6 @@ device, devres::Devres, io::{ - io_define_read, - io_define_write, Io, IoCapable, IoKnownSize, @@ -85,63 +83,6 @@ pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> { _marker: PhantomData, } -/// Internal helper macros used to invoke C PCI configuration space read functions. -/// -/// This macro is intended to be used by higher-level PCI configuration space access macros -/// (io_define_read) and provides a unified expansion for infallible vs. fallible read semantics. It -/// emits a direct call into the corresponding C helper and performs the required cast to the Rust -/// return type. -/// -/// # Parameters -/// -/// * `$c_fn` – The C function performing the PCI configuration space write. -/// * `$self` – The I/O backend object. -/// * `$ty` – The type of the value to read. -/// * `$addr` – The PCI configuration space offset to read. -/// -/// This macro does not perform any validation; all invariants must be upheld by the higher-level -/// abstraction invoking it. -macro_rules! call_config_read { - (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr) => {{ - let mut val: $ty = 0; - // SAFETY: By the type invariant `$self.pdev` is a valid address. - // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset - // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits - // within `i32` without truncation or sign change. - // Return value from C function is ignored in infallible accessors. - let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, &mut val) }; - val - }}; -} - -/// Internal helper macros used to invoke C PCI configuration space write functions. -/// -/// This macro is intended to be used by higher-level PCI configuration space access macros -/// (io_define_write) and provides a unified expansion for infallible vs. fallible read semantics. -/// It emits a direct call into the corresponding C helper and performs the required cast to the -/// Rust return type. -/// -/// # Parameters -/// -/// * `$c_fn` – The C function performing the PCI configuration space write. -/// * `$self` – The I/O backend object. -/// * `$ty` – The type of the written value. -/// * `$addr` – The configuration space offset to write. -/// * `$value` – The value to write. -/// -/// This macro does not perform any validation; all invariants must be upheld by the higher-level -/// abstraction invoking it. -macro_rules! call_config_write { - (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => { - // SAFETY: By the type invariant `$self.pdev` is a valid address. - // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset - // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits - // within `i32` without truncation or sign change. - // Return value from C function is ignored in infallible accessors. - let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, $value) }; - }; -} - /// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`. macro_rules! impl_config_space_io_capable { ($ty:ty, $read_fn:ident, $write_fn:ident) => { @@ -190,17 +131,6 @@ fn addr(&self) -> usize { fn maxsize(&self) -> usize { self.pdev.cfg_size().into_raw() } - - // PCI configuration space does not support fallible operations. - // The default implementations from the Io trait are not used. - - io_define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8); - io_define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16); - io_define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32); - - io_define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8); - io_define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16); - io_define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32); } impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> { From 6291ee23da4224a7584ece2d292104e872b9b5fc Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Feb 2026 15:00:20 +0900 Subject: [PATCH 107/712] rust: io: remove overloaded Io methods of Mmio Since `Mmio` now has the relevant implementations of `IoCapable`, the default methods of `Io` can be used in place of the overloaded ones. Remove them as well as the macros generating them. Reviewed-by: Daniel Almeida Acked-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260206-io-v2-6-71dea20a06e6@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 207 ---------------------------------------------- 1 file changed, 207 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 2ae2420be344..947eb378d297 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -137,173 +137,6 @@ pub fn maxsize(&self) -> usize { #[repr(transparent)] pub struct Mmio(MmioRaw); -/// Internal helper macros used to invoke C MMIO read functions. -/// -/// This macro is intended to be used by higher-level MMIO access macros (io_define_read) and -/// provides a unified expansion for infallible vs. fallible read semantics. It emits a direct call -/// into the corresponding C helper and performs the required cast to the Rust return type. -/// -/// # Parameters -/// -/// * `$c_fn` – The C function performing the MMIO read. -/// * `$self` – The I/O backend object. -/// * `$ty` – The type of the value to be read. -/// * `$addr` – The MMIO address to read. -/// -/// This macro does not perform any validation; all invariants must be upheld by the higher-level -/// abstraction invoking it. -macro_rules! call_mmio_read { - (infallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => { - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn($addr as *const c_void) as $type } - }; - - (fallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => {{ - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - Ok(unsafe { bindings::$c_fn($addr as *const c_void) as $type }) - }}; -} - -/// Internal helper macros used to invoke C MMIO write functions. -/// -/// This macro is intended to be used by higher-level MMIO access macros (io_define_write) and -/// provides a unified expansion for infallible vs. fallible write semantics. It emits a direct call -/// into the corresponding C helper and performs the required cast to the Rust return type. -/// -/// # Parameters -/// -/// * `$c_fn` – The C function performing the MMIO write. -/// * `$self` – The I/O backend object. -/// * `$ty` – The type of the written value. -/// * `$addr` – The MMIO address to write. -/// * `$value` – The value to write. -/// -/// This macro does not perform any validation; all invariants must be upheld by the higher-level -/// abstraction invoking it. -macro_rules! call_mmio_write { - (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => { - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn($value, $addr as *mut c_void) } - }; - - (fallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {{ - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn($value, $addr as *mut c_void) }; - Ok(()) - }}; -} - -/// Generates an accessor method for reading from an I/O backend. -/// -/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked -/// (infallible) or runtime bounds-checked (fallible) read methods. It abstracts the address -/// calculation and bounds checking, and delegates the actual I/O read operation to a specified -/// helper macro, making it generic over different I/O backends. -/// -/// # Parameters -/// -/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on -/// `IoKnownSize` for compile-time checks and returns the value directly. `fallible` performs -/// runtime checks against `maxsize()` and returns a `Result`. -/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g., -/// `#[cfg(CONFIG_64BIT)]` or inline directives). -/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`). -/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `read32`, -/// `try_read8`). -/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call -/// (e.g., `call_mmio_read`). -/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the -/// `$call_macro`. -/// * `$type_name:ty` - The Rust type of the value being read (e.g., `u8`, `u32`). -macro_rules! io_define_read { - (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) -> - $type_name:ty) => { - /// Read IO data from a given offset known at compile time. - /// - /// Bound checks are performed on compile time, hence if the offset is not known at compile - /// time, the build will fail. - $(#[$attr])* - // Always inline to optimize out error path of `io_addr_assert`. - #[inline(always)] - $vis fn $name(&self, offset: usize) -> $type_name { - let addr = self.io_addr_assert::<$type_name>(offset); - - // SAFETY: By the type invariant `addr` is a valid address for IO operations. - $call_macro!(infallible, $c_fn, self, $type_name, addr) - } - }; - - (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) -> - $type_name:ty) => { - /// Read IO data from a given offset. - /// - /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is - /// out of bounds. - $(#[$attr])* - $vis fn $try_name(&self, offset: usize) -> Result<$type_name> { - let addr = self.io_addr::<$type_name>(offset)?; - - // SAFETY: By the type invariant `addr` is a valid address for IO operations. - $call_macro!(fallible, $c_fn, self, $type_name, addr) - } - }; -} - -/// Generates an accessor method for writing to an I/O backend. -/// -/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked -/// (infallible) or runtime bounds-checked (fallible) write methods. It abstracts the address -/// calculation and bounds checking, and delegates the actual I/O write operation to a specified -/// helper macro, making it generic over different I/O backends. -/// -/// # Parameters -/// -/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on -/// `IoKnownSize` for compile-time checks and returns `()`. `fallible` performs runtime checks -/// against `maxsize()` and returns a `Result`. -/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g., -/// `#[cfg(CONFIG_64BIT)]` or inline directives). -/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`). -/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `write32`, -/// `try_write8`). -/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call -/// (e.g., `call_mmio_write`). -/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the -/// `$call_macro`. -/// * `$type_name:ty` - The Rust type of the value being written (e.g., `u8`, `u32`). Note the use -/// of `<-` before the type to denote a write operation. -macro_rules! io_define_write { - (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) <- - $type_name:ty) => { - /// Write IO data from a given offset known at compile time. - /// - /// Bound checks are performed on compile time, hence if the offset is not known at compile - /// time, the build will fail. - $(#[$attr])* - // Always inline to optimize out error path of `io_addr_assert`. - #[inline(always)] - $vis fn $name(&self, value: $type_name, offset: usize) { - let addr = self.io_addr_assert::<$type_name>(offset); - - $call_macro!(infallible, $c_fn, self, $type_name, addr, value); - } - }; - - (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) <- - $type_name:ty) => { - /// Write IO data from a given offset. - /// - /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is - /// out of bounds. - $(#[$attr])* - $vis fn $try_name(&self, value: $type_name, offset: usize) -> Result { - let addr = self.io_addr::<$type_name>(offset)?; - - $call_macro!(fallible, $c_fn, self, $type_name, addr, value) - } - }; -} - /// Checks whether an access of type `U` at the given `offset` /// is valid within this region. #[inline] @@ -640,46 +473,6 @@ fn addr(&self) -> usize { fn maxsize(&self) -> usize { self.0.maxsize() } - - io_define_read!(fallible, try_read8, call_mmio_read(readb) -> u8); - io_define_read!(fallible, try_read16, call_mmio_read(readw) -> u16); - io_define_read!(fallible, try_read32, call_mmio_read(readl) -> u32); - io_define_read!( - fallible, - #[cfg(CONFIG_64BIT)] - try_read64, - call_mmio_read(readq) -> u64 - ); - - io_define_write!(fallible, try_write8, call_mmio_write(writeb) <- u8); - io_define_write!(fallible, try_write16, call_mmio_write(writew) <- u16); - io_define_write!(fallible, try_write32, call_mmio_write(writel) <- u32); - io_define_write!( - fallible, - #[cfg(CONFIG_64BIT)] - try_write64, - call_mmio_write(writeq) <- u64 - ); - - io_define_read!(infallible, read8, call_mmio_read(readb) -> u8); - io_define_read!(infallible, read16, call_mmio_read(readw) -> u16); - io_define_read!(infallible, read32, call_mmio_read(readl) -> u32); - io_define_read!( - infallible, - #[cfg(CONFIG_64BIT)] - read64, - call_mmio_read(readq) -> u64 - ); - - io_define_write!(infallible, write8, call_mmio_write(writeb) <- u8); - io_define_write!(infallible, write16, call_mmio_write(writew) <- u16); - io_define_write!(infallible, write32, call_mmio_write(writel) <- u32); - io_define_write!( - infallible, - #[cfg(CONFIG_64BIT)] - write64, - call_mmio_write(writeq) <- u64 - ); } impl IoKnownSize for Mmio { From 3cc319d5f433a4d560cc944ecfb1fe50b866cd66 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:11 +0900 Subject: [PATCH 108/712] rust: enable the `generic_arg_infer` feature This feature is stable since 1.89, and used in subsequent patches. Reviewed-by: Gary Guo Tested-by: Dirk Behme Acked-by: Miguel Ojeda Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-1-86805b2f7e9d@nvidia.com [ Resolve merge conflict. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 3 +++ scripts/Makefile.build | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index d93292d47420..34b924819288 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -41,6 +41,9 @@ // Stable since Rust 1.84.0. #![feature(strict_provenance)] // +// Stable since Rust 1.89.0. +#![feature(generic_arg_infer)] +// // Expected to become stable. #![feature(arbitrary_self_types)] // diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 3652b85be545..010d08472fb2 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -316,12 +316,13 @@ $(obj)/%.lst: $(obj)/%.c FORCE # `feature(offset_of_nested)`, `feature(raw_ref_op)`. # - Stable since Rust 1.84.0: `feature(strict_provenance)`. # - Stable since Rust 1.87.0: `feature(asm_goto)`. +# - Stable since Rust 1.89.0: `feature(generic_arg_infer)`. # - Expected to become stable: `feature(arbitrary_self_types)`. # - To be determined: `feature(used_with_arg)`. # # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on # the unstable features in use. -rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,slice_ptr_len,strict_provenance,used_with_arg +rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,generic_arg_infer,lint_reasons,offset_of_nested,raw_ref_op,slice_ptr_len,strict_provenance,used_with_arg # `--out-dir` is required to avoid temporaries being created by `rustc` in the # current working directory, which may be not accessible in the out-of-tree From c59a2d14cd248c77457b821b15c72e6a6a268553 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:12 +0900 Subject: [PATCH 109/712] rust: num: add `shr` and `shl` methods to `Bounded` Shifting a `Bounded` left or right changes the number of bits required to represent the value. Add methods that perform the shift and return a `Bounded` with the appropriately adjusted bit width. These methods are particularly useful for bitfield extraction. Suggested-by: Alice Ryhl Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Reviewed-by: Daniel Almeida Tested-by: Dirk Behme Acked-by: Miguel Ojeda Acked-by: Yury Norov Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-2-86805b2f7e9d@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/num/bounded.rs | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index fa81acbdc8c2..2f5f13ecd3d6 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -473,6 +473,48 @@ pub fn cast(self) -> Bounded // `N` bits, and with the same signedness. unsafe { Bounded::__new(value) } } + + /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >= + /// N - SHIFT`. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::Bounded; + /// + /// let v = Bounded::::new::<0xff00>(); + /// let v_shifted: Bounded:: = v.shr::<8, _>(); + /// + /// assert_eq!(v_shifted.get(), 0xff); + /// ``` + pub fn shr(self) -> Bounded { + const { assert!(RES + SHIFT >= N) } + + // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to + // represent the shifted value by as much, and just asserted that `RES >= N - SHIFT`. + unsafe { Bounded::__new(self.0 >> SHIFT) } + } + + /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >= + /// N + SHIFT`. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::Bounded; + /// + /// let v = Bounded::::new::<0xff>(); + /// let v_shifted: Bounded:: = v.shl::<8, _>(); + /// + /// assert_eq!(v_shifted.get(), 0xff00); + /// ``` + pub fn shl(self) -> Bounded { + const { assert!(RES >= N + SHIFT) } + + // SAFETY: We shift the value left by `SHIFT`, augmenting the number of bits needed to + // represent the shifted value by as much, and just asserted that `RES >= N + SHIFT`. + unsafe { Bounded::__new(self.0 << SHIFT) } + } } impl Deref for Bounded From 164f8634bfd8eef7b90c429156c59706635cfb88 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:13 +0900 Subject: [PATCH 110/712] rust: num: add `into_bool` method to `Bounded` Single-bit numbers are typically treated as booleans. There is an `Into` implementation for those, but invoking it from contexts that lack type expectations is not always convenient. Add an `into_bool` method as a simpler shortcut. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Reviewed-by: Daniel Almeida Reviewed-by: Yury Norov Tested-by: Dirk Behme Acked-by: Miguel Ojeda Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-3-86805b2f7e9d@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/num/bounded.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index 2f5f13ecd3d6..d28d118abd8e 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -1101,3 +1101,24 @@ fn from(value: bool) -> Self { unsafe { Self::__new(T::from(value)) } } } + +impl Bounded +where + T: Integer + Zeroable, +{ + /// Converts this [`Bounded`] into a [`bool`]. + /// + /// This is a shorter way of writing `bool::from(self)`. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::Bounded; + /// + /// assert_eq!(Bounded::::new::<0>().into_bool(), false); + /// assert_eq!(Bounded::::new::<1>().into_bool(), true); + /// ``` + pub fn into_bool(self) -> bool { + self.into() + } +} From 7836ec76ec5cd8d45759a6a360b1fda4829d2734 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:14 +0900 Subject: [PATCH 111/712] rust: num: make Bounded::get const There is a need to access the inner value of a `Bounded` in const context, notably for bitfields and registers. Remove the invariant check of `Bounded::get`, which allows us to make it const. Reviewed-by: Gary Guo Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-4-86805b2f7e9d@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/num/bounded.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index d28d118abd8e..bbab6bbcb315 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -379,6 +379,9 @@ pub fn from_expr(expr: T) -> Self { /// Returns the wrapped value as the backing type. /// + /// This is similar to the [`Deref`] implementation, but doesn't enforce the size invariant of + /// the [`Bounded`], which might produce slightly less optimal code. + /// /// # Examples /// /// ``` @@ -387,8 +390,8 @@ pub fn from_expr(expr: T) -> Self { /// let v = Bounded::::new::<7>(); /// assert_eq!(v.get(), 7u32); /// ``` - pub fn get(self) -> T { - *self.deref() + pub const fn get(self) -> T { + self.0 } /// Increases the number of bits usable for `self`. From 498823541be1e2d9f947b37a10cc98e681da9828 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:15 +0900 Subject: [PATCH 112/712] rust: io: add IoLoc type and generic I/O accessors I/O accesses are defined by the following properties: - An I/O location, which consists of a start address, a width, and a type to interpret the read value as, - A value, which is returned for reads or provided for writes. Introduce the `IoLoc` trait, which allows implementing types to fully specify an I/O location. This allows I/O operations to be made generic through the new `read` and `write` methods. This design will allow us to factorize the I/O code working with primitives, and to introduce ways to perform I/O with a higher degree of control through register types. Co-developed-by: Gary Guo Signed-off-by: Gary Guo Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-5-86805b2f7e9d@nvidia.com [ Fix incorrect reference to io_addr_assert() in try_update(). - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 947eb378d297..e1e9802bb603 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -173,6 +173,30 @@ pub trait IoCapable { unsafe fn io_write(&self, value: T, address: usize); } +/// Describes a given I/O location: its offset, width, and type to convert the raw value from and +/// into. +/// +/// This trait is the key abstraction allowing [`Io::read`], [`Io::write`], and [`Io::update`] (and +/// their fallible [`try_read`](Io::try_read), [`try_write`](Io::try_write) and +/// [`try_update`](Io::try_update) counterparts) to work uniformly with both raw [`usize`] offsets +/// (for primitive types like [`u32`]) and typed ones. +/// +/// An `IoLoc` carries three pieces of information: +/// +/// - The offset to access (returned by [`IoLoc::offset`]), +/// - The width of the access (determined by [`IoLoc::IoType`]), +/// - The type `T` in which the raw data is returned or provided. +/// +/// `T` and `IoLoc::IoType` may differ: for instance, a typed register has `T` = the register type +/// with its bitfields, and `IoType` = its backing primitive (e.g. `u32`). +pub trait IoLoc { + /// Size ([`u8`], [`u16`], etc) of the I/O performed on the returned [`offset`](IoLoc::offset). + type IoType: Into + From; + + /// Consumes `self` and returns the offset of this location. + fn offset(self) -> usize; +} + /// Types implementing this trait (e.g. MMIO BARs or PCI config regions) /// can perform I/O operations on regions of memory. /// @@ -406,6 +430,106 @@ fn write64(&self, value: u64, offset: usize) // SAFETY: `address` has been validated by `io_addr_assert`. unsafe { self.io_write(value, address) } } + + /// Generic fallible read with runtime bounds check. + #[inline(always)] + fn try_read(&self, location: L) -> Result + where + L: IoLoc, + Self: IoCapable, + { + let address = self.io_addr::(location.offset())?; + + // SAFETY: `address` has been validated by `io_addr`. + Ok(unsafe { self.io_read(address) }.into()) + } + + /// Generic fallible write with runtime bounds check. + #[inline(always)] + fn try_write(&self, location: L, value: T) -> Result + where + L: IoLoc, + Self: IoCapable, + { + let address = self.io_addr::(location.offset())?; + let io_value = value.into(); + + // SAFETY: `address` has been validated by `io_addr`. + unsafe { self.io_write(io_value, address) } + + Ok(()) + } + + /// Generic fallible update with runtime bounds check. + /// + /// Note: this does not perform any synchronization. The caller is responsible for ensuring + /// exclusive access if required. + #[inline(always)] + fn try_update(&self, location: L, f: F) -> Result + where + L: IoLoc, + Self: IoCapable, + F: FnOnce(T) -> T, + { + let address = self.io_addr::(location.offset())?; + + // SAFETY: `address` has been validated by `io_addr`. + let value: T = unsafe { self.io_read(address) }.into(); + let io_value = f(value).into(); + + // SAFETY: `address` has been validated by `io_addr`. + unsafe { self.io_write(io_value, address) } + + Ok(()) + } + + /// Generic infallible read with compile-time bounds check. + #[inline(always)] + fn read(&self, location: L) -> T + where + L: IoLoc, + Self: IoKnownSize + IoCapable, + { + let address = self.io_addr_assert::(location.offset()); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_read(address) }.into() + } + + /// Generic infallible write with compile-time bounds check. + #[inline(always)] + fn write(&self, location: L, value: T) + where + L: IoLoc, + Self: IoKnownSize + IoCapable, + { + let address = self.io_addr_assert::(location.offset()); + let io_value = value.into(); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_write(io_value, address) } + } + + /// Generic infallible update with compile-time bounds check. + /// + /// Note: this does not perform any synchronization. The caller is responsible for ensuring + /// exclusive access if required. + #[inline(always)] + fn update(&self, location: L, f: F) + where + L: IoLoc, + Self: IoKnownSize + IoCapable + Sized, + F: FnOnce(T) -> T, + { + let address = self.io_addr_assert::(location.offset()); + + // SAFETY: `address` has been validated by `io_addr_assert`. + let value: T = unsafe { self.io_read(address) }.into(); + let io_value = f(value).into(); + + // SAFETY: `address` has been validated by `io_addr_assert`. + unsafe { self.io_write(io_value, address) } + } } /// Trait for types with a known size at compile time. From 147b41ba23d63f43ed34e6940417a5506e323370 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:16 +0900 Subject: [PATCH 113/712] rust: io: use generic read/write accessors for primitive accesses By providing the required `IoLoc` implementations on `usize`, we can leverage the generic accessors and reduce the number of unsafe blocks in the module. This also allows us to directly call the generic `read/write/update` methods with primitive types, so add examples illustrating this. Signed-off-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260314-register-v9-6-86805b2f7e9d@nvidia.com [ Slightly improve wording in doc-comment. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 218 +++++++++++++++++++++++++++++++--------------- 1 file changed, 150 insertions(+), 68 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index e1e9802bb603..c51a87b9169b 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -197,6 +197,26 @@ pub trait IoLoc { fn offset(self) -> usize; } +/// Implements [`IoLoc<$ty>`] for [`usize`], allowing [`usize`] to be used as a parameter of +/// [`Io::read`] and [`Io::write`]. +macro_rules! impl_usize_ioloc { + ($($ty:ty),*) => { + $( + impl IoLoc<$ty> for usize { + type IoType = $ty; + + #[inline(always)] + fn offset(self) -> usize { + self + } + } + )* + } +} + +// Provide the ability to read any primitive type from a [`usize`]. +impl_usize_ioloc!(u8, u16, u32, u64); + /// Types implementing this trait (e.g. MMIO BARs or PCI config regions) /// can perform I/O operations on regions of memory. /// @@ -241,10 +261,7 @@ fn try_read8(&self, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - Ok(unsafe { self.io_read(address) }) + self.try_read(offset) } /// Fallible 16-bit read with runtime bounds check. @@ -253,10 +270,7 @@ fn try_read16(&self, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - Ok(unsafe { self.io_read(address) }) + self.try_read(offset) } /// Fallible 32-bit read with runtime bounds check. @@ -265,10 +279,7 @@ fn try_read32(&self, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - Ok(unsafe { self.io_read(address) }) + self.try_read(offset) } /// Fallible 64-bit read with runtime bounds check. @@ -277,10 +288,7 @@ fn try_read64(&self, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - Ok(unsafe { self.io_read(address) }) + self.try_read(offset) } /// Fallible 8-bit write with runtime bounds check. @@ -289,11 +297,7 @@ fn try_write8(&self, value: u8, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - unsafe { self.io_write(value, address) }; - Ok(()) + self.try_write(offset, value) } /// Fallible 16-bit write with runtime bounds check. @@ -302,11 +306,7 @@ fn try_write16(&self, value: u16, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - unsafe { self.io_write(value, address) }; - Ok(()) + self.try_write(offset, value) } /// Fallible 32-bit write with runtime bounds check. @@ -315,11 +315,7 @@ fn try_write32(&self, value: u32, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - unsafe { self.io_write(value, address) }; - Ok(()) + self.try_write(offset, value) } /// Fallible 64-bit write with runtime bounds check. @@ -328,11 +324,7 @@ fn try_write64(&self, value: u64, offset: usize) -> Result where Self: IoCapable, { - let address = self.io_addr::(offset)?; - - // SAFETY: `address` has been validated by `io_addr`. - unsafe { self.io_write(value, address) }; - Ok(()) + self.try_write(offset, value) } /// Infallible 8-bit read with compile-time bounds check. @@ -341,10 +333,7 @@ fn read8(&self, offset: usize) -> u8 where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_read(address) } + self.read(offset) } /// Infallible 16-bit read with compile-time bounds check. @@ -353,10 +342,7 @@ fn read16(&self, offset: usize) -> u16 where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_read(address) } + self.read(offset) } /// Infallible 32-bit read with compile-time bounds check. @@ -365,10 +351,7 @@ fn read32(&self, offset: usize) -> u32 where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_read(address) } + self.read(offset) } /// Infallible 64-bit read with compile-time bounds check. @@ -377,10 +360,7 @@ fn read64(&self, offset: usize) -> u64 where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_read(address) } + self.read(offset) } /// Infallible 8-bit write with compile-time bounds check. @@ -389,10 +369,7 @@ fn write8(&self, value: u8, offset: usize) where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_write(value, address) } + self.write(offset, value) } /// Infallible 16-bit write with compile-time bounds check. @@ -401,10 +378,7 @@ fn write16(&self, value: u16, offset: usize) where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_write(value, address) } + self.write(offset, value) } /// Infallible 32-bit write with compile-time bounds check. @@ -413,10 +387,7 @@ fn write32(&self, value: u32, offset: usize) where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_write(value, address) } + self.write(offset, value) } /// Infallible 64-bit write with compile-time bounds check. @@ -425,13 +396,31 @@ fn write64(&self, value: u64, offset: usize) where Self: IoKnownSize + IoCapable, { - let address = self.io_addr_assert::(offset); - - // SAFETY: `address` has been validated by `io_addr_assert`. - unsafe { self.io_write(value, address) } + self.write(offset, value) } /// Generic fallible read with runtime bounds check. + /// + /// # Examples + /// + /// Read a primitive type from an I/O address: + /// + /// ```no_run + /// use kernel::io::{ + /// Io, + /// Mmio, + /// }; + /// + /// fn do_reads(io: &Mmio) -> Result { + /// // 32-bit read from address `0x10`. + /// let v: u32 = io.try_read(0x10)?; + /// + /// // 8-bit read from address `0xfff`. + /// let v: u8 = io.try_read(0xfff)?; + /// + /// Ok(()) + /// } + /// ``` #[inline(always)] fn try_read(&self, location: L) -> Result where @@ -445,6 +434,27 @@ fn try_read(&self, location: L) -> Result } /// Generic fallible write with runtime bounds check. + /// + /// # Examples + /// + /// Write a primitive type to an I/O address: + /// + /// ```no_run + /// use kernel::io::{ + /// Io, + /// Mmio, + /// }; + /// + /// fn do_writes(io: &Mmio) -> Result { + /// // 32-bit write of value `1` at address `0x10`. + /// io.try_write(0x10, 1u32)?; + /// + /// // 8-bit write of value `0xff` at address `0xfff`. + /// io.try_write(0xfff, 0xffu8)?; + /// + /// Ok(()) + /// } + /// ``` #[inline(always)] fn try_write(&self, location: L, value: T) -> Result where @@ -464,6 +474,23 @@ fn try_write(&self, location: L, value: T) -> Result /// /// Note: this does not perform any synchronization. The caller is responsible for ensuring /// exclusive access if required. + /// + /// # Examples + /// + /// Read the u32 value at address `0x10`, increment it, and store the updated value back: + /// + /// ```no_run + /// use kernel::io::{ + /// Io, + /// Mmio, + /// }; + /// + /// fn do_update(io: &Mmio<0x1000>) -> Result { + /// io.try_update(0x10, |v: u32| { + /// v + 1 + /// }) + /// } + /// ``` #[inline(always)] fn try_update(&self, location: L, f: F) -> Result where @@ -484,6 +511,25 @@ fn try_update(&self, location: L, f: F) -> Result } /// Generic infallible read with compile-time bounds check. + /// + /// # Examples + /// + /// Read a primitive type from an I/O address: + /// + /// ```no_run + /// use kernel::io::{ + /// Io, + /// Mmio, + /// }; + /// + /// fn do_reads(io: &Mmio<0x1000>) { + /// // 32-bit read from address `0x10`. + /// let v: u32 = io.read(0x10); + /// + /// // 8-bit read from the top of the I/O space. + /// let v: u8 = io.read(0xfff); + /// } + /// ``` #[inline(always)] fn read(&self, location: L) -> T where @@ -497,6 +543,25 @@ fn read(&self, location: L) -> T } /// Generic infallible write with compile-time bounds check. + /// + /// # Examples + /// + /// Write a primitive type to an I/O address: + /// + /// ```no_run + /// use kernel::io::{ + /// Io, + /// Mmio, + /// }; + /// + /// fn do_writes(io: &Mmio<0x1000>) { + /// // 32-bit write of value `1` at address `0x10`. + /// io.write(0x10, 1u32); + /// + /// // 8-bit write of value `0xff` at the top of the I/O space. + /// io.write(0xfff, 0xffu8); + /// } + /// ``` #[inline(always)] fn write(&self, location: L, value: T) where @@ -514,6 +579,23 @@ fn write(&self, location: L, value: T) /// /// Note: this does not perform any synchronization. The caller is responsible for ensuring /// exclusive access if required. + /// + /// # Examples + /// + /// Read the u32 value at address `0x10`, increment it, and store the updated value back: + /// + /// ```no_run + /// use kernel::io::{ + /// Io, + /// Mmio, + /// }; + /// + /// fn do_update(io: &Mmio<0x1000>) { + /// io.update(0x10, |v: u32| { + /// v + 1 + /// }) + /// } + /// ``` #[inline(always)] fn update(&self, location: L, f: F) where From 20ba6a1dbcb957152f6d858015b3a3311dd6da49 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:17 +0900 Subject: [PATCH 114/712] rust: io: add `register!` macro Add a macro for defining hardware register types with I/O accessors. Each register field is represented as a `Bounded` of the appropriate bit width, ensuring field values are never silently truncated. Fields can optionally be converted to/from custom types, either fallibly or infallibly. The address of registers can be direct, relative, or indexed, supporting most of the patterns in which registers are arranged. Suggested-by: Danilo Krummrich Link: https://lore.kernel.org/all/20250306222336.23482-6-dakr@kernel.org/ Co-developed-by: Gary Guo Signed-off-by: Gary Guo Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-7-86805b2f7e9d@nvidia.com [ * Improve wording and formatting of doc-comments, * Import build_assert!(), * Add missing inline annotations, * Call static_assert!() with absolute path, * Use expect instead of allow. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 5 +- rust/kernel/io/register.rs | 1229 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1233 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/io/register.rs diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index c51a87b9169b..4950cecf30ca 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -11,8 +11,10 @@ pub mod mem; pub mod poll; +pub mod register; pub mod resource; +pub use crate::register; pub use resource::Resource; /// Physical address type. @@ -179,7 +181,8 @@ pub trait IoCapable { /// This trait is the key abstraction allowing [`Io::read`], [`Io::write`], and [`Io::update`] (and /// their fallible [`try_read`](Io::try_read), [`try_write`](Io::try_write) and /// [`try_update`](Io::try_update) counterparts) to work uniformly with both raw [`usize`] offsets -/// (for primitive types like [`u32`]) and typed ones. +/// (for primitive types like [`u32`]) and typed ones (like those generated by the [`register!`] +/// macro). /// /// An `IoLoc` carries three pieces of information: /// diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs new file mode 100644 index 000000000000..dbd458aaa761 --- /dev/null +++ b/rust/kernel/io/register.rs @@ -0,0 +1,1229 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Macro to define register layout and accessors. +//! +//! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for +//! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such +//! type comes with its own field accessors that can return an error if a field's value is invalid. +//! +//! Note: most of the items in this module are public so they can be referenced by the macro, but +//! most are not to be used directly by users. Outside of the `register!` macro itself, the only +//! items you might want to import from this module are [`WithBase`] and [`Array`]. +//! +//! # Simple example +//! +//! ```no_run +//! use kernel::io::register; +//! +//! register! { +//! /// Basic information about the chip. +//! pub BOOT_0(u32) @ 0x00000100 { +//! /// Vendor ID. +//! 15:8 vendor_id; +//! /// Major revision of the chip. +//! 7:4 major_revision; +//! /// Minor revision of the chip. +//! 3:0 minor_revision; +//! } +//! } +//! ``` +//! +//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an +//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4 +//! least significant bits of the type. +//! +//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their +//! getter method, which is named after them. They also have setter methods prefixed with `with_` +//! for runtime values and `with_const_` for constant values. All setters return the updated +//! register value. +//! +//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and +//! `?=>` syntaxes. +//! +//! If present, doc comments above register or fields definitions are added to the relevant item +//! they document (the register type itself, or the field's setter and getter methods). +//! +//! Note that multiple registers can be defined in a single `register!` invocation. This can be +//! useful to group related registers together. +//! +//! Here is how the register defined above can be used in code: +//! +//! +//! ```no_run +//! use kernel::{ +//! io::{ +//! register, +//! Io, +//! IoLoc, +//! }, +//! num::Bounded, +//! }; +//! # use kernel::io::Mmio; +//! # register! { +//! # pub BOOT_0(u32) @ 0x00000100 { +//! # 15:8 vendor_id; +//! # 7:4 major_revision; +//! # 3:0 minor_revision; +//! # } +//! # } +//! # fn test(io: &Mmio<0x1000>) { +//! # fn obtain_vendor_id() -> u8 { 0xff } +//! +//! // Read from the register's defined offset (0x100). +//! let boot0 = io.read(BOOT_0); +//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get()); +//! +//! // Update some fields and write the new value back. +//! let new_boot0 = boot0 +//! // Constant values. +//! .with_const_major_revision::<3>() +//! .with_const_minor_revision::<10>() +//! // Runtime value. +//! .with_vendor_id(obtain_vendor_id()); +//! io.write((), new_boot0); +//! +//! // Or, build a new value from zero and write it: +//! io.write((), BOOT_0::zeroed() +//! .with_const_major_revision::<3>() +//! .with_const_minor_revision::<10>() +//! .with_vendor_id(obtain_vendor_id()) +//! ); +//! +//! // Or, read and update the register in a single step. +//! io.update(BOOT_0, |r| r +//! .with_const_major_revision::<3>() +//! .with_const_minor_revision::<10>() +//! .with_vendor_id(obtain_vendor_id()) +//! ); +//! +//! // Constant values can also be built using the const setters. +//! const V: BOOT_0 = pin_init::zeroed::() +//! .with_const_major_revision::<3>() +//! .with_const_minor_revision::<10>(); +//! # } +//! ``` +//! +//! For more extensive documentation about how to define registers, see the +//! [`register!`](kernel::io::register!) macro. + +use core::marker::PhantomData; + +use crate::io::IoLoc; + +use kernel::build_assert; + +/// Trait implemented by all registers. +pub trait Register: Sized { + /// Backing primitive type of the register. + type Storage: Into + From; + + /// Start offset of the register. + /// + /// The interpretation of this offset depends on the type of the register. + const OFFSET: usize; +} + +/// Trait implemented by registers with a fixed offset. +pub trait FixedRegister: Register {} + +/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when +/// passing a [`FixedRegister`] value. +impl IoLoc for () +where + T: FixedRegister, +{ + type IoType = T::Storage; + + #[inline(always)] + fn offset(self) -> usize { + T::OFFSET + } +} + +/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used +/// as an [`IoLoc`]. +impl IoLoc for T +where + T: FixedRegister, +{ + type IoType = T::Storage; + + #[inline(always)] + fn offset(self) -> usize { + T::OFFSET + } +} + +/// Location of a fixed register. +pub struct FixedRegisterLoc(PhantomData); + +impl FixedRegisterLoc { + /// Returns the location of `T`. + #[inline(always)] + // We do not implement `Default` so we can be const. + #[expect(clippy::new_without_default)] + pub const fn new() -> Self { + Self(PhantomData) + } +} + +impl IoLoc for FixedRegisterLoc +where + T: FixedRegister, +{ + type IoType = T::Storage; + + #[inline(always)] + fn offset(self) -> usize { + T::OFFSET + } +} + +/// Trait providing a base address to be added to the offset of a relative register to obtain +/// its actual offset. +/// +/// The `T` generic argument is used to distinguish which base to use, in case a type provides +/// several bases. It is given to the `register!` macro to restrict the use of the register to +/// implementors of this particular variant. +pub trait RegisterBase { + /// Base address to which register offsets are added. + const BASE: usize; +} + +/// Trait implemented by all registers that are relative to a base. +pub trait WithBase { + /// Family of bases applicable to this register. + type BaseFamily; + + /// Returns the absolute location of this type when using `B` as its base. + #[inline(always)] + fn of>() -> RelativeRegisterLoc + where + Self: Register, + { + RelativeRegisterLoc::new() + } +} + +/// Trait implemented by relative registers. +pub trait RelativeRegister: Register + WithBase {} + +/// Location of a relative register. +/// +/// This can either be an immediately accessible regular [`RelativeRegister`], or a +/// [`RelativeRegisterArray`] that needs one additional resolution through +/// [`RelativeRegisterLoc::at`]. +pub struct RelativeRegisterLoc(PhantomData, PhantomData); + +impl RelativeRegisterLoc +where + T: Register + WithBase, + B: RegisterBase + ?Sized, +{ + /// Returns the location of a relative register or register array. + #[inline(always)] + // We do not implement `Default` so we can be const. + #[expect(clippy::new_without_default)] + pub const fn new() -> Self { + Self(PhantomData, PhantomData) + } + + // Returns the absolute offset of the relative register using base `B`. + // + // This is implemented as a private const method so it can be reused by the [`IoLoc`] + // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`]. + #[inline] + const fn offset(self) -> usize { + B::BASE + T::OFFSET + } +} + +impl IoLoc for RelativeRegisterLoc +where + T: RelativeRegister, + B: RegisterBase + ?Sized, +{ + type IoType = T::Storage; + + #[inline(always)] + fn offset(self) -> usize { + RelativeRegisterLoc::offset(self) + } +} + +/// Trait implemented by arrays of registers. +pub trait RegisterArray: Register { + /// Number of elements in the registers array. + const SIZE: usize; + /// Number of bytes between the start of elements in the registers array. + const STRIDE: usize; +} + +/// Location of an array register. +pub struct RegisterArrayLoc(usize, PhantomData); + +impl RegisterArrayLoc { + /// Returns the location of register `T` at position `idx`, with build-time validation. + #[inline(always)] + pub fn new(idx: usize) -> Self { + build_assert!(idx < T::SIZE); + + Self(idx, PhantomData) + } + + /// Attempts to return the location of register `T` at position `idx`, with runtime validation. + #[inline(always)] + pub fn try_new(idx: usize) -> Option { + if idx < T::SIZE { + Some(Self(idx, PhantomData)) + } else { + None + } + } +} + +impl IoLoc for RegisterArrayLoc +where + T: RegisterArray, +{ + type IoType = T::Storage; + + #[inline(always)] + fn offset(self) -> usize { + T::OFFSET + self.0 * T::STRIDE + } +} + +/// Trait providing location builders for [`RegisterArray`]s. +pub trait Array { + /// Returns the location of the register at position `idx`, with build-time validation. + #[inline(always)] + fn at(idx: usize) -> RegisterArrayLoc + where + Self: RegisterArray, + { + RegisterArrayLoc::new(idx) + } + + /// Returns the location of the register at position `idx`, with runtime validation. + #[inline(always)] + fn try_at(idx: usize) -> Option> + where + Self: RegisterArray, + { + RegisterArrayLoc::try_new(idx) + } +} + +/// Trait implemented by arrays of relative registers. +pub trait RelativeRegisterArray: RegisterArray + WithBase {} + +/// Location of a relative array register. +pub struct RelativeRegisterArrayLoc< + T: RelativeRegisterArray, + B: RegisterBase + ?Sized, +>(RelativeRegisterLoc, usize); + +impl RelativeRegisterArrayLoc +where + T: RelativeRegisterArray, + B: RegisterBase + ?Sized, +{ + /// Returns the location of register `T` from the base `B` at index `idx`, with build-time + /// validation. + #[inline(always)] + pub fn new(idx: usize) -> Self { + build_assert!(idx < T::SIZE); + + Self(RelativeRegisterLoc::new(), idx) + } + + /// Attempts to return the location of register `T` from the base `B` at index `idx`, with + /// runtime validation. + #[inline(always)] + pub fn try_new(idx: usize) -> Option { + if idx < T::SIZE { + Some(Self(RelativeRegisterLoc::new(), idx)) + } else { + None + } + } +} + +/// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`]. +impl RelativeRegisterLoc +where + T: RelativeRegisterArray, + B: RegisterBase + ?Sized, +{ + /// Returns the location of the register at position `idx`, with build-time validation. + #[inline(always)] + pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc { + RelativeRegisterArrayLoc::new(idx) + } + + /// Returns the location of the register at position `idx`, with runtime validation. + #[inline(always)] + pub fn try_at(self, idx: usize) -> Option> { + RelativeRegisterArrayLoc::try_new(idx) + } +} + +impl IoLoc for RelativeRegisterArrayLoc +where + T: RelativeRegisterArray, + B: RegisterBase + ?Sized, +{ + type IoType = T::Storage; + + #[inline(always)] + fn offset(self) -> usize { + self.0.offset() + self.1 * T::STRIDE + } +} + +/// Defines a dedicated type for a register, including getter and setter methods for its fields and +/// methods to read and write it from an [`Io`](kernel::io::Io) region. +/// +/// This documentation focuses on how to declare registers. See the [module-level +/// documentation](mod@kernel::io::register) for examples of how to access them. +/// +/// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of +/// registers, and relative arrays of registers. +/// +/// ## Fixed offset registers +/// +/// These are the simplest kind of registers. Their location is simply an offset inside the I/O +/// region. For instance: +/// +/// ```ignore +/// register! { +/// pub FIXED_REG(u16) @ 0x80 { +/// ... +/// } +/// } +/// ``` +/// +/// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region. +/// +/// These registers' location can be built simply by referencing their name: +/// +/// ```no_run +/// use kernel::{ +/// io::{ +/// register, +/// Io, +/// }, +/// }; +/// # use kernel::io::Mmio; +/// +/// register! { +/// FIXED_REG(u32) @ 0x100 { +/// 16:8 high_byte; +/// 7:0 low_byte; +/// } +/// } +/// +/// # fn test(io: &Mmio<0x1000>) { +/// let val = io.read(FIXED_REG); +/// +/// // Write from an already-existing value. +/// io.write(FIXED_REG, val.with_low_byte(0xff)); +/// +/// // Create a register value from scratch. +/// let val2 = FIXED_REG::zeroed().with_high_byte(0x80); +/// +/// // The location of fixed offset registers is already contained in their type. Thus, the +/// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`. +/// io.write((), val2); +/// # } +/// +/// ``` +/// +/// It is possible to create an alias of an existing register with new field definitions by using +/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on +/// the context: +/// +/// ```no_run +/// use kernel::io::register; +/// +/// register! { +/// /// Scratch register. +/// pub SCRATCH(u32) @ 0x00000200 { +/// 31:0 value; +/// } +/// +/// /// Boot status of the firmware. +/// pub SCRATCH_BOOT_STATUS(u32) => SCRATCH { +/// 0:0 completed; +/// } +/// } +/// ``` +/// +/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing +/// its own `completed` field. +/// +/// ## Relative registers +/// +/// Relative registers can be instantiated several times at a relative offset of a group of bases. +/// For instance, imagine the following I/O space: +/// +/// ```text +/// +-----------------------------+ +/// | ... | +/// | | +/// 0x100--->+------------CPU0-------------+ +/// | | +/// 0x110--->+-----------------------------+ +/// | CPU_CTL | +/// +-----------------------------+ +/// | ... | +/// | | +/// | | +/// 0x200--->+------------CPU1-------------+ +/// | | +/// 0x210--->+-----------------------------+ +/// | CPU_CTL | +/// +-----------------------------+ +/// | ... | +/// +-----------------------------+ +/// ``` +/// +/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O +/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define +/// them twice and would prefer a way to select which one to use from a single definition. +/// +/// This can be done using the `Base + Offset` syntax when specifying the register's address: +/// +/// ```ignore +/// register! { +/// pub RELATIVE_REG(u32) @ Base + 0x80 { +/// ... +/// } +/// } +/// ``` +/// +/// This creates a register with an offset of `0x80` from a given base. +/// +/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the +/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for +/// this register needs to implement `RegisterBase`. +/// +/// The location of relative registers can be built using the [`WithBase::of`] method to specify +/// its base. All relative registers implement [`WithBase`]. +/// +/// Here is the above layout translated into code: +/// +/// ```no_run +/// use kernel::{ +/// io::{ +/// register, +/// register::{ +/// RegisterBase, +/// WithBase, +/// }, +/// Io, +/// }, +/// }; +/// # use kernel::io::Mmio; +/// +/// // Type used to identify the base. +/// pub struct CpuCtlBase; +/// +/// // ZST describing `CPU0`. +/// struct Cpu0; +/// impl RegisterBase for Cpu0 { +/// const BASE: usize = 0x100; +/// } +/// +/// // ZST describing `CPU1`. +/// struct Cpu1; +/// impl RegisterBase for Cpu1 { +/// const BASE: usize = 0x200; +/// } +/// +/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase`. +/// register! { +/// /// CPU core control. +/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 { +/// 0:0 start; +/// } +/// } +/// +/// # fn test(io: Mmio<0x1000>) { +/// // Read the status of `Cpu0`. +/// let cpu0_started = io.read(CPU_CTL::of::()); +/// +/// // Stop `Cpu0`. +/// io.write(WithBase::of::(), CPU_CTL::zeroed()); +/// # } +/// +/// // Aliases can also be defined for relative register. +/// register! { +/// /// Alias to CPU core control. +/// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL { +/// /// Start the aliased CPU core. +/// 1:1 alias_start; +/// } +/// } +/// +/// # fn test2(io: Mmio<0x1000>) { +/// // Start the aliased `CPU0`, leaving its other fields untouched. +/// io.update(CPU_CTL_ALIAS::of::(), |r| r.with_alias_start(true)); +/// # } +/// ``` +/// +/// ## Arrays of registers +/// +/// Some I/O areas contain consecutive registers that share the same field layout. These areas can +/// be defined as an array of identical registers, allowing them to be accessed by index with +/// compile-time or runtime bound checking: +/// +/// ```ignore +/// register! { +/// pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 { +/// ... +/// } +/// } +/// ``` +/// +/// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each +/// register is separated from its neighbor by 4 bytes. +/// +/// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from +/// each other. +/// +/// A location for a register in a register array is built using the [`Array::at`] trait method. +/// All arrays of registers implement [`Array`]. +/// +/// ```no_run +/// use kernel::{ +/// io::{ +/// register, +/// register::Array, +/// Io, +/// }, +/// }; +/// # use kernel::io::Mmio; +/// # fn get_scratch_idx() -> usize { +/// # 0x15 +/// # } +/// +/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`. +/// register! { +/// /// Scratch registers. +/// pub SCRATCH(u32)[64] @ 0x00000080 { +/// 31:0 value; +/// } +/// } +/// +/// # fn test(io: &Mmio<0x1000>) +/// # -> Result<(), Error>{ +/// // Read scratch register 0, i.e. I/O address `0x80`. +/// let scratch_0 = io.read(SCRATCH::at(0)).value(); +/// +/// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`. +/// io.write(Array::at(15), SCRATCH::from(0xffeeaabb)); +/// +/// // This is out of bounds and won't build. +/// // let scratch_128 = io.read(SCRATCH::at(128)).value(); +/// +/// // Runtime-obtained array index. +/// let idx = get_scratch_idx(); +/// // Access on a runtime index returns an error if it is out-of-bounds. +/// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value(); +/// +/// // Alias to a specific register in an array. +/// // Here `SCRATCH[8]` is used to convey the firmware exit code. +/// register! { +/// /// Firmware exit status code. +/// pub FIRMWARE_STATUS(u32) => SCRATCH[8] { +/// 7:0 status; +/// } +/// } +/// +/// let status = io.read(FIRMWARE_STATUS).status(); +/// +/// // Non-contiguous register arrays can be defined by adding a stride parameter. +/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the +/// // registers of the two declarations below are interleaved. +/// register! { +/// /// Scratch registers bank 0. +/// pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 { +/// 31:0 value; +/// } +/// +/// /// Scratch registers bank 1. +/// pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 { +/// 31:0 value; +/// } +/// } +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Relative arrays of registers +/// +/// Combining the two features described in the sections above, arrays of registers accessible from +/// a base can also be defined: +/// +/// ```ignore +/// register! { +/// pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 { +/// ... +/// } +/// } +/// ``` +/// +/// Like relative registers, they implement the [`WithBase`] trait. However the return value of +/// [`WithBase::of`] cannot be used directly as a location and must be further specified using the +/// [`at`](RelativeRegisterLoc::at) method. +/// +/// ```no_run +/// use kernel::{ +/// io::{ +/// register, +/// register::{ +/// RegisterBase, +/// WithBase, +/// }, +/// Io, +/// }, +/// }; +/// # use kernel::io::Mmio; +/// # fn get_scratch_idx() -> usize { +/// # 0x15 +/// # } +/// +/// // Type used as parameter of `RegisterBase` to specify the base. +/// pub struct CpuCtlBase; +/// +/// // ZST describing `CPU0`. +/// struct Cpu0; +/// impl RegisterBase for Cpu0 { +/// const BASE: usize = 0x100; +/// } +/// +/// // ZST describing `CPU1`. +/// struct Cpu1; +/// impl RegisterBase for Cpu1 { +/// const BASE: usize = 0x200; +/// } +/// +/// // 64 per-cpu scratch registers, arranged as a contiguous array. +/// register! { +/// /// Per-CPU scratch registers. +/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 { +/// 31:0 value; +/// } +/// } +/// +/// # fn test(io: &Mmio<0x1000>) -> Result<(), Error> { +/// // Read scratch register 0 of CPU0. +/// let scratch = io.read(CPU_SCRATCH::of::().at(0)); +/// +/// // Write the retrieved value into scratch register 15 of CPU1. +/// io.write(WithBase::of::().at(15), scratch); +/// +/// // This won't build. +/// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::().at(128)).value(); +/// +/// // Runtime-obtained array index. +/// let scratch_idx = get_scratch_idx(); +/// // Access on a runtime index returns an error if it is out-of-bounds. +/// let cpu0_scratch = io.read( +/// CPU_SCRATCH::of::().try_at(scratch_idx).ok_or(EINVAL)? +/// ).value(); +/// # Ok(()) +/// # } +/// +/// // Alias to `SCRATCH[8]` used to convey the firmware exit code. +/// register! { +/// /// Per-CPU firmware exit status code. +/// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] { +/// 7:0 status; +/// } +/// } +/// +/// // Non-contiguous relative register arrays can be defined by adding a stride parameter. +/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the +/// // registers of the two declarations below are interleaved. +/// register! { +/// /// Scratch registers bank 0. +/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 { +/// 31:0 value; +/// } +/// +/// /// Scratch registers bank 1. +/// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 { +/// 31:0 value; +/// } +/// } +/// +/// # fn test2(io: &Mmio<0x1000>) -> Result<(), Error> { +/// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::()).status(); +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! register { + // Entry point for the macro, allowing multiple registers to be defined in one call. + // It matches all possible register declaration patterns to dispatch them to corresponding + // `@reg` rule that defines a single register. + ( + $( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + $([ $size:expr $(, stride = $stride:expr)? ])? + $(@ $($base:ident +)? $offset:literal)? + $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )? + { $($fields:tt)* } + )* + ) => { + $( + $crate::register!( + @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])? + $(@ $($base +)? $offset)? + $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )? + { $($fields)* } + ); + )* + }; + + // All the rules below are private helpers. + + // Creates a register at a fixed offset of the MMIO space. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal + { $($fields:tt)* } + ) => { + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!(@io_base $name($storage) @ $offset); + $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); + }; + + // Creates an alias register of fixed offset register `alias` with its own fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident + { $($fields:tt)* } + ) => { + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!( + @io_base $name($storage) @ + <$alias as $crate::io::register::Register>::OFFSET + ); + $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); + }; + + // Creates a register at a relative offset from a base address provider. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal + { $($fields:tt)* } + ) => { + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!(@io_base $name($storage) @ $offset); + $crate::register!(@io_relative $vis $name($storage) @ $base); + }; + + // Creates an alias register of relative offset register `alias` with its own fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident + { $($fields:tt)* } + ) => { + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!( + @io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET + ); + $crate::register!(@io_relative $vis $name($storage) @ $base); + }; + + // Creates an array of registers at a fixed offset of the MMIO space. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + [ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* } + ) => { + ::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride); + + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!(@io_base $name($storage) @ $offset); + $crate::register!(@io_array $vis $name($storage) [ $size, stride = $stride ]); + }; + + // Shortcut for contiguous array of registers (stride == size of element). + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal + { $($fields:tt)* } + ) => { + $crate::register!( + $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ] + @ $offset { $($fields)* } + ); + }; + + // Creates an alias of register `idx` of array of registers `alias` with its own fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ] + { $($fields:tt)* } + ) => { + ::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE); + + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!( + @io_base $name($storage) @ + <$alias as $crate::io::register::Register>::OFFSET + + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE + ); + $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); + }; + + // Creates an array of registers at a relative offset from a base address provider. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + [ $size:expr, stride = $stride:expr ] + @ $base:ident + $offset:literal { $($fields:tt)* } + ) => { + ::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride); + + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!(@io_base $name($storage) @ $offset); + $crate::register!( + @io_relative_array $vis $name($storage) [ $size, stride = $stride ] @ $base + $offset + ); + }; + + // Shortcut for contiguous array of relative registers (stride == size of element). + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] + @ $base:ident + $offset:literal { $($fields:tt)* } + ) => { + $crate::register!( + $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ] + @ $base + $offset { $($fields)* } + ); + }; + + // Creates an alias of register `idx` of relative array of registers `alias` with its own + // fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* } + ) => { + ::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE); + + $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); + $crate::register!( + @io_base $name($storage) @ + <$alias as $crate::io::register::Register>::OFFSET + + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE + ); + $crate::register!(@io_relative $vis $name($storage) @ $base); + }; + + // Generates the bitfield for the register. + // + // `#[allow(non_camel_case_types)]` is added since register names typically use + // `SCREAMING_CASE`. + ( + @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* } + ) => { + $crate::register!(@bitfield_core + #[allow(non_camel_case_types)] + $(#[$attr])* $vis $name $storage + ); + $crate::register!(@bitfield_fields $vis $name $storage { $($fields)* }); + }; + + // Implementations shared by all registers types. + (@io_base $name:ident($storage:ty) @ $offset:expr) => { + impl $crate::io::register::Register for $name { + type Storage = $storage; + + const OFFSET: usize = $offset; + } + }; + + // Implementations of fixed registers. + (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)) => { + impl $crate::io::register::FixedRegister for $name {} + + $(#[$attr])* + $vis const $name: $crate::io::register::FixedRegisterLoc<$name> = + $crate::io::register::FixedRegisterLoc::<$name>::new(); + }; + + // Implementations of relative registers. + (@io_relative $vis:vis $name:ident ($storage:ty) @ $base:ident) => { + impl $crate::io::register::WithBase for $name { + type BaseFamily = $base; + } + + impl $crate::io::register::RelativeRegister for $name {} + }; + + // Implementations of register arrays. + (@io_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]) => { + impl $crate::io::register::Array for $name {} + + impl $crate::io::register::RegisterArray for $name { + const SIZE: usize = $size; + const STRIDE: usize = $stride; + } + }; + + // Implementations of relative array registers. + ( + @io_relative_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ] + @ $base:ident + $offset:literal + ) => { + impl $crate::io::register::WithBase for $name { + type BaseFamily = $base; + } + + impl $crate::io::register::RegisterArray for $name { + const SIZE: usize = $size; + const STRIDE: usize = $stride; + } + + impl $crate::io::register::RelativeRegisterArray for $name {} + }; + + // Defines the wrapper `$name` type and its conversions from/to the storage type. + (@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => { + $(#[$attr])* + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + $vis struct $name { + inner: $storage, + } + + #[allow(dead_code)] + impl $name { + /// Creates a bitfield from a raw value. + #[inline(always)] + $vis const fn from_raw(value: $storage) -> Self { + Self{ inner: value } + } + + /// Turns this bitfield into its raw value. + /// + /// This is similar to the [`From`] implementation, but is shorter to invoke in + /// most cases. + #[inline(always)] + $vis const fn into_raw(self) -> $storage { + self.inner + } + } + + // SAFETY: `$storage` is `Zeroable` and `$name` is transparent. + unsafe impl ::pin_init::Zeroable for $name {} + + impl ::core::convert::From<$name> for $storage { + #[inline(always)] + fn from(val: $name) -> $storage { + val.into_raw() + } + } + + impl ::core::convert::From<$storage> for $name { + #[inline(always)] + fn from(val: $storage) -> $name { + Self::from_raw(val) + } + } + }; + + // Definitions requiring knowledge of individual fields: private and public field accessors, + // and `Debug` implementation. + (@bitfield_fields $vis:vis $name:ident $storage:ty { + $($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident + $(?=> $try_into_type:ty)? + $(=> $into_type:ty)? + ; + )* + } + ) => { + #[allow(dead_code)] + impl $name { + $( + $crate::register!(@private_field_accessors $vis $name $storage : $hi:$lo $field); + $crate::register!( + @public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field + $(?=> $try_into_type)? + $(=> $into_type)? + ); + )* + } + + $crate::register!(@debug $name { $($field;)* }); + }; + + // Private field accessors working with the exact `Bounded` type for the field. + ( + @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident + ) => { + ::kernel::macros::paste!( + $vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive = $lo..=$hi; + $vis const [<$field:upper _MASK>]: $storage = + ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1); + $vis const [<$field:upper _SHIFT>]: u32 = $lo; + ); + + ::kernel::macros::paste!( + fn [<__ $field>](self) -> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> { + // Left shift to align the field's MSB with the storage MSB. + const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1); + // Right shift to move the top-aligned field to bit 0 of the storage. + const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo; + + // Extract the field using two shifts. `Bounded::shr` produces the correctly-sized + // output type. + let val = ::kernel::num::Bounded::<$storage, { $storage::BITS }>::from( + self.inner << ALIGN_TOP + ); + val.shr::() + } + + const fn [<__with_ $field>]( + mut self, + value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>, + ) -> Self + { + const MASK: $storage = <$name>::[<$field:upper _MASK>]; + const SHIFT: u32 = <$name>::[<$field:upper _SHIFT>]; + + let value = value.get() << SHIFT; + self.inner = (self.inner & !MASK) | value; + + self + } + ); + }; + + // Public accessors for fields infallibly (`=>`) converted to a type. + ( + @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty : + $hi:literal:$lo:literal $field:ident => $into_type:ty + ) => { + ::kernel::macros::paste!( + + $(#[doc = $doc])* + #[doc = "Returns the value of this field."] + #[inline(always)] + $vis fn $field(self) -> $into_type + { + self.[<__ $field>]().into() + } + + $(#[doc = $doc])* + #[doc = "Sets this field to the given `value`."] + #[inline(always)] + $vis fn [](self, value: $into_type) -> Self + { + self.[<__with_ $field>](value.into()) + } + + ); + }; + + // Public accessors for fields fallibly (`?=>`) converted to a type. + ( + @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty : + $hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty + ) => { + ::kernel::macros::paste!( + + $(#[doc = $doc])* + #[doc = "Returns the value of this field."] + #[inline(always)] + $vis fn $field(self) -> + Result< + $try_into_type, + <$try_into_type as ::core::convert::TryFrom< + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> + >>::Error + > + { + self.[<__ $field>]().try_into() + } + + $(#[doc = $doc])* + #[doc = "Sets this field to the given `value`."] + #[inline(always)] + $vis fn [](self, value: $try_into_type) -> Self + { + self.[<__with_ $field>](value.into()) + } + + ); + }; + + // Public accessors for fields not converted to a type. + ( + @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty : + $hi:tt:$lo:tt $field:ident + ) => { + ::kernel::macros::paste!( + + $(#[doc = $doc])* + #[doc = "Returns the value of this field."] + #[inline(always)] + $vis fn $field(self) -> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> + { + self.[<__ $field>]() + } + + $(#[doc = $doc])* + #[doc = "Sets this field to the compile-time constant `VALUE`."] + #[inline(always)] + $vis const fn [](self) -> Self { + self.[<__with_ $field>]( + ::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new::() + ) + } + + $(#[doc = $doc])* + #[doc = "Sets this field to the given `value`."] + #[inline(always)] + $vis fn []( + self, + value: T, + ) -> Self + where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>, + { + self.[<__with_ $field>](value.into()) + } + + $(#[doc = $doc])* + #[doc = "Tries to set this field to `value`, returning an error if it is out of range."] + #[inline(always)] + $vis fn []( + self, + value: T, + ) -> ::kernel::error::Result + where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>, + { + Ok( + self.[<__with_ $field>]( + value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)? + ) + ) + } + + ); + }; + + // `Debug` implementation. + (@debug $name:ident { $($field:ident;)* }) => { + impl ::kernel::fmt::Debug for $name { + fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result { + f.debug_struct(stringify!($name)) + .field("", &::kernel::prelude::fmt!("{:#x}", self.inner)) + $( + .field(stringify!($field), &self.$field()) + )* + .finish() + } + } + }; +} From 9a52a8f5ed97d47c9641248874f4c6a78e136d97 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:18 +0900 Subject: [PATCH 115/712] rust: io: introduce `write_reg` and `LocatedRegister` Some I/O types, like fixed address registers, carry their location alongside their values. For these types, the regular `Io::write` method can lead into repeating the location information twice: once to provide the location itself, another time to build the value. We are also considering supporting making all register values carry their full location information for convenience and safety. Add a new `Io::write_reg` method that takes a single argument implementing `LocatedRegister`, a trait that decomposes implementors into a `(location, value)` tuple. This allows write operations on fixed offset registers to be done while specifying their name only once. Suggested-by: Danilo Krummrich Link: https://lore.kernel.org/all/DH0XBLXZD81K.22SWIZ1ZAOW1@kernel.org/ Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-8-86805b2f7e9d@nvidia.com [ Replace FIFO with VERSION register in the examples. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 87 ++++++++++++++++++++++++++++++++++++++ rust/kernel/io/register.rs | 35 ++++++++++++++- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 4950cecf30ca..fcc7678fd9e3 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -17,6 +17,8 @@ pub use crate::register; pub use resource::Resource; +use register::LocatedRegister; + /// Physical address type. /// /// This is a type alias to either `u32` or `u64` depending on the config option @@ -473,6 +475,49 @@ fn try_write(&self, location: L, value: T) -> Result Ok(()) } + /// Generic fallible write of a fully-located register value. + /// + /// # Examples + /// + /// Tuples carrying a location and a value can be used with this method: + /// + /// ```no_run + /// use kernel::io::{ + /// register, + /// Io, + /// Mmio, + /// }; + /// + /// register! { + /// VERSION(u32) @ 0x100 { + /// 15:8 major; + /// 7:0 minor; + /// } + /// } + /// + /// impl VERSION { + /// fn new(major: u8, minor: u8) -> Self { + /// VERSION::zeroed().with_major(major).with_minor(minor) + /// } + /// } + /// + /// fn do_write_reg(io: &Mmio) -> Result { + /// + /// io.try_write_reg(VERSION::new(1, 0)) + /// } + /// ``` + #[inline(always)] + fn try_write_reg(&self, value: V) -> Result + where + L: IoLoc, + V: LocatedRegister, + Self: IoCapable, + { + let (location, value) = value.into_io_op(); + + self.try_write(location, value) + } + /// Generic fallible update with runtime bounds check. /// /// Note: this does not perform any synchronization. The caller is responsible for ensuring @@ -578,6 +623,48 @@ fn write(&self, location: L, value: T) unsafe { self.io_write(io_value, address) } } + /// Generic infallible write of a fully-located register value. + /// + /// # Examples + /// + /// Tuples carrying a location and a value can be used with this method: + /// + /// ```no_run + /// use kernel::io::{ + /// register, + /// Io, + /// Mmio, + /// }; + /// + /// register! { + /// VERSION(u32) @ 0x100 { + /// 15:8 major; + /// 7:0 minor; + /// } + /// } + /// + /// impl VERSION { + /// fn new(major: u8, minor: u8) -> Self { + /// VERSION::zeroed().with_major(major).with_minor(minor) + /// } + /// } + /// + /// fn do_write_reg(io: &Mmio<0x1000>) { + /// io.write_reg(VERSION::new(1, 0)); + /// } + /// ``` + #[inline(always)] + fn write_reg(&self, value: V) + where + L: IoLoc, + V: LocatedRegister, + Self: IoKnownSize + IoCapable, + { + let (location, value) = value.into_io_op(); + + self.write(location, value) + } + /// Generic infallible update with compile-time bounds check. /// /// Note: this does not perform any synchronization. The caller is responsible for ensuring diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs index dbd458aaa761..abc49926abfe 100644 --- a/rust/kernel/io/register.rs +++ b/rust/kernel/io/register.rs @@ -80,10 +80,10 @@ //! .with_const_minor_revision::<10>() //! // Runtime value. //! .with_vendor_id(obtain_vendor_id()); -//! io.write((), new_boot0); +//! io.write_reg(new_boot0); //! //! // Or, build a new value from zero and write it: -//! io.write((), BOOT_0::zeroed() +//! io.write_reg(BOOT_0::zeroed() //! .with_const_major_revision::<3>() //! .with_const_minor_revision::<10>() //! .with_vendor_id(obtain_vendor_id()) @@ -382,6 +382,34 @@ fn offset(self) -> usize { } } +/// Trait implemented by items that contain both a register value and the absolute I/O location at +/// which to write it. +/// +/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg). +pub trait LocatedRegister { + /// Register value to write. + type Value: Register; + /// Full location information at which to write the value. + type Location: IoLoc; + + /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write + /// operation. + fn into_io_op(self) -> (Self::Location, Self::Value); +} + +impl LocatedRegister for T +where + T: FixedRegister, +{ + type Location = FixedRegisterLoc; + type Value = T; + + #[inline(always)] + fn into_io_op(self) -> (FixedRegisterLoc, T) { + (FixedRegisterLoc::new(), self) + } +} + /// Defines a dedicated type for a register, including getter and setter methods for its fields and /// methods to read and write it from an [`Io`](kernel::io::Io) region. /// @@ -436,6 +464,9 @@ fn offset(self) -> usize { /// // The location of fixed offset registers is already contained in their type. Thus, the /// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`. /// io.write((), val2); +/// +/// // Or, the single-argument `Io::write_reg` can be used. +/// io.write_reg(val2); /// # } /// /// ``` From 79cf41692aadc3d0ac9b1d8e2c2f620ce2103918 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 14 Mar 2026 10:06:19 +0900 Subject: [PATCH 116/712] sample: rust: pci: use `register!` macro Convert the direct IO accesses to properly defined registers. Tested-by: Zhi Wang Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260314-register-v9-9-86805b2f7e9d@nvidia.com [ Fix up kernel import style. - Danilo ] Signed-off-by: Danilo Krummrich --- samples/rust/rust_driver_pci.rs | 90 +++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 22 deletions(-) diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index d3d4a7931deb..47d3e84fab63 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -5,30 +5,63 @@ //! To make this driver probe, QEMU must be run with `-device pci-testdev`. use kernel::{ - device::Bound, - device::Core, + device::{ + Bound, + Core, // + }, devres::Devres, - io::Io, + io::{ + register, + register::Array, + Io, // + }, + num::Bounded, pci, prelude::*, sync::aref::ARef, // }; -struct Regs; +mod regs { + use super::*; -impl Regs { - const TEST: usize = 0x0; - const OFFSET: usize = 0x4; - const DATA: usize = 0x8; - const COUNT: usize = 0xC; - const END: usize = 0x10; + register! { + pub(super) TEST(u8) @ 0x0 { + 7:0 index => TestIndex; + } + + pub(super) OFFSET(u32) @ 0x4 { + 31:0 offset; + } + + pub(super) DATA(u8) @ 0x8 { + 7:0 data; + } + + pub(super) COUNT(u32) @ 0xC { + 31:0 count; + } + } + + pub(super) const END: usize = 0x10; } -type Bar0 = pci::Bar<{ Regs::END }>; +type Bar0 = pci::Bar<{ regs::END }>; #[derive(Copy, Clone, Debug)] struct TestIndex(u8); +impl From> for TestIndex { + fn from(value: Bounded) -> Self { + Self(value.into()) + } +} + +impl From for Bounded { + fn from(value: TestIndex) -> Self { + value.0.into() + } +} + impl TestIndex { const NO_EVENTFD: Self = Self(0); } @@ -54,40 +87,53 @@ struct SampleDriver { impl SampleDriver { fn testdev(index: &TestIndex, bar: &Bar0) -> Result { // Select the test. - bar.write8(index.0, Regs::TEST); + bar.write_reg(regs::TEST::zeroed().with_index(*index)); - let offset = bar.read32(Regs::OFFSET) as usize; - let data = bar.read8(Regs::DATA); + let offset = bar.read(regs::OFFSET).into_raw() as usize; + let data = bar.read(regs::DATA).into(); // Write `data` to `offset` to increase `count` by one. // // Note that we need `try_write8`, since `offset` can't be checked at compile-time. bar.try_write8(data, offset)?; - Ok(bar.read32(Regs::COUNT)) + Ok(bar.read(regs::COUNT).into()) } fn config_space(pdev: &pci::Device) { let config = pdev.config_space(); - // TODO: use the register!() macro for defining PCI configuration space registers once it - // has been move out of nova-core. + // Some PCI configuration space registers. + register! { + VENDOR_ID(u16) @ 0x0 { + 15:0 vendor_id; + } + + REVISION_ID(u8) @ 0x8 { + 7:0 revision_id; + } + + BAR(u32)[6] @ 0x10 { + 31:0 value; + } + } + dev_info!( pdev, "pci-testdev config space read8 rev ID: {:x}\n", - config.read8(0x8) + config.read(REVISION_ID).revision_id() ); dev_info!( pdev, "pci-testdev config space read16 vendor ID: {:x}\n", - config.read16(0) + config.read(VENDOR_ID).vendor_id() ); dev_info!( pdev, "pci-testdev config space read32 BAR 0: {:x}\n", - config.read32(0x10) + config.read(BAR::at(0)).value() ); } } @@ -111,7 +157,7 @@ fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> impl PinInit(0, c"rust_driver_pci"), + bar <- pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci"), index: *info, _: { let bar = bar.access(pdev.as_ref())?; @@ -131,7 +177,7 @@ fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> impl PinInit, this: Pin<&Self>) { if let Ok(bar) = this.bar.access(pdev.as_ref()) { // Reset pci-testdev by writing a new test index. - bar.write8(this.index.0, Regs::TEST); + bar.write_reg(regs::TEST::zeroed().with_index(this.index)); } } } From 0fb03890d18205ec0909fc47049eceae8ba36457 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 16 Mar 2026 08:48:51 +0530 Subject: [PATCH 117/712] drm/i915/backlight: Check if VESA backlight is possible Check if BACKLIGHT_BRIGHTNESS_AUX_SET_CAPABLE bit is set then EDP_PWMGEN_BIT_COUNT_CAP_MIN and EDP_PWMGEN_BIT_COUNT_CAP_MAX follow the eDP 1.4b Section 10.3. Which states min should be >= 1 and max should be >= min. Some legacy panels do not follow this properly. They set the BACKLIGHT_BRIGHTNESS_AUX_SET_CAPABLE bit while not correctly populating the min and max fields leading to a 0 max value. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7514 Fixes: 40d2f5820951 ("drm/i915/backlight: Remove try_vesa_interface") Signed-off-by: Suraj Kandpal Reviewed-by: Pranay Samala Link: https://patch.msgid.link/20260316031850.81794-1-suraj.kandpal@intel.com --- .../drm/i915/display/intel_dp_aux_backlight.c | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index a7b186d0e3c4..d0c76632a946 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -609,6 +609,34 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, return 0; } +static bool +check_if_vesa_backlight_possible(struct intel_dp *intel_dp) +{ + int ret; + u8 bit_min, bit_max; + + if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) + return true; + + ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min); + if (ret < 0) + return false; + + bit_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + if (bit_min < 1) + return false; + + ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &bit_max); + if (ret < 0) + return false; + + bit_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + if (bit_max < bit_min) + return false; + + return true; +} + static bool intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector) { @@ -625,12 +653,14 @@ intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector) return true; } - if (drm_edp_backlight_supported(intel_dp->edp_dpcd)) { + if (drm_edp_backlight_supported(intel_dp->edp_dpcd) && + check_if_vesa_backlight_possible(intel_dp)) { drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] AUX Backlight Control Supported!\n", connector->base.base.id, connector->base.name); return true; } + return false; } From 4ab0f09ee73fc853d00466682635f67c531f909c Mon Sep 17 00:00:00 2001 From: Samasth Norway Ananda Date: Mon, 16 Mar 2026 16:19:19 -0700 Subject: [PATCH 118/712] drm/i915/gmbus: fix spurious timeout on 512-byte burst reads When reading exactly 512 bytes with burst read enabled, the extra_byte_added path breaks out of the inner do-while without decrementing len. The outer while(len) then re-enters and gmbus_wait() times out since all data has been delivered. Decrement len before the break so the outer loop terminates correctly. Fixes: d5dc0f43f268 ("drm/i915/gmbus: Enable burst read") Signed-off-by: Samasth Norway Ananda Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260316231920.135438-2-samasth.norway.ananda@oracle.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_gmbus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index df48f27f1cc1..dd79a866b87e 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -495,8 +495,10 @@ gmbus_xfer_read_chunk(struct intel_display *display, val = intel_de_read_fw(display, GMBUS3(display)); do { - if (extra_byte_added && len == 1) + if (extra_byte_added && len == 1) { + len--; break; + } *buf++ = val & 0xff; val >>= 8; From e5b3fe57dc5893c7ea5f478a1161b8643adf5dd9 Mon Sep 17 00:00:00 2001 From: Samasth Norway Ananda Date: Mon, 16 Mar 2026 16:19:20 -0700 Subject: [PATCH 119/712] drm/i915/gmbus: fix a typo in comment message Fix a typo inside a comment message from ("generata" -> "generate") in function do_gmbus_xfer() before calling intel_de_write_fw() Signed-off-by: Samasth Norway Ananda Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260316231920.135438-3-samasth.norway.ananda@oracle.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_gmbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index dd79a866b87e..ea5cf8f51b31 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -694,7 +694,7 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num, goto clear_err; } - /* Generate a STOP condition on the bus. Note that gmbus can't generata + /* Generate a STOP condition on the bus. Note that gmbus can't generate * a STOP on the very first cycle. To simplify the code we * unconditionally generate the STOP condition with an additional gmbus * cycle. */ From b63c6b9b7f5ed02bb3abf7a39c18ea54d1a69f0f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 4 Mar 2026 09:36:48 +0100 Subject: [PATCH 120/712] drm/i915/fbdev: fix link failure without FBDEV emulation If CONFIG_DRM_FBDEV_EMULATION is disabled but CONFIG_FRAMEBUFFER_CONSOLE is turned on, the i915 driver now fails to link: ERROR: modpost: "intel_fbdev_fb_prefer_stolen" [drivers/gpu/drm/i915/i915.ko] undefined! Fix the contition to include a check for the symbol that controls compilation of intel_fbdev_fb.c. Fixes: 94c7d2861292 ("drm/i915/fbdev: Extract intel_fbdev_fb_prefer_stolen()") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260304083701.724908-1-arnd@kernel.org Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_initial_plane.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_initial_plane.c b/drivers/gpu/drm/i915/i915_initial_plane.c index 5594548f51d8..390a9248d631 100644 --- a/drivers/gpu/drm/i915/i915_initial_plane.c +++ b/drivers/gpu/drm/i915/i915_initial_plane.c @@ -115,7 +115,8 @@ initial_plane_vma(struct drm_i915_private *i915, * important and we should probably use that space with FBC or other * features. */ - if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) && + if (IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) && + IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) && mem == i915->mm.stolen_region && !intel_fbdev_fb_prefer_stolen(&i915->drm, size)) { drm_dbg_kms(&i915->drm, "Initial FB size exceeds half of stolen, discarding\n"); From 45c77d4bf8d4d15453d709b9b828e498898e0751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Tue, 17 Mar 2026 08:24:02 +0200 Subject: [PATCH 121/712] drm/i915/psr: Disable Panel Replay on Dell XPS 14 DA14260 as a quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new quirk (QUIRK_DISABLE_PANEL_REPLAY) for disabling Panel Replay as quirk for problematic setups. Apply this newly added quirk on Dell XPS 14 DA14260 if specific panel model is installed. We are observing problems with Dell XPS 14 DA14260. This device has certain LGD panel model which seems to be problematic. We have seen other LGD panel model with same OUI is working fine. Due to this we can't apply the quirk only based on panel OUI. There are also cases where same device model has differing panel model. We don't want to disable Panel Replay on such devices. Best we can do is to apply the quirk based on both device model and panel model. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7521 Signed-off-by: Jouni Högander Reviewed-by: Mika Kahola Link: https://patch.msgid.link/20260317062402.1888624-1-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ drivers/gpu/drm/i915/display/intel_quirks.c | 17 ++++++++++++++++- drivers/gpu/drm/i915/display/intel_quirks.h | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index c13116e6f17f..b319e5bd6274 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -49,6 +49,7 @@ #include "intel_hdmi.h" #include "intel_psr.h" #include "intel_psr_regs.h" +#include "intel_quirks.h" #include "intel_snps_phy.h" #include "intel_step.h" #include "intel_vblank.h" @@ -609,6 +610,12 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp, struct intel_conn if (intel_dp->mst_detect == DRM_DP_MST) return; + if (intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY)) { + drm_dbg_kms(display->drm, + "Panel Replay support not currently available for this setup\n"); + return; + } + ret = drm_dp_dpcd_read_data(&intel_dp->aux, DP_PANEL_REPLAY_CAP_SUPPORT, &connector->dp.panel_replay_caps.dpcd, sizeof(connector->dp.panel_replay_caps.dpcd)); diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c index 1abbdd426e58..8f1bf8f418ec 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.c +++ b/drivers/gpu/drm/i915/display/intel_quirks.c @@ -86,6 +86,14 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display *display) drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n"); } +static void quirk_disable_panel_replay(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + + intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY); + drm_info(display->drm, "Applying disable Panel Replay quirk\n"); +} + struct intel_quirk { int device; int subsystem_vendor; @@ -251,7 +259,14 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { .sink_oui = SINK_OUI(0x38, 0xec, 0x11), .hook = quirk_fw_sync_len, }, - + /* Dell XPS 14 DA14260 */ + { + .device = 0xb080, + .subsystem_vendor = 0x1028, + .subsystem_device = 0x0db9, + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), + .hook = quirk_disable_panel_replay, + }, }; void intel_init_quirks(struct intel_display *display) diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h index 06da0e286c67..77e490caed0d 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.h +++ b/drivers/gpu/drm/i915/display/intel_quirks.h @@ -21,6 +21,7 @@ enum intel_quirk_id { QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK, QUIRK_FW_SYNC_LEN, QUIRK_EDP_LIMIT_RATE_HBR2, + QUIRK_DISABLE_PANEL_REPLAY, }; void intel_init_quirks(struct intel_display *display); From 0a5dbeadf16f32945dce6631c169608f0e131e5a Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Wed, 18 Mar 2026 13:07:09 +0900 Subject: [PATCH 122/712] gpu: nova-core: gsp: fix stale doc comments on command queue methods Fix some inaccuracies / old doc comments. Reviewed-by: Zhi Wang Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260318-cmdq-locking-v5-1-18b37e3f9069@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index efa1aab1568f..f7ca6856ff35 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -502,6 +502,7 @@ fn notify_gsp(bar: &Bar0) { /// /// # Errors /// + /// - `EMSGSIZE` if the command exceeds the maximum queue element size. /// - `ETIMEDOUT` if space does not become available within the timeout. /// - `EIO` if the variable payload requested by the command has not been entirely /// written to by its [`CommandToGsp::init_variable_payload`] method. @@ -682,22 +683,20 @@ fn wait_for_msg(&self, timeout: Delta) -> Result> { /// Receive a message from the GSP. /// - /// `init` is a closure tasked with processing the message. It receives a reference to the - /// message in the message queue, and a [`SBufferIter`] pointing to its variable-length - /// payload, if any. + /// The expected message type is specified using the `M` generic parameter. If the pending + /// message has a different function code, `ERANGE` is returned and the message is consumed. /// - /// The expected message is specified using the `M` generic parameter. If the pending message - /// is different, `EAGAIN` is returned and the unexpected message is dropped. - /// - /// This design is by no means final, but it is simple and will let us go through GSP - /// initialization. + /// The read pointer is always advanced past the message, regardless of whether it matched. /// /// # Errors /// /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available. /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the /// message queue. - /// - `EINVAL` if the function of the message was unrecognized. + /// - `EINVAL` if the function code of the message was not recognized. + /// - `ERANGE` if the message had a recognized but non-matching function code. + /// + /// Error codes returned by [`MessageFromGsp::read`] are propagated as-is. pub(crate) fn receive_msg(&mut self, timeout: Delta) -> Result where // This allows all error types, including `Infallible`, to be used for `M::InitError`. From 67d9ef2bdd62c541a22da04875ccd0722ba1d3d4 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Wed, 18 Mar 2026 13:07:10 +0900 Subject: [PATCH 123/712] gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue Remove magic numbers and add a default timeout for callers to use. Tested-by: Zhi Wang Reviewed-by: Gary Guo Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260318-cmdq-locking-v5-2-18b37e3f9069@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 3 +++ drivers/gpu/nova-core/gsp/commands.rs | 5 ++--- drivers/gpu/nova-core/gsp/sequencer.rs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index f7ca6856ff35..c62db727a2a9 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -467,6 +467,9 @@ impl Cmdq { /// Timeout for waiting for space on the command queue. const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1); + /// Default timeout for receiving a message from the GSP. + pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5); + /// Creates a new command queue for `dev`. pub(crate) fn new(dev: &device::Device) -> Result { let gsp_mem = DmaGspMem::new(dev)?; diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 8f270eca33be..88df117ba575 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -11,7 +11,6 @@ device, pci, prelude::*, - time::Delta, transmute::{ AsBytes, FromBytes, // @@ -165,7 +164,7 @@ fn read( /// Waits for GSP initialization to complete. pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { loop { - match cmdq.receive_msg::(Delta::from_secs(10)) { + match cmdq.receive_msg::(Cmdq::RECEIVE_TIMEOUT) { Ok(_) => break Ok(()), Err(ERANGE) => continue, Err(e) => break Err(e), @@ -235,7 +234,7 @@ pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result(Delta::from_secs(5)) { + match cmdq.receive_msg::(Cmdq::RECEIVE_TIMEOUT) { Ok(info) => return Ok(info), Err(ERANGE) => continue, Err(e) => return Err(e), diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index 0cfbedc47fcf..ce2b3bb05d22 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -358,7 +358,7 @@ pub(crate) struct GspSequencerParams<'a> { impl<'a> GspSequencer<'a> { pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result { let seq_info = loop { - match cmdq.receive_msg::(Delta::from_secs(10)) { + match cmdq.receive_msg::(Cmdq::RECEIVE_TIMEOUT) { Ok(seq_info) => break seq_info, Err(ERANGE) => continue, Err(e) => return Err(e), From c3bd240f97491122e3c9e9922def7e59eecd6145 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Wed, 18 Mar 2026 13:07:11 +0900 Subject: [PATCH 124/712] gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp` Add type infrastructure to know what reply is expected from each `CommandToGsp`. Uses a marker type `NoReply` which does not implement `MessageFromGsp` to mark commands which don't expect a response. Update `send_command` to wait for a reply and add `send_command_no_wait` which sends a command that has no reply, without blocking. This prepares for adding locking to the queue. Tested-by: Zhi Wang Reviewed-by: Gary Guo Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260318-cmdq-locking-v5-3-18b37e3f9069@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/boot.rs | 5 +- drivers/gpu/nova-core/gsp/cmdq.rs | 62 ++++++++++++++++++- .../gpu/nova-core/gsp/cmdq/continuation.rs | 8 ++- drivers/gpu/nova-core/gsp/commands.rs | 16 ++--- 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 6db2decbc6f5..ffc478b33640 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -169,8 +169,9 @@ pub(crate) fn boot( dma_write!(wpr_meta, [0]?, GspFwWprMeta::new(&gsp_fw, &fb_layout)); self.cmdq - .send_command(bar, commands::SetSystemInfo::new(pdev))?; - self.cmdq.send_command(bar, commands::SetRegistry::new())?; + .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?; + self.cmdq + .send_command_no_wait(bar, commands::SetRegistry::new())?; gsp_falcon.reset(bar)?; let libos_handle = self.libos.dma_handle(); diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index c62db727a2a9..4fc14689d38e 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -45,10 +45,14 @@ sbuffer::SBufferIter, // }; +/// Marker type representing the absence of a reply for a command. Commands using this as their +/// reply type are sent using [`Cmdq::send_command_no_wait`]. +pub(crate) struct NoReply; + /// Trait implemented by types representing a command to send to the GSP. /// -/// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it -/// needs to send a given command. +/// The main purpose of this trait is to provide [`Cmdq`] with the information it needs to send +/// a given command. /// /// [`CommandToGsp::init`] in particular is responsible for initializing the command directly /// into the space reserved for it in the command queue buffer. @@ -63,6 +67,10 @@ pub(crate) trait CommandToGsp { /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer. type Command: FromBytes + AsBytes; + /// Type of the reply expected from the GSP, or [`NoReply`] for commands that don't + /// have a reply. + type Reply; + /// Error type returned by [`CommandToGsp::init`]. type InitError; @@ -581,7 +589,7 @@ fn send_single_command(&mut self, bar: &Bar0, command: M) -> Result /// written to by its [`CommandToGsp::init_variable_payload`] method. /// /// Error codes returned by the command initializers are propagated as-is. - pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result + fn send_command_internal(&mut self, bar: &Bar0, command: M) -> Result where M: CommandToGsp, Error: From, @@ -601,6 +609,54 @@ pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result } } + /// Sends `command` to the GSP and waits for the reply. + /// + /// Messages with non-matching function codes are silently consumed until the expected reply + /// arrives. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is + /// not received within the timeout. + /// - `EIO` if the variable payload requested by the command has not been entirely + /// written to by its [`CommandToGsp::init_variable_payload`] method. + /// + /// Error codes returned by the command and reply initializers are propagated as-is. + pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp, + M::Reply: MessageFromGsp, + Error: From, + Error: From<::InitError>, + { + self.send_command_internal(bar, command)?; + + loop { + match self.receive_msg::(Self::RECEIVE_TIMEOUT) { + Ok(reply) => break Ok(reply), + Err(ERANGE) => continue, + Err(e) => break Err(e), + } + } + } + + /// Sends `command` to the GSP without waiting for a reply. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available within the timeout. + /// - `EIO` if the variable payload requested by the command has not been entirely + /// written to by its [`CommandToGsp::init_variable_payload`] method. + /// + /// Error codes returned by the command initializers are propagated as-is. + pub(crate) fn send_command_no_wait(&mut self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp, + Error: From, + { + self.send_command_internal(bar, command) + } + /// Wait for a message to become available on the message queue. /// /// This works purely at the transport layer and does not interpret or validate the message diff --git a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs index 2aa17caac2e0..05e904f18097 100644 --- a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs +++ b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs @@ -6,7 +6,10 @@ use kernel::prelude::*; -use super::CommandToGsp; +use super::{ + CommandToGsp, + NoReply, // +}; use crate::{ gsp::fw::{ @@ -63,6 +66,7 @@ fn new(data: &'a [u8]) -> Self { impl<'a> CommandToGsp for ContinuationRecord<'a> { const FUNCTION: MsgFunction = MsgFunction::ContinuationRecord; type Command = (); + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init { @@ -144,6 +148,7 @@ fn new(command: C, payload: KVVec) -> Self { impl CommandToGsp for SplitCommand { const FUNCTION: MsgFunction = C::FUNCTION; type Command = C::Command; + type Reply = C::Reply; type InitError = C::InitError; fn init(&self) -> impl Init { @@ -206,6 +211,7 @@ fn new(len: usize) -> Result { impl CommandToGsp for TestPayload { const FUNCTION: MsgFunction = MsgFunction::Nop; type Command = TestHeader; + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init { diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 88df117ba575..77054c92fcc2 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -23,7 +23,8 @@ cmdq::{ Cmdq, CommandToGsp, - MessageFromGsp, // + MessageFromGsp, + NoReply, // }, fw::{ commands::*, @@ -48,6 +49,7 @@ pub(crate) fn new(pdev: &'a pci::Device) -> Self { impl<'a> CommandToGsp for SetSystemInfo<'a> { const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo; type Command = GspSetSystemInfo; + type Reply = NoReply; type InitError = Error; fn init(&self) -> impl Init { @@ -99,6 +101,7 @@ pub(crate) fn new() -> Self { impl CommandToGsp for SetRegistry { const FUNCTION: MsgFunction = MsgFunction::SetRegistry; type Command = PackedRegistryTable; + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init { @@ -178,6 +181,7 @@ pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { impl CommandToGsp for GetGspStaticInfo { const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo; type Command = GspStaticConfigInfo; + type Reply = GetGspStaticInfoReply; type InitError = Infallible; fn init(&self) -> impl Init { @@ -231,13 +235,5 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> { /// Send the [`GetGspInfo`] command and awaits for its reply. pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result { - cmdq.send_command(bar, GetGspStaticInfo)?; - - loop { - match cmdq.receive_msg::(Cmdq::RECEIVE_TIMEOUT) { - Ok(info) => return Ok(info), - Err(ERANGE) => continue, - Err(e) => return Err(e), - } - } + cmdq.send_command(bar, GetGspStaticInfo) } From 9b786c7f630924fc3a6179b515e9d0d222d91c95 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Wed, 18 Mar 2026 13:07:12 +0900 Subject: [PATCH 125/712] gpu: nova-core: gsp: make `Cmdq` a pinned type Make `Cmdq` a pinned type. This is needed to use Mutex, which is needed to add locking to `Cmdq`. Reviewed-by: Zhi Wang Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260318-cmdq-locking-v5-4-18b37e3f9069@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp.rs | 5 +++-- drivers/gpu/nova-core/gsp/cmdq.rs | 9 ++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index c69adaa92bbe..72f173726f87 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -114,6 +114,7 @@ pub(crate) struct Gsp { /// RM log buffer. logrm: LogBuffer, /// Command queue. + #[pin] pub(crate) cmdq: Cmdq, /// RM arguments. rmargs: CoherentAllocation, @@ -134,7 +135,7 @@ pub(crate) fn new(pdev: &pci::Device) -> impl PinInit::alloc_coherent( dev, 1, @@ -151,7 +152,7 @@ pub(crate) fn new(pdev: &pci::Device) -> impl PinInit { /// /// Provides the ability to send commands and receive messages from the GSP using a shared memory /// area. +#[pin_data] pub(crate) struct Cmdq { /// Device this command queue belongs to. dev: ARef, @@ -479,13 +480,11 @@ impl Cmdq { pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5); /// Creates a new command queue for `dev`. - pub(crate) fn new(dev: &device::Device) -> Result { - let gsp_mem = DmaGspMem::new(dev)?; - - Ok(Cmdq { + pub(crate) fn new(dev: &device::Device) -> impl PinInit + '_ { + try_pin_init!(Self { + gsp_mem: DmaGspMem::new(dev)?, dev: dev.into(), seq: 0, - gsp_mem, }) } From a19457958c3018783881c4416f272cd594f13049 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Wed, 18 Mar 2026 13:07:13 +0900 Subject: [PATCH 126/712] gpu: nova-core: gsp: add mutex locking to Cmdq Wrap `Cmdq`'s mutable state in a new struct `CmdqInner` and wrap that in a Mutex. This lets `Cmdq` methods take &self instead of &mut self, which lets required commands be sent e.g. while unloading the driver. The mutex is held over both send and receive in `send_command` to make sure that it doesn't get the reply of some other command that could have been sent just beforehand. Reviewed-by: Zhi Wang Tested-by: Zhi Wang Signed-off-by: Eliot Courtney Link: https://patch.msgid.link/20260318-cmdq-locking-v5-5-18b37e3f9069@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/boot.rs | 8 +- drivers/gpu/nova-core/gsp/cmdq.rs | 170 +++++++++++++++---------- drivers/gpu/nova-core/gsp/commands.rs | 4 +- drivers/gpu/nova-core/gsp/sequencer.rs | 2 +- 4 files changed, 107 insertions(+), 77 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index ffc478b33640..5e73bd769dcc 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -137,7 +137,7 @@ fn run_fwsec_frts( /// /// Upon return, the GSP is up and running, and its runtime object given as return value. pub(crate) fn boot( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, pdev: &pci::Device, bar: &Bar0, chipset: Chipset, @@ -223,13 +223,13 @@ pub(crate) fn boot( dev: pdev.as_ref().into(), bar, }; - GspSequencer::run(&mut self.cmdq, seq_params)?; + GspSequencer::run(&self.cmdq, seq_params)?; // Wait until GSP is fully initialized. - commands::wait_gsp_init_done(&mut self.cmdq)?; + commands::wait_gsp_init_done(&self.cmdq)?; // Obtain and display basic GPU information. - let info = commands::get_gsp_info(&mut self.cmdq, bar)?; + let info = commands::get_gsp_info(&self.cmdq, bar)?; match info.gpu_name() { Ok(name) => dev_info!(pdev, "GPU name: {}\n", name), Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e), diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 86ff9a3d1732..d36a62ba1c60 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -12,8 +12,12 @@ }, dma_write, io::poll::read_poll_timeout, + new_mutex, prelude::*, - sync::aref::ARef, + sync::{ + aref::ARef, + Mutex, // + }, time::Delta, transmute::{ AsBytes, @@ -448,12 +452,9 @@ struct GspMessage<'a> { /// area. #[pin_data] pub(crate) struct Cmdq { - /// Device this command queue belongs to. - dev: ARef, - /// Current command sequence number. - seq: u32, - /// Memory area shared with the GSP for communicating commands and messages. - gsp_mem: DmaGspMem, + /// Inner mutex-protected state. + #[pin] + inner: Mutex, } impl Cmdq { @@ -473,18 +474,17 @@ impl Cmdq { /// Number of page table entries for the GSP shared region. pub(crate) const NUM_PTES: usize = size_of::() >> GSP_PAGE_SHIFT; - /// Timeout for waiting for space on the command queue. - const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1); - /// Default timeout for receiving a message from the GSP. pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5); /// Creates a new command queue for `dev`. pub(crate) fn new(dev: &device::Device) -> impl PinInit + '_ { try_pin_init!(Self { - gsp_mem: DmaGspMem::new(dev)?, - dev: dev.into(), - seq: 0, + inner <- new_mutex!(CmdqInner { + dev: dev.into(), + gsp_mem: DmaGspMem::new(dev)?, + seq: 0, + }), }) } @@ -508,6 +508,89 @@ fn notify_gsp(bar: &Bar0) { .write(bar); } + /// Sends `command` to the GSP and waits for the reply. + /// + /// Messages with non-matching function codes are silently consumed until the expected reply + /// arrives. + /// + /// The queue is locked for the entire send+receive cycle to ensure that no other command can + /// be interleaved. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is + /// not received within the timeout. + /// - `EIO` if the variable payload requested by the command has not been entirely + /// written to by its [`CommandToGsp::init_variable_payload`] method. + /// + /// Error codes returned by the command and reply initializers are propagated as-is. + pub(crate) fn send_command(&self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp, + M::Reply: MessageFromGsp, + Error: From, + Error: From<::InitError>, + { + let mut inner = self.inner.lock(); + inner.send_command(bar, command)?; + + loop { + match inner.receive_msg::(Self::RECEIVE_TIMEOUT) { + Ok(reply) => break Ok(reply), + Err(ERANGE) => continue, + Err(e) => break Err(e), + } + } + } + + /// Sends `command` to the GSP without waiting for a reply. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available within the timeout. + /// - `EIO` if the variable payload requested by the command has not been entirely + /// written to by its [`CommandToGsp::init_variable_payload`] method. + /// + /// Error codes returned by the command initializers are propagated as-is. + pub(crate) fn send_command_no_wait(&self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp, + Error: From, + { + self.inner.lock().send_command(bar, command) + } + + /// Receive a message from the GSP. + /// + /// See [`CmdqInner::receive_msg`] for details. + pub(crate) fn receive_msg(&self, timeout: Delta) -> Result + where + // This allows all error types, including `Infallible`, to be used for `M::InitError`. + Error: From, + { + self.inner.lock().receive_msg(timeout) + } + + /// Returns the DMA handle of the command queue's shared memory region. + pub(crate) fn dma_handle(&self) -> DmaAddress { + self.inner.lock().gsp_mem.0.dma_handle() + } +} + +/// Inner mutex protected state of [`Cmdq`]. +struct CmdqInner { + /// Device this command queue belongs to. + dev: ARef, + /// Current command sequence number. + seq: u32, + /// Memory area shared with the GSP for communicating commands and messages. + gsp_mem: DmaGspMem, +} + +impl CmdqInner { + /// Timeout for waiting for space on the command queue. + const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1); + /// Sends `command` to the GSP, without splitting it. /// /// # Errors @@ -588,7 +671,7 @@ fn send_single_command(&mut self, bar: &Bar0, command: M) -> Result /// written to by its [`CommandToGsp::init_variable_payload`] method. /// /// Error codes returned by the command initializers are propagated as-is. - fn send_command_internal(&mut self, bar: &Bar0, command: M) -> Result + fn send_command(&mut self, bar: &Bar0, command: M) -> Result where M: CommandToGsp, Error: From, @@ -608,54 +691,6 @@ fn send_command_internal(&mut self, bar: &Bar0, command: M) -> Result } } - /// Sends `command` to the GSP and waits for the reply. - /// - /// Messages with non-matching function codes are silently consumed until the expected reply - /// arrives. - /// - /// # Errors - /// - /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is - /// not received within the timeout. - /// - `EIO` if the variable payload requested by the command has not been entirely - /// written to by its [`CommandToGsp::init_variable_payload`] method. - /// - /// Error codes returned by the command and reply initializers are propagated as-is. - pub(crate) fn send_command(&mut self, bar: &Bar0, command: M) -> Result - where - M: CommandToGsp, - M::Reply: MessageFromGsp, - Error: From, - Error: From<::InitError>, - { - self.send_command_internal(bar, command)?; - - loop { - match self.receive_msg::(Self::RECEIVE_TIMEOUT) { - Ok(reply) => break Ok(reply), - Err(ERANGE) => continue, - Err(e) => break Err(e), - } - } - } - - /// Sends `command` to the GSP without waiting for a reply. - /// - /// # Errors - /// - /// - `ETIMEDOUT` if space does not become available within the timeout. - /// - `EIO` if the variable payload requested by the command has not been entirely - /// written to by its [`CommandToGsp::init_variable_payload`] method. - /// - /// Error codes returned by the command initializers are propagated as-is. - pub(crate) fn send_command_no_wait(&mut self, bar: &Bar0, command: M) -> Result - where - M: CommandToGsp, - Error: From, - { - self.send_command_internal(bar, command) - } - /// Wait for a message to become available on the message queue. /// /// This works purely at the transport layer and does not interpret or validate the message @@ -691,7 +726,7 @@ fn wait_for_msg(&self, timeout: Delta) -> Result> { let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?; dev_dbg!( - self.dev, + &self.dev, "GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n", header.sequence(), header.function(), @@ -726,7 +761,7 @@ fn wait_for_msg(&self, timeout: Delta) -> Result> { ])) != 0 { dev_err!( - self.dev, + &self.dev, "GSP RPC: receive: Call {} - bad checksum\n", header.sequence() ); @@ -755,7 +790,7 @@ fn wait_for_msg(&self, timeout: Delta) -> Result> { /// - `ERANGE` if the message had a recognized but non-matching function code. /// /// Error codes returned by [`MessageFromGsp::read`] are propagated as-is. - pub(crate) fn receive_msg(&mut self, timeout: Delta) -> Result + fn receive_msg(&mut self, timeout: Delta) -> Result where // This allows all error types, including `Infallible`, to be used for `M::InitError`. Error: From, @@ -791,9 +826,4 @@ pub(crate) fn receive_msg(&mut self, timeout: Delta) -> Resul result } - - /// Returns the DMA handle of the command queue's shared memory region. - pub(crate) fn dma_handle(&self) -> DmaAddress { - self.gsp_mem.0.dma_handle() - } } diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 77054c92fcc2..c89c7b57a751 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -165,7 +165,7 @@ fn read( } /// Waits for GSP initialization to complete. -pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { +pub(crate) fn wait_gsp_init_done(cmdq: &Cmdq) -> Result { loop { match cmdq.receive_msg::(Cmdq::RECEIVE_TIMEOUT) { Ok(_) => break Ok(()), @@ -234,6 +234,6 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> { } /// Send the [`GetGspInfo`] command and awaits for its reply. -pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result { +pub(crate) fn get_gsp_info(cmdq: &Cmdq, bar: &Bar0) -> Result { cmdq.send_command(bar, GetGspStaticInfo) } diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index ce2b3bb05d22..474e4c8021db 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -356,7 +356,7 @@ pub(crate) struct GspSequencerParams<'a> { } impl<'a> GspSequencer<'a> { - pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result { + pub(crate) fn run(cmdq: &Cmdq, params: GspSequencerParams<'a>) -> Result { let seq_info = loop { match cmdq.receive_msg::(Cmdq::RECEIVE_TIMEOUT) { Ok(seq_info) => break seq_info, From e1eabb072c75681f78312c484ccfffb7430f206e Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Wed, 11 Mar 2026 17:12:15 +0800 Subject: [PATCH 127/712] usb: gadget: u_ether: Fix race between gether_disconnect and eth_stop A race condition between gether_disconnect() and eth_stop() leads to a NULL pointer dereference. Specifically, if eth_stop() is triggered concurrently while gether_disconnect() is tearing down the endpoints, eth_stop() attempts to access the cleared endpoint descriptor, causing the following NPE: Unable to handle kernel NULL pointer dereference Call trace: __dwc3_gadget_ep_enable+0x60/0x788 dwc3_gadget_ep_enable+0x70/0xe4 usb_ep_enable+0x60/0x15c eth_stop+0xb8/0x108 Because eth_stop() crashes while holding the dev->lock, the thread running gether_disconnect() fails to acquire the same lock and spins forever, resulting in a hardlockup: Core - Debugging Information for Hardlockup core(7) Call trace: queued_spin_lock_slowpath+0x94/0x488 _raw_spin_lock+0x64/0x6c gether_disconnect+0x19c/0x1e8 ncm_set_alt+0x68/0x1a0 composite_setup+0x6a0/0xc50 The root cause is that the clearing of dev->port_usb in gether_disconnect() is delayed until the end of the function. Move the clearing of dev->port_usb to the very beginning of gether_disconnect() while holding dev->lock. This cuts off the link immediately, ensuring eth_stop() will see dev->port_usb as NULL and safely bail out. Fixes: 2b3d942c4878 ("usb ethernet gadget: split out network core") Cc: stable Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260311-gether-disconnect-npe-v1-1-454966adf7c7@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ether.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 1a9e7c495e2e..23c7c0cdf202 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1223,6 +1223,11 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); + spin_lock(&dev->lock); + dev->port_usb = NULL; + link->is_suspend = false; + spin_unlock(&dev->lock); + netif_stop_queue(dev->net); netif_carrier_off(dev->net); @@ -1260,11 +1265,6 @@ void gether_disconnect(struct gether *link) dev->header_len = 0; dev->unwrap = NULL; dev->wrap = NULL; - - spin_lock(&dev->lock); - dev->port_usb = NULL; - link->is_suspend = false; - spin_unlock(&dev->lock); } EXPORT_SYMBOL_GPL(gether_disconnect); From 8a768552f7a8276fb9e01d49773d2094ace7c8f1 Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Thu, 12 Mar 2026 09:27:28 -0300 Subject: [PATCH 128/712] usb: usbtmc: Flush anchored URBs in usbtmc_release When calling usbtmc_release, pending anchored URBs must be flushed or killed to prevent use-after-free errors (e.g. in the HCD giveback path). Call usbtmc_draw_down() to allow anchored URBs to be completed. Fixes: 4f3c8d6eddc2 ("usb: usbtmc: Support Read Status Byte with SRQ per file") Reported-by: syzbot+9a3c54f52bd1edbd975f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=9a3c54f52bd1edbd975f Cc: stable Signed-off-by: Heitor Alves de Siqueira Link: https://patch.msgid.link/20260312-usbtmc-flush-release-v1-1-5755e9f4336f@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index d39bbfd7fd18..bd9347804dec 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -254,6 +254,9 @@ static int usbtmc_release(struct inode *inode, struct file *file) list_del(&file_data->file_elem); spin_unlock_irq(&file_data->data->dev_lock); + + /* flush anchored URBs */ + usbtmc_draw_down(file_data); mutex_unlock(&file_data->data->io_mutex); kref_put(&file_data->data->kref, usbtmc_delete); From d2d8c17ac01a1b1f638ea5d340a884ccc5015186 Mon Sep 17 00:00:00 2001 From: Nathan Rebello Date: Fri, 13 Mar 2026 18:24:53 -0400 Subject: [PATCH 129/712] usb: typec: ucsi: validate connector number in ucsi_notify_common() The connector number extracted from CCI via UCSI_CCI_CONNECTOR() is a 7-bit field (0-127) that is used to index into the connector array in ucsi_connector_change(). However, the array is only allocated for the number of connectors reported by the device (typically 2-4 entries). A malicious or malfunctioning device could report an out-of-range connector number in the CCI, causing an out-of-bounds array access in ucsi_connector_change(). Add a bounds check in ucsi_notify_common(), the central point where CCI is parsed after arriving from hardware, so that bogus connector numbers are rejected before they propagate further. Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable Reviewed-by: Heikki Krogerus Signed-off-by: Nathan Rebello Link: https://patch.msgid.link/20260313222453.123-1-nathan.c.rebello@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index f38a4d7ebc42..8333bdaf5566 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -43,8 +43,13 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) if (cci & UCSI_CCI_BUSY) return; - if (UCSI_CCI_CONNECTOR(cci)) - ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); + if (UCSI_CCI_CONNECTOR(cci)) { + if (UCSI_CCI_CONNECTOR(cci) <= ucsi->cap.num_connectors) + ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); + else + dev_err(ucsi->dev, "bogus connector number in CCI: %lu\n", + UCSI_CCI_CONNECTOR(cci)); + } if (cci & UCSI_CCI_ACK_COMPLETE && test_and_clear_bit(ACK_PENDING, &ucsi->flags)) From 616a63ff495df12863692ab3f9f7b84e3fa7a66d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 15 Mar 2026 14:30:43 -0400 Subject: [PATCH 130/712] USB: dummy-hcd: Fix locking/synchronization error Syzbot testing was able to provoke an addressing exception and crash in the usb_gadget_udc_reset() routine in drivers/usb/gadgets/udc/core.c, resulting from the fact that the routine was called with a second ("driver") argument of NULL. The bad caller was set_link_state() in dummy_hcd.c, and the problem arose because of a race between a USB reset and driver unbind. These sorts of races were not supposed to be possible; commit 7dbd8f4cabd9 ("USB: dummy-hcd: Fix erroneous synchronization change"), along with a few followup commits, was written specifically to prevent them. As it turns out, there are (at least) two errors remaining in the code. Another patch will address the second error; this one is concerned with the first. The error responsible for the syzbot crash occurred because the stop_activity() routine will sometimes drop and then re-acquire the dum->lock spinlock. A call to stop_activity() occurs in set_link_state() when handling an emulated USB reset, after the test of dum->ints_enabled and before the increment of dum->callback_usage. This allowed another thread (doing a driver unbind) to sneak in and grab the spinlock, and then clear dum->ints_enabled and dum->driver. Normally this other thread would have to wait for dum->callback_usage to go down to 0 before it would clear dum->driver, but in this case it didn't have to wait since dum->callback_usage had not yet been incremented. The fix is to increment dum->callback_usage _before_ calling stop_activity() instead of after. Then the thread doing the unbind will not clear dum->driver until after the call to usb_gadget_udc_reset() safely returns and dum->callback_usage has been decremented again. Signed-off-by: Alan Stern Reported-by: syzbot+19bed92c97bee999e5db@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-usb/68fc7c9c.050a0220.346f24.023c.GAE@google.com/ Tested-by: syzbot+19bed92c97bee999e5db@syzkaller.appspotmail.com Fixes: 7dbd8f4cabd9 ("USB: dummy-hcd: Fix erroneous synchronization change") Cc: stable Link: https://patch.msgid.link/46135f42-fdbe-46b5-aac0-6ca70492af15@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index c9eca90376e2..860ee7d3ef98 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -462,8 +462,13 @@ static void set_link_state(struct dummy_hcd *dum_hcd) /* Report reset and disconnect events to the driver */ if (dum->ints_enabled && (disconnect || reset)) { - stop_activity(dum); ++dum->callback_usage; + /* + * stop_activity() can drop dum->lock, so it must + * not come between the dum->ints_enabled test + * and the ++dum->callback_usage. + */ + stop_activity(dum); spin_unlock(&dum->lock); if (reset) usb_gadget_udc_reset(&dum->gadget, dum->driver); From 2ca9e46f8f1f5a297eb0ac83f79d35d5b3a02541 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 15 Mar 2026 14:31:00 -0400 Subject: [PATCH 131/712] USB: dummy-hcd: Fix interrupt synchronization error This fixes an error in synchronization in the dummy-hcd driver. The error has a somewhat involved history. The synchronization mechanism was introduced by commit 7dbd8f4cabd9 ("USB: dummy-hcd: Fix erroneous synchronization change"), which added an emulated "interrupts enabled" flag together with code emulating synchronize_irq() (it waits until all current handler callbacks have returned). But the emulated interrupt-disable occurred too late, after the driver containing the handler callback routines had been told that it was unbound and no more callbacks would occur. Commit 4a5d797a9f9c ("usb: gadget: dummy_hcd: fix gpf in gadget_setup") tried to fix this by moving the synchronize_irq() emulation code from dummy_stop() to dummy_pullup(), which runs before the unbind callback. There still were races, though, because the emulated interrupt-disable still occurred too late. It couldn't be moved to dummy_pullup(), because that routine can be called for reasons other than an impending unbind. Therefore commits 7dc0c55e9f30 ("USB: UDC core: Add udc_async_callbacks gadget op") and 04145a03db9d ("USB: UDC: Implement udc_async_callbacks in dummy-hcd") added an API allowing the UDC core to tell dummy-hcd exactly when emulated interrupts and their callbacks should be disabled. That brings us to the current state of things, which is still wrong because the emulated synchronize_irq() occurs before the emulated interrupt-disable! That's no good, beause it means that more emulated interrupts can occur after the synchronize_irq() emulation has run, leading to the possibility that a callback handler may be running when the gadget driver is unbound. To fix this, we have to move the synchronize_irq() emulation code yet again, to the dummy_udc_async_callbacks() routine, which takes care of enabling and disabling emulated interrupt requests. The synchronization will now run immediately after emulated interrupts are disabled, which is where it belongs. Signed-off-by: Alan Stern Fixes: 04145a03db9d ("USB: UDC: Implement udc_async_callbacks in dummy-hcd") Cc: stable Link: https://patch.msgid.link/c7bc93fe-4241-4d04-bd56-27c12ba35c97@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 860ee7d3ef98..e55701575857 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -913,21 +913,6 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value) spin_lock_irqsave(&dum->lock, flags); dum->pullup = (value != 0); set_link_state(dum_hcd); - if (value == 0) { - /* - * Emulate synchronize_irq(): wait for callbacks to finish. - * This seems to be the best place to emulate the call to - * synchronize_irq() that's in usb_gadget_remove_driver(). - * Doing it in dummy_udc_stop() would be too late since it - * is called after the unbind callback and unbind shouldn't - * be invoked until all the other callbacks are finished. - */ - while (dum->callback_usage > 0) { - spin_unlock_irqrestore(&dum->lock, flags); - usleep_range(1000, 2000); - spin_lock_irqsave(&dum->lock, flags); - } - } spin_unlock_irqrestore(&dum->lock, flags); usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); @@ -950,6 +935,20 @@ static void dummy_udc_async_callbacks(struct usb_gadget *_gadget, bool enable) spin_lock_irq(&dum->lock); dum->ints_enabled = enable; + if (!enable) { + /* + * Emulate synchronize_irq(): wait for callbacks to finish. + * This has to happen after emulated interrupts are disabled + * (dum->ints_enabled is clear) and before the unbind callback, + * just like the call to synchronize_irq() in + * gadget/udc/core:gadget_unbind_driver(). + */ + while (dum->callback_usage > 0) { + spin_unlock_irq(&dum->lock); + usleep_range(1000, 2000); + spin_lock_irq(&dum->lock); + } + } spin_unlock_irq(&dum->lock); } From e002e92e88e12457373ed096b18716d97e7bbb20 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Mon, 16 Mar 2026 15:49:09 +0800 Subject: [PATCH 132/712] usb: gadget: u_ether: Fix NULL pointer deref in eth_get_drvinfo Commit ec35c1969650 ("usb: gadget: f_ncm: Fix net_device lifecycle with device_move") reparents the gadget device to /sys/devices/virtual during unbind, clearing the gadget pointer. If the userspace tool queries on the surviving interface during this detached window, this leads to a NULL pointer dereference. Unable to handle kernel NULL pointer dereference Call trace: eth_get_drvinfo+0x50/0x90 ethtool_get_drvinfo+0x5c/0x1f0 __dev_ethtool+0xaec/0x1fe0 dev_ethtool+0x134/0x2e0 dev_ioctl+0x338/0x560 Add a NULL check for dev->gadget in eth_get_drvinfo(). When detached, skip copying the fw_version and bus_info strings, which is natively handled by ethtool_get_drvinfo for empty strings. Suggested-by: Val Packett Reported-by: Val Packett Closes: https://lore.kernel.org/linux-usb/10890524-cf83-4a71-b879-93e2b2cc1fcc@packett.cool/ Fixes: ec35c1969650 ("usb: gadget: f_ncm: Fix net_device lifecycle with device_move") Cc: stable Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260316-eth-null-deref-v1-1-07005f33be85@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ether.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 23c7c0cdf202..59d85d6a84a8 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -113,8 +113,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) strscpy(p->driver, "g_ether", sizeof(p->driver)); strscpy(p->version, UETH__VERSION, sizeof(p->version)); - strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); - strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); + if (dev->gadget) { + strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); + strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); + } } /* REVISIT can also support: From b2f6648c735639d246dc5f98f377b69d5374c2bd Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 16 Mar 2026 17:48:11 +0800 Subject: [PATCH 133/712] usb: hcd: queue wakeup_work to system_freezable_wq workqueue After commit 4fb352df14de ("PM: sleep: Do not flag runtime PM workqueue as freezable"), pm_wq workqueue will be unfreezable during system pm. This brings issue as below: [ 344.255749] ------------[ cut here ]------------ [ 344.277740] URB 000000004aae4ad1 submitted while active [ 344.282996] WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x5a4/0x5e0, CPU#2: kworker/u16:14/964 [ 344.292477] Modules linked in: [ 344.295532] CPU: 2 UID: 0 PID: 964 Comm: kworker/u16:14 Not tainted 7.0.0-rc2-next-20260303-00006-gf03fe0b53b39-dirty #100 PREEMPT [ 344.307341] Hardware name: NXP i.MX943 EVK board (DT) [ 344.312386] Workqueue: async async_run_entry_fn [ 344.316919] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 344.323862] pc : usb_submit_urb+0x5a4/0x5e0 [ 344.328046] lr : usb_submit_urb+0x5a4/0x5e0 [ 344.332217] sp : ffff800083283b30 [ 344.335528] x29: ffff800083283b30 x28: ffff000082631000 x27: 0000000000000000 [ 344.342661] x26: 0000000000000003 x25: 0000000000000c00 x24: 0000000000000000 [ 344.349793] x23: 0000000000000004 x22: 0000000000000010 x21: 0000000000000000 [ 344.356917] x20: 0000000000000002 x19: ffff00008253ce40 x18: ffff000089e4eec0 [ 344.364050] x17: 000000040044ffff x16: 000005d9d87f6289 x15: 0000000000000000 [ 344.371174] x14: ffff000089e4eec0 x13: 6576697463612065 x12: 6c69687720646574 [ 344.378298] x11: 0000000000000058 x10: 0000000000000001 x9 : 0000000000000001 [ 344.385431] x8 : 0000000000000000 x7 : 0000005028dd0800 x6 : 0000000000000002 [ 344.392563] x5 : ffffc48f74e9daf8 x4 : 0000000000000000 x3 : 0000000000000000 [ 344.399696] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff000089e4ee40 [ 344.406835] Call trace: [ 344.409280] usb_submit_urb+0x5a4/0x5e0 (P) [ 344.413456] hub_activate+0x268/0x878 [ 344.417116] hub_resume+0x5c/0x10c [ 344.420522] usb_resume_interface.isra.0+0xa8/0x110 [ 344.425393] usb_resume_both+0x10c/0x1e0 [ 344.429310] usb_resume+0x1c/0x70 [ 344.432621] usb_dev_resume+0x14/0x20 [ 344.436278] dpm_run_callback.isra.0+0x38/0xf8 [ 344.440715] device_resume+0xec/0x1e8 [ 344.444373] async_resume+0x20/0x38 [ 344.447848] async_run_entry_fn+0x34/0xe0 [ 344.451852] process_one_work+0x150/0x290 [ 344.455856] worker_thread+0x18c/0x300 [ 344.459600] kthread+0x118/0x124 [ 344.462824] ret_from_fork+0x10/0x20 The reason is if the host controller resume routine xhci_resume() call usb_hcd_resume_root_hub(), wakeup_work will be queued and run immediately. Then usb_autoresume_device() will be called and usb device will exit runtime suspended state (if it was suspended before). For a hub device, hub_resume()/hub_reset_resume() will be called accordingly. After the host controller device system resume is finished, the root hub usb device "usb1" will do system resume too. Then hub_resume() will be called again. Above sequence will cause hub->urb to be submitted twice. To avoid this issue, restore the previous behavior by queuing wakeup_work to system_freezable_wq workqueue. Acked-by: Alan Stern Fixes: 4fb352df14de ("PM: sleep: Do not flag runtime PM workqueue as freezable") Cc: stable Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260316094811.1559471-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index dee842ea6931..89221f1ce769 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2403,7 +2403,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) if (hcd->rh_registered) { pm_wakeup_event(&hcd->self.root_hub->dev, 0); set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); - queue_work(pm_wq, &hcd->wakeup_work); + queue_work(system_freezable_wq, &hcd->wakeup_work); } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } From f97e96c303d689708f7f713d8f3afcc31f1237e9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 17 Mar 2026 09:41:10 +0100 Subject: [PATCH 134/712] cdc-acm: new quirk for EPSON HMD This device has a union descriptor that is just garbage and needs a custom descriptor. In principle this could be done with a (conditionally activated) heuristic. That would match more devices without a need for defining a new quirk. However, this always carries the risk that the heuristics does the wrong thing and leads to more breakage. Defining the quirk and telling it exactly what to do is the safe and conservative approach. Signed-off-by: Oliver Neukum Cc: stable Link: https://patch.msgid.link/20260317084139.1461008-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 9 +++++++++ drivers/usb/class/cdc-acm.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 7ede29d4c7c1..cf3c3eede1a5 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1225,6 +1225,12 @@ static int acm_probe(struct usb_interface *intf, if (!data_interface || !control_interface) return -ENODEV; goto skip_normal_probe; + } else if (quirks == NO_UNION_12) { + data_interface = usb_ifnum_to_if(usb_dev, 2); + control_interface = usb_ifnum_to_if(usb_dev, 1); + if (!data_interface || !control_interface) + return -ENODEV; + goto skip_normal_probe; } /* normal probing*/ @@ -1748,6 +1754,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x045b, 0x024D), /* Renesas R-Car E3 USB Download mode */ .driver_info = DISABLE_ECHO, /* Don't echo banner */ }, + { USB_DEVICE(0x04b8, 0x0d12), /* EPSON HMD Com&Sens */ + .driver_info = NO_UNION_12, /* union descriptor is garbage */ + }, { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 76f73853a60b..25fd5329a878 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -114,3 +114,4 @@ struct acm { #define SEND_ZERO_PACKET BIT(6) #define DISABLE_ECHO BIT(7) #define MISSING_CAP_BRK BIT(8) +#define NO_UNION_12 BIT(9) From 1a122198ee26d2f328edae802b2ca4fa0518a20a Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 16 Mar 2026 23:30:06 +0800 Subject: [PATCH 135/712] dwc3: google: Fix PM domain leak in dwc3_google_probe() When syscon_regmap_lookup_by_phandle_args() fails, the function was returning directly without cleaning up the power domain initialized earlier by dwc3_google_pm_domain_init(). Fix by jumping to err_deinit_pdom to properly clean up resources. Fixes: 8995a37371bf4 ("usb: dwc3: Add Google Tensor SoC DWC3 glue driver") Signed-off-by: Felix Gu Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260316-dwc3-google-v1-1-c9bde1b02f62@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-google.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c index 2105c72af753..4ca567ec01d0 100644 --- a/drivers/usb/dwc3/dwc3-google.c +++ b/drivers/usb/dwc3/dwc3-google.c @@ -385,8 +385,9 @@ static int dwc3_google_probe(struct platform_device *pdev) "google,usb-cfg-csr", ARRAY_SIZE(args), args); if (IS_ERR(google->usb_cfg_regmap)) { - return dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap), - "invalid usb cfg csr\n"); + ret = dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap), + "invalid usb cfg csr\n"); + goto err_deinit_pdom; } google->host_cfg_offset = args[0]; From f50200dd44125e445a6164e88c217472fa79cdbc Mon Sep 17 00:00:00 2001 From: Sebastian Urban Date: Sun, 15 Mar 2026 16:10:45 +0100 Subject: [PATCH 136/712] usb: gadget: dummy_hcd: fix premature URB completion when ZLP follows partial transfer When a gadget request is only partially transferred in transfer() because the per-frame bandwidth budget is exhausted, the loop advances to the next queued request. If that next request is a zero-length packet (ZLP), len evaluates to zero and the code takes the unlikely(len == 0) path, which sets is_short = 1. This bypasses the bandwidth guard ("limit < ep->ep.maxpacket && limit < len") that lives in the else branch and would otherwise break out of the loop for non-zero requests. The is_short path then completes the URB before all data from the first request has been transferred. Reproducer (bulk IN, high speed): Device side (FunctionFS with Linux AIO): 1. Queue a 65024-byte write via io_submit (127 * 512, i.e. a multiple of the HS bulk max packet size). 2. Immediately queue a zero-length write (ZLP) via io_submit. Host side: 3. Submit a 65536-byte bulk IN URB. Expected: URB completes with actual_length = 65024. Actual: URB completes with actual_length = 53248, losing 11776 bytes that leak into subsequent URBs. At high speed the per-frame budget is 53248 bytes (512 * 13 * 8). The 65024-byte request exhausts this budget after 53248 bytes, leaving the request incomplete (req->req.actual < req->req.length). Neither the request nor the URB is finished, and rescan is 0, so the loop advances to the ZLP. For the ZLP, dev_len = 0, so len = min(12288, 0) = 0, taking the unlikely(len == 0) path and setting is_short = 1. The is_short handler then sets *status = 0, completing the URB with only 53248 of the expected 65024 bytes. Fix this by breaking out of the loop when the current request has remaining data (req->req.actual < req->req.length). The request resumes on the next timer tick, preserving correct data ordering. Signed-off-by: Sebastian Urban Cc: stable Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260315151045.1155850-1-surban@surban.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index e55701575857..f094491b1041 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1538,6 +1538,12 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb, /* rescan to continue with any other queued i/o */ if (rescan) goto top; + + /* request not fully transferred; stop iterating to + * preserve data ordering across queued requests. + */ + if (req->req.actual < req->req.length) + break; } return sent; } From a464bace0482aa9a83e9aa7beefbaf44cd58e6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Mar 2026 13:07:40 +0200 Subject: [PATCH 137/712] drm/i915: Order OP vs. timeout correctly in __wait_for() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put the barrier() before the OP so that anything we read out in OP and check in COND will actually be read out after the timeout has been evaluated. Currently the only place where we use OP is __intel_wait_for_register(), but the use there is precisely susceptible to this reordering, assuming the ktime_*() stuff itself doesn't act as a sufficient barrier: __intel_wait_for_register(...) { ... ret = __wait_for(reg_value = intel_uncore_read_notrace(...), (reg_value & mask) == value, ...); ... } Cc: stable@vger.kernel.org Fixes: 1c3c1dc66a96 ("drm/i915: Add compiler barrier to wait_for") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260313110740.24620-1-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/i915_wait_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_wait_util.h b/drivers/gpu/drm/i915/i915_wait_util.h index 7376898e3bf8..e1ed7921ec70 100644 --- a/drivers/gpu/drm/i915/i915_wait_util.h +++ b/drivers/gpu/drm/i915/i915_wait_util.h @@ -25,9 +25,9 @@ might_sleep(); \ for (;;) { \ const bool expired__ = ktime_after(ktime_get_raw(), end__); \ - OP; \ /* Guarantee COND check prior to timeout */ \ barrier(); \ + OP; \ if (COND) { \ ret__ = 0; \ break; \ From 017ecd04985573eeeb0745fa2c23896fb22ee0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 16 Mar 2026 18:39:51 +0200 Subject: [PATCH 138/712] drm/i915: Unlink NV12 planes earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit unlink_nv12_plane() will clobber parts of the plane state potentially already set up by plane_atomic_check(), so we must make sure not to call the two in the wrong order. The problem happens when a plane previously selected as a Y plane is now configured as a normal plane by user space. plane_atomic_check() will first compute the proper plane state based on the userspace request, and unlink_nv12_plane() later clears some of the state. This used to work on account of unlink_nv12_plane() skipping the state clearing based on the plane visibility. But I removed that check, thinking it was an impossible situation. Now when that situation happens unlink_nv12_plane() will just WARN and proceed to clobber the state. Rather than reverting to the old way of doing things, I think it's more clear if we unlink the NV12 planes before we even compute the new plane state. Cc: stable@vger.kernel.org Reported-by: Khaled Almahallawy Closes: https://lore.kernel.org/intel-gfx/20260212004852.1920270-1-khaled.almahallawy@intel.com/ Tested-by: Khaled Almahallawy Fixes: 6a01df2f1b2a ("drm/i915: Remove pointless visible check in unlink_nv12_plane()") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260316163953.12905-2-ville.syrjala@linux.intel.com Reviewed-by: Uma Shankar --- drivers/gpu/drm/i915/display/intel_plane.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index e06a0618b4c6..076b9b356481 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -436,11 +436,16 @@ void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, drm_framebuffer_get(plane_state->hw.fb); } +static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state); + void intel_plane_set_invisible(struct intel_crtc_state *crtc_state, struct intel_plane_state *plane_state) { struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + unlink_nv12_plane(crtc_state, plane_state); + crtc_state->active_planes &= ~BIT(plane->id); crtc_state->scaled_planes &= ~BIT(plane->id); crtc_state->nv12_planes &= ~BIT(plane->id); @@ -1513,6 +1518,9 @@ static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, struct intel_display *display = to_intel_display(plane_state); struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + if (!plane_state->planar_linked_plane) + return; + plane_state->planar_linked_plane = NULL; if (!plane_state->is_y_plane) @@ -1550,8 +1558,7 @@ static int icl_check_nv12_planes(struct intel_atomic_state *state, if (plane->pipe != crtc->pipe) continue; - if (plane_state->planar_linked_plane) - unlink_nv12_plane(crtc_state, plane_state); + unlink_nv12_plane(crtc_state, plane_state); } if (!crtc_state->nv12_planes) From 7b3a14322d1a8cbc6facdc857ac81960497e1c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 16 Mar 2026 18:39:52 +0200 Subject: [PATCH 139/712] drm/i915: Relocate unlink_nv12_plane() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move unlink_nv12_plane() ahead of its first caller to avoid the forward declaration. Cc: Khaled Almahallawy Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260316163953.12905-3-ville.syrjala@linux.intel.com Reviewed-by: Uma Shankar --- drivers/gpu/drm/i915/display/intel_plane.c | 49 ++++++++++------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 076b9b356481..bc1c801a06d7 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -437,7 +437,29 @@ void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, } static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, - struct intel_plane_state *plane_state); + struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane_state); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + + if (!plane_state->planar_linked_plane) + return; + + plane_state->planar_linked_plane = NULL; + + if (!plane_state->is_y_plane) + return; + + drm_WARN_ON(display->drm, plane_state->uapi.visible); + + plane_state->is_y_plane = false; + + crtc_state->enabled_planes &= ~BIT(plane->id); + crtc_state->active_planes &= ~BIT(plane->id); + crtc_state->update_planes |= BIT(plane->id); + crtc_state->data_rate[plane->id] = 0; + crtc_state->rel_data_rate[plane->id] = 0; +} void intel_plane_set_invisible(struct intel_crtc_state *crtc_state, struct intel_plane_state *plane_state) @@ -1512,31 +1534,6 @@ static void link_nv12_planes(struct intel_crtc_state *crtc_state, icl_link_nv12_planes(uv_plane_state, y_plane_state); } -static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, - struct intel_plane_state *plane_state) -{ - struct intel_display *display = to_intel_display(plane_state); - struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); - - if (!plane_state->planar_linked_plane) - return; - - plane_state->planar_linked_plane = NULL; - - if (!plane_state->is_y_plane) - return; - - drm_WARN_ON(display->drm, plane_state->uapi.visible); - - plane_state->is_y_plane = false; - - crtc_state->enabled_planes &= ~BIT(plane->id); - crtc_state->active_planes &= ~BIT(plane->id); - crtc_state->update_planes |= BIT(plane->id); - crtc_state->data_rate[plane->id] = 0; - crtc_state->rel_data_rate[plane->id] = 0; -} - static int icl_check_nv12_planes(struct intel_atomic_state *state, struct intel_crtc *crtc) { From c5121204ad99196b6f80d12e5029e9a766cd8008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 16 Mar 2026 18:39:53 +0200 Subject: [PATCH 140/712] drm/i915: Skip redundant NV12 plane unlinking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plane_atomic_check() will already have unlinked the old NV12 planes by the time icl_check_nv12_planes() gets called. Drop the redundant second unlinking. Cc: Khaled Almahallawy Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260316163953.12905-4-ville.syrjala@linux.intel.com Reviewed-by: Uma Shankar --- drivers/gpu/drm/i915/display/intel_plane.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index bc1c801a06d7..5390ceb21ca4 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -1547,17 +1547,6 @@ static int icl_check_nv12_planes(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11) return 0; - /* - * Destroy all old plane links and make the Y plane invisible - * in the crtc_state->active_planes mask. - */ - for_each_new_intel_plane_in_state(state, plane, plane_state, i) { - if (plane->pipe != crtc->pipe) - continue; - - unlink_nv12_plane(crtc_state, plane_state); - } - if (!crtc_state->nv12_planes) return 0; From c2d3e41f6e52269736cb9ecb17416e04f52b959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:26 +0200 Subject: [PATCH 141/712] drm/i915/wm: Nuke is_planar from skl+ wm structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need is_planar in either the actual watermarks or the wm_params structure used during the wm computation. Get rid of both. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/skl_watermark.c | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index d3a9ace4c9d1..93b8b2f91484 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -851,7 +851,6 @@ struct skl_plane_wm { struct skl_wm_level wm0; struct skl_wm_level trans_wm; } sagv; - bool is_planar; }; struct skl_pipe_wm { diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index b1f9546b8cda..0f99a3264f05 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -63,7 +63,6 @@ static void skl_sagv_disable(struct intel_display *display); struct skl_wm_params { bool x_tiled, y_tiled; bool rc_surface; - bool is_planar; u32 width; u8 cpp; u32 plane_pixel_rate; @@ -1675,10 +1674,9 @@ skl_compute_wm_params(const struct intel_crtc_state *crtc_state, wp->y_tiled = modifier != I915_FORMAT_MOD_X_TILED && intel_fb_is_tiled_modifier(modifier); wp->rc_surface = intel_fb_is_ccs_modifier(modifier); - wp->is_planar = intel_format_info_is_yuv_semiplanar(format, modifier); wp->width = width; - if (color_plane == 1 && wp->is_planar) + if (color_plane == 1 && intel_format_info_is_yuv_semiplanar(format, modifier)) wp->width /= 2; wp->cpp = format->cpp[color_plane]; @@ -2073,8 +2071,6 @@ static int skl_build_plane_wm_uv(struct intel_crtc_state *crtc_state, struct skl_wm_params wm_params; int ret; - wm->is_planar = true; - /* uv plane watermarks must also be validated for NV12/Planar */ ret = skl_compute_plane_wm_params(crtc_state, plane_state, &wm_params, 1); From cdb41e341c6c174e9e778c02caa56eb08d256c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:27 +0200 Subject: [PATCH 142/712] drm/i915/wm: Reorder the arguments to skl_allocate_plane_ddb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group the ddb and data_rate together in the skl_allocate_plane_ddb() arguments. Upcoming changes will adjust the UV plane handling and keeing the ddb allocation and the data rate used to calculate it together will help with clarity. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 0f99a3264f05..1664b84d0387 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1391,9 +1391,8 @@ struct skl_plane_ddb_iter { static void skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, - struct skl_ddb_entry *ddb, const struct skl_wm_level *wm, - u64 data_rate) + struct skl_ddb_entry *ddb, u64 data_rate) { u16 size, extra = 0; @@ -1523,13 +1522,13 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11 && crtc_state->nv12_planes & BIT(plane_id)) { - skl_allocate_plane_ddb(&iter, ddb_y, &wm->wm[level], - crtc_state->rel_data_rate_y[plane_id]); - skl_allocate_plane_ddb(&iter, ddb, &wm->uv_wm[level], - crtc_state->rel_data_rate[plane_id]); + skl_allocate_plane_ddb(&iter, &wm->wm[level], + ddb_y, crtc_state->rel_data_rate_y[plane_id]); + skl_allocate_plane_ddb(&iter, &wm->uv_wm[level], + ddb, crtc_state->rel_data_rate[plane_id]); } else { - skl_allocate_plane_ddb(&iter, ddb, &wm->wm[level], - crtc_state->rel_data_rate[plane_id]); + skl_allocate_plane_ddb(&iter, &wm->wm[level], + ddb, crtc_state->rel_data_rate[plane_id]); } if (DISPLAY_VER(display) >= 30) { From cd4387fa57b4a2b276b40c61caa6e2e6ad217724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:28 +0200 Subject: [PATCH 143/712] drm/i915/wm: s/skl_check_nv12_wm_level()/skl_check_wm_level_nv12()/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename skl_check_nv12_wm_level() to skl_check_wm_level_nv12(). There will be a sort of DDB counterparts to skl_check_wm_level*(), and putting the "nv12" part to the end will allow consistent naming. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-4-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 1664b84d0387..24978f312fec 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1356,7 +1356,7 @@ skl_check_wm_level(struct skl_wm_level *wm, const struct skl_ddb_entry *ddb) } static void -skl_check_nv12_wm_level(struct skl_wm_level *wm, struct skl_wm_level *uv_wm, +skl_check_wm_level_nv12(struct skl_wm_level *wm, struct skl_wm_level *uv_wm, const struct skl_ddb_entry *ddb_y, const struct skl_ddb_entry *ddb) { if (wm->min_ddb_alloc > skl_ddb_entry_size(ddb_y) || @@ -1555,7 +1555,7 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11 && crtc_state->nv12_planes & BIT(plane_id)) - skl_check_nv12_wm_level(&wm->wm[level], + skl_check_wm_level_nv12(&wm->wm[level], &wm->uv_wm[level], ddb_y, ddb); else From bcc8da0436839562becbbc9e1f134559e86a8470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:29 +0200 Subject: [PATCH 144/712] drm/i915/wm: Extract skl_allocate_plane_ddb_nv12() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract skl_allocate_plane_ddb_nv12() as the compute counterpart to skl_check_wm_level_nv12(). Mainly to hide some of the clutter from skl_crtc_allocate_plane_ddb(). Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-5-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 40 ++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 24978f312fec..7c4c42dde991 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1390,9 +1390,9 @@ struct skl_plane_ddb_iter { }; static void -skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, - const struct skl_wm_level *wm, - struct skl_ddb_entry *ddb, u64 data_rate) +_skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, + u16 min_ddb_alloc, + struct skl_ddb_entry *ddb, u64 data_rate) { u16 size, extra = 0; @@ -1409,12 +1409,31 @@ skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, * to avoid skl_ddb_add_affected_planes() adding them to * the state when other planes change their allocations. */ - size = wm->min_ddb_alloc + extra; + size = min_ddb_alloc + extra; if (size) iter->start = skl_ddb_entry_init(ddb, iter->start, iter->start + size); } +static void +skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, + const struct skl_wm_level *wm, + struct skl_ddb_entry *ddb, u64 data_rate) +{ + _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc, ddb, data_rate); +} + +static void +skl_allocate_plane_ddb_nv12(struct skl_plane_ddb_iter *iter, + const struct skl_wm_level *wm, + struct skl_ddb_entry *ddb_y, u64 data_rate_y, + const struct skl_wm_level *uv_wm, + struct skl_ddb_entry *ddb, u64 data_rate) +{ + _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc, ddb_y, data_rate_y); + _skl_allocate_plane_ddb(iter, uv_wm->min_ddb_alloc, ddb, data_rate); +} + static int skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, struct intel_crtc *crtc) @@ -1521,15 +1540,14 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, continue; if (DISPLAY_VER(display) < 11 && - crtc_state->nv12_planes & BIT(plane_id)) { - skl_allocate_plane_ddb(&iter, &wm->wm[level], - ddb_y, crtc_state->rel_data_rate_y[plane_id]); - skl_allocate_plane_ddb(&iter, &wm->uv_wm[level], - ddb, crtc_state->rel_data_rate[plane_id]); - } else { + crtc_state->nv12_planes & BIT(plane_id)) + skl_allocate_plane_ddb_nv12(&iter, &wm->wm[level], + ddb_y, crtc_state->rel_data_rate_y[plane_id], + &wm->uv_wm[level], + ddb, crtc_state->rel_data_rate[plane_id]); + else skl_allocate_plane_ddb(&iter, &wm->wm[level], ddb, crtc_state->rel_data_rate[plane_id]); - } if (DISPLAY_VER(display) >= 30) { *min_ddb = wm->wm[0].min_ddb_alloc; From eb0ec2989e717a94f5e0bce9d73526a6fc598ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:30 +0200 Subject: [PATCH 145/712] drm/i915/wm: Nuke wm->uv_wm[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently keep around the full watermarks for the UV plane on pre-icl, even though the hardware doesn't need most of this information. The only thing we need to keep is the min_ddb_alloc for the UV plane. Move that into the main wm->wm[].min_ddb_alloc_uv alongside the other min_ddb_alloc (used for Y/RGB). This makes our state tracking match the hardware more closely, and avoids having to justify everwhere why uv_wm[] is being ignored. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-6-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- .../drm/i915/display/intel_display_types.h | 2 +- drivers/gpu/drm/i915/display/skl_watermark.c | 43 ++++++++----------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 93b8b2f91484..e2496db1642a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -835,6 +835,7 @@ struct intel_pipe_wm { struct skl_wm_level { u16 min_ddb_alloc; + u16 min_ddb_alloc_uv; /* for pre-icl */ u16 blocks; u8 lines; bool enable; @@ -845,7 +846,6 @@ struct skl_wm_level { struct skl_plane_wm { struct skl_wm_level wm[8]; - struct skl_wm_level uv_wm[8]; struct skl_wm_level trans_wm; struct { struct skl_wm_level wm0; diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 7c4c42dde991..8b1b371fbfab 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1356,14 +1356,13 @@ skl_check_wm_level(struct skl_wm_level *wm, const struct skl_ddb_entry *ddb) } static void -skl_check_wm_level_nv12(struct skl_wm_level *wm, struct skl_wm_level *uv_wm, - const struct skl_ddb_entry *ddb_y, const struct skl_ddb_entry *ddb) +skl_check_wm_level_nv12(struct skl_wm_level *wm, + const struct skl_ddb_entry *ddb_y, + const struct skl_ddb_entry *ddb) { if (wm->min_ddb_alloc > skl_ddb_entry_size(ddb_y) || - uv_wm->min_ddb_alloc > skl_ddb_entry_size(ddb)) { + wm->min_ddb_alloc_uv > skl_ddb_entry_size(ddb)) memset(wm, 0, sizeof(*wm)); - memset(uv_wm, 0, sizeof(*uv_wm)); - } } static bool skl_need_wm_copy_wa(struct intel_display *display, int level, @@ -1427,11 +1426,10 @@ static void skl_allocate_plane_ddb_nv12(struct skl_plane_ddb_iter *iter, const struct skl_wm_level *wm, struct skl_ddb_entry *ddb_y, u64 data_rate_y, - const struct skl_wm_level *uv_wm, struct skl_ddb_entry *ddb, u64 data_rate) { _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc, ddb_y, data_rate_y); - _skl_allocate_plane_ddb(iter, uv_wm->min_ddb_alloc, ddb, data_rate); + _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc_uv, ddb, data_rate); } static int @@ -1499,7 +1497,7 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, } blocks += wm->wm[level].min_ddb_alloc; - blocks += wm->uv_wm[level].min_ddb_alloc; + blocks += wm->wm[level].min_ddb_alloc_uv; } if (blocks <= iter.size) { @@ -1543,7 +1541,6 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, crtc_state->nv12_planes & BIT(plane_id)) skl_allocate_plane_ddb_nv12(&iter, &wm->wm[level], ddb_y, crtc_state->rel_data_rate_y[plane_id], - &wm->uv_wm[level], ddb, crtc_state->rel_data_rate[plane_id]); else skl_allocate_plane_ddb(&iter, &wm->wm[level], @@ -1573,9 +1570,7 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11 && crtc_state->nv12_planes & BIT(plane_id)) - skl_check_wm_level_nv12(&wm->wm[level], - &wm->uv_wm[level], - ddb_y, ddb); + skl_check_wm_level_nv12(&wm->wm[level], ddb_y, ddb); else skl_check_wm_level(&wm->wm[level], ddb); @@ -2084,9 +2079,11 @@ static int skl_build_plane_wm_uv(struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, struct intel_plane *plane) { + struct intel_display *display = to_intel_display(crtc_state); struct skl_plane_wm *wm = &crtc_state->wm.skl.raw.planes[plane->id]; + struct skl_wm_level uv_wm[ARRAY_SIZE(wm->wm)] = {}; struct skl_wm_params wm_params; - int ret; + int ret, level; /* uv plane watermarks must also be validated for NV12/Planar */ ret = skl_compute_plane_wm_params(crtc_state, plane_state, @@ -2094,7 +2091,14 @@ static int skl_build_plane_wm_uv(struct intel_crtc_state *crtc_state, if (ret) return ret; - skl_compute_wm_levels(crtc_state, plane, &wm_params, wm->uv_wm); + skl_compute_wm_levels(crtc_state, plane, &wm_params, uv_wm); + + /* + * Only keep the min_ddb_alloc for UV as + * the hardware needs nothing else. + */ + for (level = 0; level < display->wm.num_levels; level++) + wm->wm[level].min_ddb_alloc_uv = uv_wm[level].min_ddb_alloc; return 0; } @@ -2317,7 +2321,6 @@ static int skl_wm_check_vblank(struct intel_crtc_state *crtc_state) * thing as bad via min_ddb_alloc=U16_MAX? */ wm->wm[level].enable = false; - wm->uv_wm[level].enable = false; } } @@ -2388,11 +2391,6 @@ static bool skl_plane_wm_equals(struct intel_display *display, int level; for (level = 0; level < display->wm.num_levels; level++) { - /* - * We don't check uv_wm as the hardware doesn't actually - * use it. It only gets used for calculating the required - * ddb allocation. - */ if (!skl_wm_level_equals(&wm1->wm[level], &wm2->wm[level])) return false; } @@ -2753,11 +2751,6 @@ static bool skl_plane_selected_wm_equals(struct intel_plane *plane, int level; for (level = 0; level < display->wm.num_levels; level++) { - /* - * We don't check uv_wm as the hardware doesn't actually - * use it. It only gets used for calculating the required - * ddb allocation. - */ if (!skl_wm_level_equals(skl_plane_wm_level(old_pipe_wm, plane->id, level), skl_plane_wm_level(new_pipe_wm, plane->id, level))) return false; From e2a5a5b8876137df75506ca9272619813f958b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:31 +0200 Subject: [PATCH 146/712] drm/i915/wm: s/skl_print_plane_changes()/skl_print_plane_wm_changes()/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename skl_print_plane_changes() to skl_print_plane_wm_changes() to better reflect what it does. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-7-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 8b1b371fbfab..6c8dab847ae2 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2602,11 +2602,12 @@ static char enast(bool enable) } static noinline_for_stack void -skl_print_plane_changes(struct intel_display *display, - struct intel_plane *plane, - const struct skl_plane_wm *old_wm, - const struct skl_plane_wm *new_wm) +skl_print_plane_wm_changes(struct intel_plane *plane, + const struct skl_plane_wm *old_wm, + const struct skl_plane_wm *new_wm) { + struct intel_display *display = to_intel_display(plane); + drm_dbg_kms(display->drm, "[PLANE:%d:%s] level %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm" " -> %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm\n", @@ -2738,7 +2739,7 @@ skl_print_wm_changes(struct intel_atomic_state *state) if (skl_plane_wm_equals(display, old_wm, new_wm)) continue; - skl_print_plane_changes(display, plane, old_wm, new_wm); + skl_print_plane_wm_changes(plane, old_wm, new_wm); } } } From 27e58f7614533c0ba48d5919032283732f5342b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:32 +0200 Subject: [PATCH 147/712] drm/i915/wm: Extract skl_print_plane_ddb_changes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have skl_print_plane_wm_changes() but the DDB counterpart is just inline in the main loop. Extract it into a function. We'll have a second use for this soon. The "ddb" part is already parametrized in anticipation of the second user. v2: Use prink field width for ddb_name alignment Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-8-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 6c8dab847ae2..6ad0546c928f 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2601,6 +2601,21 @@ static char enast(bool enable) return enable ? '*' : ' '; } +static void +skl_print_plane_ddb_changes(struct intel_plane *plane, + const struct skl_ddb_entry *old, + const struct skl_ddb_entry *new, + const char *ddb_name) +{ + struct intel_display *display = to_intel_display(plane); + + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] %5s (%4d - %4d) -> (%4d - %4d), size %4d -> %4d\n", + plane->base.base.id, plane->base.name, ddb_name, + old->start, old->end, new->start, new->end, + skl_ddb_entry_size(old), skl_ddb_entry_size(new)); +} + static noinline_for_stack void skl_print_plane_wm_changes(struct intel_plane *plane, const struct skl_plane_wm *old_wm, @@ -2722,11 +2737,8 @@ skl_print_wm_changes(struct intel_atomic_state *state) if (skl_ddb_entry_equal(old, new)) continue; - drm_dbg_kms(display->drm, - "[PLANE:%d:%s] ddb (%4d - %4d) -> (%4d - %4d), size %4d -> %4d\n", - plane->base.base.id, plane->base.name, - old->start, old->end, new->start, new->end, - skl_ddb_entry_size(old), skl_ddb_entry_size(new)); + + skl_print_plane_ddb_changes(plane, old, new, "ddb"); } for_each_intel_plane_on_crtc(display->drm, crtc, plane) { From e4ab44d8b28db868955d0da5dcadae5ef99a1dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:33 +0200 Subject: [PATCH 148/712] drm/i915/wm: Include ddb_y in skl_print_wm_changes() on pre-icl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-icl doesn't use a separate hardware plane for Y scanout, and instead it's all handled magically by the hardware. We do still need to allocate DDB space for the Y color plane though (PLANE_NV12_BUF_CFG). Include that information in the debugs so that we know where it ended up. On icl+ the equivalent information is dumped as the hardware Y plane's normal ddb allocation. v2: Use prink field width for ddb_name alignment Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-9-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 6ad0546c928f..e9cfbb7c9a77 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2735,10 +2735,17 @@ skl_print_wm_changes(struct intel_atomic_state *state) old = &old_crtc_state->wm.skl.plane_ddb[plane_id]; new = &new_crtc_state->wm.skl.plane_ddb[plane_id]; - if (skl_ddb_entry_equal(old, new)) + if (!skl_ddb_entry_equal(old, new)) + skl_print_plane_ddb_changes(plane, old, new, "ddb"); + + if (DISPLAY_VER(display) >= 11) continue; - skl_print_plane_ddb_changes(plane, old, new, "ddb"); + old = &old_crtc_state->wm.skl.plane_ddb_y[plane_id]; + new = &new_crtc_state->wm.skl.plane_ddb_y[plane_id]; + + if (!skl_ddb_entry_equal(old, new)) + skl_print_plane_ddb_changes(plane, old, new, "ddb_y"); } for_each_intel_plane_on_crtc(display->drm, crtc, plane) { From 39f4b29d9e7359d8a5a4d176f09623ad963d4d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:34 +0200 Subject: [PATCH 149/712] drm/i915/wm: Include .min_ddb_alloc_uv in the wm dumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We include the Y/RGB .min_ddb_alloc in the wm state change dumps. Do the same for .min_ddb_alloc_uv, on the platforms where it is used. Also adjust the whitespace in the other debug prints to keep the values for each wm level lined up across all the lines. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-10-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 30 +++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index e9cfbb7c9a77..d45b3bcc6ef0 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2624,7 +2624,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, struct intel_display *display = to_intel_display(plane); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] level %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm" + "[PLANE:%d:%s] level %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm" " -> %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm\n", plane->base.base.id, plane->base.name, enast(old_wm->wm[0].enable), enast(old_wm->wm[1].enable), @@ -2643,7 +2643,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, enast(new_wm->sagv.trans_wm.enable)); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] lines %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%4d" + "[PLANE:%d:%s] lines %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%4d" " -> %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%4d\n", plane->base.base.id, plane->base.name, enast(old_wm->wm[0].ignore_lines), old_wm->wm[0].lines, @@ -2670,7 +2670,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, enast(new_wm->sagv.trans_wm.ignore_lines), new_wm->sagv.trans_wm.lines); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] blocks %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" + "[PLANE:%d:%s] blocks %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d\n", plane->base.base.id, plane->base.name, old_wm->wm[0].blocks, old_wm->wm[1].blocks, @@ -2689,7 +2689,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, new_wm->sagv.trans_wm.blocks); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] min_ddb %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" + "[PLANE:%d:%s] min_ddb %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d\n", plane->base.base.id, plane->base.name, old_wm->wm[0].min_ddb_alloc, old_wm->wm[1].min_ddb_alloc, @@ -2706,6 +2706,28 @@ skl_print_plane_wm_changes(struct intel_plane *plane, new_wm->trans_wm.min_ddb_alloc, new_wm->sagv.wm0.min_ddb_alloc, new_wm->sagv.trans_wm.min_ddb_alloc); + + if (DISPLAY_VER(display) >= 11) + return; + + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] min_ddb_uv %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" + " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d\n", + plane->base.base.id, plane->base.name, + old_wm->wm[0].min_ddb_alloc_uv, old_wm->wm[1].min_ddb_alloc_uv, + old_wm->wm[2].min_ddb_alloc_uv, old_wm->wm[3].min_ddb_alloc_uv, + old_wm->wm[4].min_ddb_alloc_uv, old_wm->wm[5].min_ddb_alloc_uv, + old_wm->wm[6].min_ddb_alloc_uv, old_wm->wm[7].min_ddb_alloc_uv, + old_wm->trans_wm.min_ddb_alloc_uv, + old_wm->sagv.wm0.min_ddb_alloc_uv, + old_wm->sagv.trans_wm.min_ddb_alloc_uv, + new_wm->wm[0].min_ddb_alloc_uv, new_wm->wm[1].min_ddb_alloc_uv, + new_wm->wm[2].min_ddb_alloc_uv, new_wm->wm[3].min_ddb_alloc_uv, + new_wm->wm[4].min_ddb_alloc_uv, new_wm->wm[5].min_ddb_alloc_uv, + new_wm->wm[6].min_ddb_alloc_uv, new_wm->wm[7].min_ddb_alloc_uv, + new_wm->trans_wm.min_ddb_alloc_uv, + new_wm->sagv.wm0.min_ddb_alloc_uv, + new_wm->sagv.trans_wm.min_ddb_alloc_uv); } static void From 24869650dff34a6fc8fd1cc91b2058a72f9abc95 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 18:13:31 -0500 Subject: [PATCH 150/712] iio: adc: ti-adc161s626: fix buffer read on big-endian Rework ti_adc_trigger_handler() to properly handle data on big-endian architectures. The scan data format is 16-bit CPU-endian, so we can't cast it to a int * on big-endian and expect it to work. Instead, we introduce a local int variable to read the data into, and then copy it to the buffer. Since the buffer isn't passed to any SPI functions, we don't need it to be DMA-safe. So we can drop it from the driver data struct and just use stack memory for the scan data. Since there is only one data value (plus timestamp), we don't need an array and can just declare a struct with the correct data type instead. Also fix alignment of iio_get_time_ns() to ( while we are touching this. Fixes: 4d671b71beef ("iio: adc: ti-adc161s626: add support for TI 1-channel differential ADCs") Signed-off-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc161s626.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 28aa6b80160c..42968d96572b 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -70,8 +70,6 @@ struct ti_adc_data { u8 read_size; u8 shift; - - u8 buffer[16] __aligned(IIO_DMA_MINALIGN); }; static int ti_adc_read_measurement(struct ti_adc_data *data, @@ -114,15 +112,20 @@ static irqreturn_t ti_adc_trigger_handler(int irq, void *private) struct iio_poll_func *pf = private; struct iio_dev *indio_dev = pf->indio_dev; struct ti_adc_data *data = iio_priv(indio_dev); - int ret; + struct { + s16 data; + aligned_s64 timestamp; + } scan = { }; + int ret, val; - ret = ti_adc_read_measurement(data, &indio_dev->channels[0], - (int *) &data->buffer); - if (!ret) - iio_push_to_buffers_with_timestamp(indio_dev, - data->buffer, - iio_get_time_ns(indio_dev)); + ret = ti_adc_read_measurement(data, &indio_dev->channels[0], &val); + if (ret) + goto exit_notify_done; + scan.data = val; + iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); + + exit_notify_done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; From 768461517a28d80fe81ea4d5d03a90cd184ea6ad Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 18:13:32 -0500 Subject: [PATCH 151/712] iio: adc: ti-adc161s626: use DMA-safe memory for spi_read() Add a DMA-safe buffer and use it for spi_read() instead of a stack memory. All SPI buffers must be DMA-safe. Since we only need up to 3 bytes, we just use a u8[] instead of __be16 and __be32 and change the conversion functions appropriately. Fixes: 4d671b71beef ("iio: adc: ti-adc161s626: add support for TI 1-channel differential ADCs") Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc161s626.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 42968d96572b..be1cc2e77862 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ struct ti_adc_data { u8 read_size; u8 shift; + u8 buf[3] __aligned(IIO_DMA_MINALIGN); }; static int ti_adc_read_measurement(struct ti_adc_data *data, @@ -78,26 +80,20 @@ static int ti_adc_read_measurement(struct ti_adc_data *data, int ret; switch (data->read_size) { - case 2: { - __be16 buf; - - ret = spi_read(data->spi, (void *) &buf, 2); + case 2: + ret = spi_read(data->spi, data->buf, 2); if (ret) return ret; - *val = be16_to_cpu(buf); + *val = get_unaligned_be16(data->buf); break; - } - case 3: { - __be32 buf; - - ret = spi_read(data->spi, (void *) &buf, 3); + case 3: + ret = spi_read(data->spi, data->buf, 3); if (ret) return ret; - *val = be32_to_cpu(buf) >> 8; + *val = get_unaligned_be24(data->buf); break; - } default: return -EINVAL; } From fdc7aa54a5d44c05880a4aad7cfb41aacfd16d7b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 17:18:10 -0500 Subject: [PATCH 152/712] iio: light: vcnl4035: fix scan buffer on big-endian Rework vcnl4035_trigger_consumer_handler() so that we are not passing what should be a u16 value as an int * to regmap_read(). This won't work on bit endian systems. Instead, add a new unsigned int variable to pass to regmap_read(). Then copy that value into the buffer struct. The buffer array is replaced with a struct since there is only one value being read. This allows us to use the correct u16 data type and has a side-effect of simplifying the alignment specification. Also fix the endianness of the scan format from little-endian to CPU endianness. Since we are using regmap to read the value, it will be CPU-endian. Fixes: 55707294c4eb ("iio: light: Add support for vishay vcnl4035") Signed-off-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4035.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 963747927425..16aeb17067bc 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -103,17 +103,23 @@ static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct vcnl4035_data *data = iio_priv(indio_dev); /* Ensure naturally aligned timestamp */ - u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8) = { }; + struct { + u16 als_data; + aligned_s64 timestamp; + } buffer = { }; + unsigned int val; int ret; - ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer); + ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, &val); if (ret < 0) { dev_err(&data->client->dev, "Trigger consumer can't read from sensor.\n"); goto fail_read; } - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns(indio_dev)); + + buffer.als_data = val; + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, + iio_get_time_ns(indio_dev)); fail_read: iio_trigger_notify_done(indio_dev->trig); @@ -381,7 +387,7 @@ static const struct iio_chan_spec vcnl4035_channels[] = { .sign = 'u', .realbits = 16, .storagebits = 16, - .endianness = IIO_LE, + .endianness = IIO_CPU, }, }, { @@ -395,7 +401,7 @@ static const struct iio_chan_spec vcnl4035_channels[] = { .sign = 'u', .realbits = 16, .storagebits = 16, - .endianness = IIO_LE, + .endianness = IIO_CPU, }, }, }; From db08b1940f4beb25460b4a4e9da3446454f2e8fe Mon Sep 17 00:00:00 2001 From: Cheng-Yang Chou Date: Sat, 21 Mar 2026 18:54:58 +0800 Subject: [PATCH 153/712] sched_ext: Fix inconsistent NUMA node lookup in scx_select_cpu_dfl() In the WAKE_SYNC path of scx_select_cpu_dfl(), waker_node was computed with cpu_to_node(), while node (for prev_cpu) was computed with scx_cpu_node_if_enabled(). When scx_builtin_idle_per_node is disabled, idle_cpumask(waker_node) is called with a real node ID even though per-node idle tracking is disabled, resulting in undefined behavior. Fix by using scx_cpu_node_if_enabled() for waker_node as well, ensuring both variables are computed consistently. Fixes: 48849271e6611 ("sched_ext: idle: Per-node idle cpumasks") Cc: stable@vger.kernel.org # v6.15+ Signed-off-by: Cheng-Yang Chou Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/ext_idle.c b/kernel/sched/ext_idle.c index ba298ac3ce6c..0ae93cd64004 100644 --- a/kernel/sched/ext_idle.c +++ b/kernel/sched/ext_idle.c @@ -543,7 +543,7 @@ s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags, * piled up on it even if there is an idle core elsewhere on * the system. */ - waker_node = cpu_to_node(cpu); + waker_node = scx_cpu_node_if_enabled(cpu); if (!(current->flags & PF_EXITING) && cpu_rq(cpu)->scx.local_dsq.nr == 0 && (!(flags & SCX_PICK_IDLE_IN_NODE) || (waker_node == node)) && From c7f27a8ab9f2f43570f0725256597a0d7abe2c5b Mon Sep 17 00:00:00 2001 From: Song Liu Date: Sat, 21 Mar 2026 20:30:45 -0700 Subject: [PATCH 154/712] workqueue: Fix false positive stall reports On weakly ordered architectures (e.g., arm64), the lockless check in wq_watchdog_timer_fn() can observe a reordering between the worklist insertion and the last_progress_ts update. Specifically, the watchdog can see a non-empty worklist (from a list_add) while reading a stale last_progress_ts value, causing a false positive stall report. This was confirmed by reading pool->last_progress_ts again after holding pool->lock in wq_watchdog_timer_fn(): workqueue watchdog: pool 7 false positive detected! lockless_ts=4784580465 locked_ts=4785033728 diff=453263ms worklist_empty=0 To avoid slowing down the hot path (queue_work, etc.), recheck last_progress_ts with pool->lock held. This will eliminate the false positive with minimal overhead. Remove two extra empty lines in wq_watchdog_timer_fn() as we are on it. Fixes: 82607adcf9cd ("workqueue: implement lockup detector") Cc: stable@vger.kernel.org # v4.5+ Assisted-by: claude-code:claude-opus-4-6 Signed-off-by: Song Liu Signed-off-by: Tejun Heo --- kernel/workqueue.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b77119d71641..ff97b705f25e 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -7699,8 +7699,28 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) else ts = touched; - /* did we stall? */ + /* + * Did we stall? + * + * Do a lockless check first. On weakly ordered + * architectures, the lockless check can observe a + * reordering between worklist insert_work() and + * last_progress_ts update from __queue_work(). Since + * __queue_work() is a much hotter path than the timer + * function, we handle false positive here by reading + * last_progress_ts again with pool->lock held. + */ if (time_after(now, ts + thresh)) { + scoped_guard(raw_spinlock_irqsave, &pool->lock) { + pool_ts = pool->last_progress_ts; + if (time_after(pool_ts, touched)) + ts = pool_ts; + else + ts = touched; + } + if (!time_after(now, ts + thresh)) + continue; + lockup_detected = true; stall_time = jiffies_to_msecs(now - pool_ts) / 1000; max_stall_time = max(max_stall_time, stall_time); @@ -7712,8 +7732,6 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) pr_cont_pool_info(pool); pr_cont(" stuck for %us!\n", stall_time); } - - } if (lockup_detected) From 67c3f99bed6f422ba343d2b70a2eeeccdfd91bef Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Fri, 30 Jan 2026 14:23:52 +0200 Subject: [PATCH 155/712] counter: rz-mtu3-cnt: prevent counter from being toggled multiple times Runtime PM counter is incremented / decremented each time the sysfs enable file is written to. If user writes 0 to the sysfs enable file multiple times, runtime PM usage count underflows, generating the following message. rz-mtu3-counter rz-mtu3-counter.0: Runtime PM usage count underflow! At the same time, hardware registers end up being accessed with clocks off in rz_mtu3_terminate_counter() to disable an already disabled channel. If user writes 1 to the sysfs enable file multiple times, runtime PM usage count will be incremented each time, requiring the same number of 0 writes to get it back to 0. If user writes 0 to the sysfs enable file while PWM is in progress, PWM is stopped without counter being the owner of the underlying MTU3 channel. Check against the cached count_is_enabled value and exit if the user is trying to set the same enable value. Cc: stable@vger.kernel.org Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver") Signed-off-by: Cosmin Tanislav Link: https://lore.kernel.org/r/20260130122353.2263273-5-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: William Breathitt Gray --- drivers/counter/rz-mtu3-cnt.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c index e755d54dfece..a4a8ef2d88f0 100644 --- a/drivers/counter/rz-mtu3-cnt.c +++ b/drivers/counter/rz-mtu3-cnt.c @@ -499,21 +499,25 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter, struct rz_mtu3_cnt *const priv = counter_priv(counter); int ret = 0; + mutex_lock(&priv->lock); + + if (priv->count_is_enabled[count->id] == enable) + goto exit; + if (enable) { - mutex_lock(&priv->lock); pm_runtime_get_sync(ch->dev); ret = rz_mtu3_initialize_counter(counter, count->id); if (ret == 0) priv->count_is_enabled[count->id] = true; - mutex_unlock(&priv->lock); } else { - mutex_lock(&priv->lock); rz_mtu3_terminate_counter(counter, count->id); priv->count_is_enabled[count->id] = false; pm_runtime_put(ch->dev); - mutex_unlock(&priv->lock); } +exit: + mutex_unlock(&priv->lock); + return ret; } From 2932095c114b98cbb40ccf34fc00d613cb17cead Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Fri, 30 Jan 2026 14:23:53 +0200 Subject: [PATCH 156/712] counter: rz-mtu3-cnt: do not use struct rz_mtu3_channel's dev member The counter driver can use HW channels 1 and 2, while the PWM driver can use HW channels 0, 1, 2, 3, 4, 6, 7. The dev member is assigned both by the counter driver and the PWM driver for channels 1 and 2, to their own struct device instance, overwriting the previous value. The sub-drivers race to assign their own struct device pointer to the same struct rz_mtu3_channel's dev member. The dev member of struct rz_mtu3_channel is used by the counter sub-driver for runtime PM. Depending on the probe order of the counter and PWM sub-drivers, the dev member may point to the wrong struct device instance, causing the counter sub-driver to do runtime PM actions on the wrong device. To fix this, use the parent pointer of the counter, which is assigned during probe to the correct struct device, not the struct device pointer inside the shared struct rz_mtu3_channel. Cc: stable@vger.kernel.org Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver") Signed-off-by: Cosmin Tanislav Link: https://lore.kernel.org/r/20260130122353.2263273-6-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: William Breathitt Gray --- drivers/counter/rz-mtu3-cnt.c | 55 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c index a4a8ef2d88f0..7bfb6979193c 100644 --- a/drivers/counter/rz-mtu3-cnt.c +++ b/drivers/counter/rz-mtu3-cnt.c @@ -107,9 +107,9 @@ static bool rz_mtu3_is_counter_invalid(struct counter_device *counter, int id) struct rz_mtu3_cnt *const priv = counter_priv(counter); unsigned long tmdr; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); if (id == RZ_MTU3_32_BIT_CH && test_bit(RZ_MTU3_TMDR3_LWA, &tmdr)) return false; @@ -165,12 +165,12 @@ static int rz_mtu3_count_read(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id == RZ_MTU3_32_BIT_CH) *val = rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW); else *val = rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -187,26 +187,26 @@ static int rz_mtu3_count_write(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id == RZ_MTU3_32_BIT_CH) rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, val); else rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, val); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; } static int rz_mtu3_count_function_read_helper(struct rz_mtu3_channel *const ch, - struct rz_mtu3_cnt *const priv, + struct counter_device *const counter, enum counter_function *function) { u8 timer_mode; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) { case RZ_MTU3_TMDR1_PH_CNT_MODE_1: @@ -240,7 +240,7 @@ static int rz_mtu3_count_function_read(struct counter_device *counter, if (ret) return ret; - ret = rz_mtu3_count_function_read_helper(ch, priv, function); + ret = rz_mtu3_count_function_read_helper(ch, counter, function); mutex_unlock(&priv->lock); return ret; @@ -279,9 +279,9 @@ static int rz_mtu3_count_function_write(struct counter_device *counter, return -EINVAL; } - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -300,9 +300,9 @@ static int rz_mtu3_count_direction_read(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); *direction = (tsr & RZ_MTU3_TSR_TCFD) ? COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; @@ -377,14 +377,14 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter, return -EINVAL; } - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id == RZ_MTU3_32_BIT_CH) rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling); else rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling); rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -495,7 +495,6 @@ static int rz_mtu3_count_enable_read(struct counter_device *counter, static int rz_mtu3_count_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); struct rz_mtu3_cnt *const priv = counter_priv(counter); int ret = 0; @@ -505,14 +504,14 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter, goto exit; if (enable) { - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); ret = rz_mtu3_initialize_counter(counter, count->id); if (ret == 0) priv->count_is_enabled[count->id] = true; } else { rz_mtu3_terminate_counter(counter, count->id); priv->count_is_enabled[count->id] = false; - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); } exit: @@ -544,9 +543,9 @@ static int rz_mtu3_cascade_counts_enable_get(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); *cascade_enable = test_bit(RZ_MTU3_TMDR3_LWA, &tmdr); mutex_unlock(&priv->lock); @@ -563,10 +562,10 @@ static int rz_mtu3_cascade_counts_enable_set(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_LWA, cascade_enable); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -583,9 +582,9 @@ static int rz_mtu3_ext_input_phase_clock_select_get(struct counter_device *count if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); *ext_input_phase_clock_select = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); mutex_unlock(&priv->lock); @@ -602,11 +601,11 @@ static int rz_mtu3_ext_input_phase_clock_select_set(struct counter_device *count if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_PHCKSEL, ext_input_phase_clock_select); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -644,7 +643,7 @@ static int rz_mtu3_action_read(struct counter_device *counter, if (ret) return ret; - ret = rz_mtu3_count_function_read_helper(ch, priv, &function); + ret = rz_mtu3_count_function_read_helper(ch, counter, &function); if (ret) { mutex_unlock(&priv->lock); return ret; From 4f51e6c0baae80e52bd013092e82a55678be31fc Mon Sep 17 00:00:00 2001 From: Valek Andrej Date: Fri, 13 Mar 2026 10:24:13 +0100 Subject: [PATCH 157/712] iio: accel: fix ADXL355 temperature signature value Temperature was wrongly represented as 12-bit signed, confirmed by checking the datasheet. Even if the temperature is negative, the value in the register stays unsigned. Fixes: 12ed27863ea3 iio: accel: Add driver support for ADXL355 Signed-off-by: Valek Andrej Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl355_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 1c1d64d5cbcb..8f90c58f4100 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -745,7 +745,7 @@ static const struct iio_chan_spec adxl355_channels[] = { BIT(IIO_CHAN_INFO_OFFSET), .scan_index = 3, .scan_type = { - .sign = 's', + .sign = 'u', .realbits = 12, .storagebits = 16, .endianness = IIO_BE, From c354521708175d776d896f8bdae44b18711eccb6 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 12 Mar 2026 13:20:24 +0200 Subject: [PATCH 158/712] iio: dac: ad5770r: fix error return in ad5770r_read_raw() Return the error code from regmap_bulk_read() instead of 0 so that I/O failures are properly propagated. Fixes: cbbb819837f6 ("iio: dac: ad5770r: Add AD5770R support") Signed-off-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5770r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index cd47cb1c685c..6027e8d88b27 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -322,7 +322,7 @@ static int ad5770r_read_raw(struct iio_dev *indio_dev, chan->address, st->transf_buf, 2); if (ret) - return 0; + return ret; buf16 = get_unaligned_le16(st->transf_buf); *val = buf16 >> 2; From 9d3fa23d5d55a137fd4396d3d4799102587a7f2b Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 12 Mar 2026 13:20:23 +0200 Subject: [PATCH 159/712] iio: accel: adxl313: add missing error check in predisable Check the return value of the FIFO bypass regmap_write() before proceeding to disable interrupts. Fixes: ff8093fa6ba4 ("iio: accel: adxl313: add buffered FIFO watermark with interrupt handling") Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl313_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index 9f5d4d2cb325..83dcac17a042 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -998,6 +998,8 @@ static int adxl313_buffer_predisable(struct iio_dev *indio_dev) ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL, FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, ADXL313_FIFO_BYPASS)); + if (ret) + return ret; ret = regmap_write(data->regmap, ADXL313_REG_INT_ENABLE, 0); if (ret) From 2452969ca1081fea6bd9ab7ad5e168a5d11f28ec Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 22 Feb 2026 17:45:39 +0800 Subject: [PATCH 160/712] iio: adc: nxp-sar-adc: Fix DMA channel leak in trigger mode The DMA channel was requested in nxp_sar_adc_buffer_postenable() but was only released in nxp_sar_adc_buffer_software_do_predisable(). This caused a DMA channel resource leak when operating in trigger mode. Fix this by moving dma_request_chan() from nxp_sar_adc_buffer_postenable() into nxp_sar_adc_buffer_software_do_postenable(), ensuring the DMA channel is only requested in software mode. Fixes: 4434072a893e ("iio: adc: Add the NXP SAR ADC support for the s32g2/3 platforms") Signed-off-by: Felix Gu Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 9efa883c277d..58103bf16aff 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -718,6 +718,10 @@ static int nxp_sar_adc_buffer_software_do_postenable(struct iio_dev *indio_dev) struct nxp_sar_adc *info = iio_priv(indio_dev); int ret; + info->dma_chan = dma_request_chan(indio_dev->dev.parent, "rx"); + if (IS_ERR(info->dma_chan)) + return PTR_ERR(info->dma_chan); + nxp_sar_adc_dma_channels_enable(info, *indio_dev->active_scan_mask); nxp_sar_adc_dma_cfg(info, true); @@ -738,6 +742,7 @@ static int nxp_sar_adc_buffer_software_do_postenable(struct iio_dev *indio_dev) out_dma_channels_disable: nxp_sar_adc_dma_cfg(info, false); nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask); + dma_release_channel(info->dma_chan); return ret; } @@ -765,10 +770,6 @@ static int nxp_sar_adc_buffer_postenable(struct iio_dev *indio_dev) unsigned long channel; int ret; - info->dma_chan = dma_request_chan(indio_dev->dev.parent, "rx"); - if (IS_ERR(info->dma_chan)) - return PTR_ERR(info->dma_chan); - info->channels_used = 0; /* From fb69d0076e687421188bc8103ab0e8e5825b1df1 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 20 Mar 2026 11:29:00 +0200 Subject: [PATCH 161/712] drm/i915/dp_tunnel: Fix error handling when clearing stream BW in atomic state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clearing the DP tunnel stream BW in the atomic state involves getting the tunnel group state, which can fail. Handle the error accordingly. This fixes at least one issue where drm_dp_tunnel_atomic_set_stream_bw() failed to get the tunnel group state returning -EDEADLK, which wasn't handled. This lead to the ctx->contended warn later in modeset_lock() while taking a WW mutex for another object in the same atomic state, and thus within the same already contended WW context. Moving intel_crtc_state_alloc() later would avoid freeing saved_state on the error path; this stable patch leaves that simplification for a follow-up. Cc: Uma Shankar Cc: Ville Syrjälä Cc: # v6.9+ Fixes: a4efae87ecb2 ("drm/i915/dp: Compute DP tunnel BW during encoder state computation") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7617 Reviewed-by: Michał Grzelak Reviewed-by: Uma Shankar Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260320092900.13210-1-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 8 +++++++- .../gpu/drm/i915/display/intel_dp_tunnel.c | 20 +++++++++++++------ .../gpu/drm/i915/display/intel_dp_tunnel.h | 11 ++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index ee501009a251..882db77c0bbc 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4640,6 +4640,7 @@ intel_crtc_prepare_cleared_state(struct intel_atomic_state *state, struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_crtc_state *saved_state; + int err; saved_state = intel_crtc_state_alloc(crtc); if (!saved_state) @@ -4648,7 +4649,12 @@ intel_crtc_prepare_cleared_state(struct intel_atomic_state *state, /* free the old crtc_state->hw members */ intel_crtc_free_hw_state(crtc_state); - intel_dp_tunnel_atomic_clear_stream_bw(state, crtc_state); + err = intel_dp_tunnel_atomic_clear_stream_bw(state, crtc_state); + if (err) { + kfree(saved_state); + + return err; + } /* FIXME: before the switch to atomic started, a new pipe_config was * kzalloc'd. Code that depends on any field being zero should be diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index 1fd1ac8d556d..7363c9817297 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -659,19 +659,27 @@ int intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, * * Clear any DP tunnel stream BW requirement set by * intel_dp_tunnel_atomic_compute_stream_bw(). + * + * Returns 0 in case of success, a negative error code otherwise. */ -void intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, - struct intel_crtc_state *crtc_state) +int intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + int err; if (!crtc_state->dp_tunnel_ref.tunnel) - return; + return 0; + + err = drm_dp_tunnel_atomic_set_stream_bw(&state->base, + crtc_state->dp_tunnel_ref.tunnel, + crtc->pipe, 0); + if (err) + return err; - drm_dp_tunnel_atomic_set_stream_bw(&state->base, - crtc_state->dp_tunnel_ref.tunnel, - crtc->pipe, 0); drm_dp_tunnel_ref_put(&crtc_state->dp_tunnel_ref); + + return 0; } /** diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h index 7f0f720e8dca..10ab9eebcef6 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h @@ -40,8 +40,8 @@ int intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, struct intel_dp *intel_dp, const struct intel_connector *connector, struct intel_crtc_state *crtc_state); -void intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, - struct intel_crtc_state *crtc_state); +int intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state); int intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state, struct intel_crtc *crtc); @@ -88,9 +88,12 @@ intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, return 0; } -static inline void +static inline int intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, - struct intel_crtc_state *crtc_state) {} + struct intel_crtc_state *crtc_state) +{ + return 0; +} static inline int intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state, From 710abda58055ed5eaa8958107633cc12a365c328 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 18 Mar 2026 15:00:53 +0100 Subject: [PATCH 162/712] gpio: shared: call gpio_chip::of_xlate() if set OF-based GPIO controller drivers may provide a translation function that calculates the real chip offset from whatever devicetree sources provide. We need to take this into account in the shared GPIO management and call of_xlate() if it's provided and adjust the entry->offset we initially set when scanning the tree. To that end: modify the shared GPIO API to take the GPIO chip as argument on setup (to avoid having to rcu_dereference() it from the GPIO device) and protect the access to entry->offset with the existing lock. Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Reported-by: Jon Hunter Closes: https://lore.kernel.org/all/921ba8ce-b18e-4a99-966d-c763d22081e2@nvidia.com/ Reviewed-by: Linus Walleij Tested-by: Jon Hunter Acked-by: Jon Hunter Link: https://patch.msgid.link/20260318-gpio-shared-xlate-v2-1-0ce34c707e81@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 27 ++++++++++++++++++++++++++- drivers/gpio/gpiolib-shared.h | 4 ++-- drivers/gpio/gpiolib.c | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 17a7128b6bd9..3a8db9bf456d 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -506,8 +506,9 @@ static void gpio_shared_remove_adev(struct auxiliary_device *adev) auxiliary_device_uninit(adev); } -int gpio_device_setup_shared(struct gpio_device *gdev) +int gpiochip_setup_shared(struct gpio_chip *gc) { + struct gpio_device *gdev = gc->gpiodev; struct gpio_shared_entry *entry; struct gpio_shared_ref *ref; struct gpio_desc *desc; @@ -532,12 +533,34 @@ int gpio_device_setup_shared(struct gpio_device *gdev) * exposing shared pins. Find them and create the proxy devices. */ list_for_each_entry(entry, &gpio_shared_list, list) { + guard(mutex)(&entry->lock); + if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; if (list_count_nodes(&entry->refs) <= 1) continue; +#if IS_ENABLED(CONFIG_OF) + if (is_of_node(entry->fwnode) && gc->of_xlate) { + /* + * This is the earliest that we can tranlate the + * devicetree offset to the chip offset. + */ + struct of_phandle_args gpiospec = { }; + + gpiospec.np = to_of_node(entry->fwnode); + gpiospec.args_count = 2; + gpiospec.args[0] = entry->offset; + + ret = gc->of_xlate(gc, &gpiospec, NULL); + if (ret < 0) + return ret; + + entry->offset = ret; + } +#endif /* CONFIG_OF */ + desc = &gdev->descs[entry->offset]; __set_bit(GPIOD_FLAG_SHARED, &desc->flags); @@ -575,6 +598,8 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) struct gpio_shared_ref *ref; list_for_each_entry(entry, &gpio_shared_list, list) { + guard(mutex)(&entry->lock); + if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index 40568ef7364c..e11e260e1f59 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -14,14 +14,14 @@ struct device; #if IS_ENABLED(CONFIG_GPIO_SHARED) -int gpio_device_setup_shared(struct gpio_device *gdev); +int gpiochip_setup_shared(struct gpio_chip *gc); void gpio_device_teardown_shared(struct gpio_device *gdev); int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, unsigned long lflags); #else -static inline int gpio_device_setup_shared(struct gpio_device *gdev) +static inline int gpiochip_setup_shared(struct gpio_chip *gc) { return 0; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f52d2d3efac4..d82fcf3fb458 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1211,7 +1211,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_remove_irqchip_mask; - ret = gpio_device_setup_shared(gdev); + ret = gpiochip_setup_shared(gc); if (ret) goto err_remove_irqchip; From ec42a3a90ae9ae64b16d01a2e5d32ec0865ca8cf Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 18 Mar 2026 15:00:54 +0100 Subject: [PATCH 163/712] gpio: shared: handle pins shared by child nodes of devices Shared GPIOs may be assigned to child nodes of device nodes which don't themselves bind to any struct device. We need to pass the firmware node that is the actual consumer to gpiolib-shared and compare against it instead of unconditionally using the fwnode of the consumer device. Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Reported-by: Jon Hunter Closes: https://lore.kernel.org/all/921ba8ce-b18e-4a99-966d-c763d22081e2@nvidia.com/ Tested-by: Jon Hunter Acked-by: Jon Hunter Link: https://patch.msgid.link/20260318-gpio-shared-xlate-v2-2-0ce34c707e81@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 6 +++--- drivers/gpio/gpiolib-shared.h | 7 +++++-- drivers/gpio/gpiolib.c | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 3a8db9bf456d..e257212fa5e3 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -443,8 +443,8 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, } #endif /* CONFIG_RESET_GPIO */ -int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, - unsigned long lflags) +int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *fwnode, + const char *con_id, unsigned long lflags) { const char *dev_id = dev_name(consumer); struct gpiod_lookup_table *lookup; @@ -458,7 +458,7 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, if (!ref->fwnode && device_is_compatible(consumer, "reset-gpio")) { if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) continue; - } else if (!device_match_fwnode(consumer, ref->fwnode)) { + } else if (fwnode != ref->fwnode) { continue; } diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index e11e260e1f59..15e72a8dcdb1 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -11,13 +11,15 @@ struct gpio_device; struct gpio_desc; struct device; +struct fwnode_handle; #if IS_ENABLED(CONFIG_GPIO_SHARED) int gpiochip_setup_shared(struct gpio_chip *gc); void gpio_device_teardown_shared(struct gpio_device *gdev); -int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, - unsigned long lflags); +int gpio_shared_add_proxy_lookup(struct device *consumer, + struct fwnode_handle *fwnode, + const char *con_id, unsigned long lflags); #else @@ -29,6 +31,7 @@ static inline int gpiochip_setup_shared(struct gpio_chip *gc) static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { } static inline int gpio_shared_add_proxy_lookup(struct device *consumer, + struct fwnode_handle *fwnode, const char *con_id, unsigned long lflags) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d82fcf3fb458..300de30fd920 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4714,8 +4714,8 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, * lookup table for the proxy device as previously * we only knew the consumer's fwnode. */ - ret = gpio_shared_add_proxy_lookup(consumer, con_id, - lookupflags); + ret = gpio_shared_add_proxy_lookup(consumer, fwnode, + con_id, lookupflags); if (ret) return ERR_PTR(ret); From 8de4e0f44c638c66cdc5eeb4d5ab9acd61c31e4f Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 20 Mar 2026 22:56:38 +0800 Subject: [PATCH 164/712] gpio: qixis-fpga: Fix error handling for devm_regmap_init_mmio() devm_regmap_init_mmio() returns an ERR_PTR() on failure, not NULL. The original code checked for NULL which would never trigger on error, potentially leading to an invalid pointer dereference. Use IS_ERR() and PTR_ERR() to properly handle the error case. Fixes: e88500247dc3 ("gpio: add QIXIS FPGA GPIO controller") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260320-qixis-v1-1-a8efc22e8945@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-qixis-fpga.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-qixis-fpga.c b/drivers/gpio/gpio-qixis-fpga.c index 6e67f43ac0bd..3ced47db1521 100644 --- a/drivers/gpio/gpio-qixis-fpga.c +++ b/drivers/gpio/gpio-qixis-fpga.c @@ -60,8 +60,8 @@ static int qixis_cpld_gpio_probe(struct platform_device *pdev) return PTR_ERR(reg); regmap = devm_regmap_init_mmio(&pdev->dev, reg, ®map_config_8r_8v); - if (!regmap) - return -ENODEV; + if (IS_ERR(regmap)) + return PTR_ERR(regmap); /* In this case, the offset of our register is 0 inside the * regmap area that we just created. From f9f0b4a1f35d39a1a2a2f8ec46eb7b81efc70a63 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Thu, 19 Mar 2026 17:07:22 -0400 Subject: [PATCH 165/712] rust: interop: Add list module for C linked list interface Add a new module `kernel::interop::list` for working with C's doubly circular linked lists. Provide low-level iteration over list nodes. Typed iteration over actual items is provided with a `clist_create` macro to assist in creation of the `CList` type. Cc: Nikola Djukic Reviewed-by: Daniel Almeida Reviewed-by: Alexandre Courbot Reviewed-by: Alice Ryhl Acked-by: Alexandre Courbot Acked-by: Gary Guo Acked-by: Miguel Ojeda Signed-off-by: Joel Fernandes Link: https://patch.msgid.link/20260319210722.1543776-1-joelagnelf@nvidia.com [ * Remove stray empty comment and double blank line in doctest, * Improve wording and fix a few typos, * Use markdown emphasis instead of caps, * Move interop/mod.rs to interop.rs. - Danilo ] Signed-off-by: Danilo Krummrich --- MAINTAINERS | 9 + rust/helpers/helpers.c | 1 + rust/helpers/list.c | 17 ++ rust/kernel/interop.rs | 9 + rust/kernel/interop/list.rs | 339 ++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 6 files changed, 377 insertions(+) create mode 100644 rust/helpers/list.c create mode 100644 rust/kernel/interop.rs create mode 100644 rust/kernel/interop/list.rs diff --git a/MAINTAINERS b/MAINTAINERS index 2c0077bc5b0b..fa30c515d1d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23191,6 +23191,15 @@ T: git https://github.com/Rust-for-Linux/linux.git alloc-next F: rust/kernel/alloc.rs F: rust/kernel/alloc/ +RUST [INTEROP] +M: Joel Fernandes +M: Alexandre Courbot +L: rust-for-linux@vger.kernel.org +S: Maintained +T: git https://github.com/Rust-for-Linux/linux.git interop-next +F: rust/kernel/interop.rs +F: rust/kernel/interop/ + RUST [NUM] M: Alexandre Courbot R: Yury Norov diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a3c42e51f00a..724fcb8240ac 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -35,6 +35,7 @@ #include "io.c" #include "jump_label.c" #include "kunit.c" +#include "list.c" #include "maple_tree.c" #include "mm.c" #include "mutex.c" diff --git a/rust/helpers/list.c b/rust/helpers/list.c new file mode 100644 index 000000000000..18095a5593c5 --- /dev/null +++ b/rust/helpers/list.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Helpers for C circular doubly linked list implementation. + */ + +#include + +__rust_helper void rust_helper_INIT_LIST_HEAD(struct list_head *list) +{ + INIT_LIST_HEAD(list); +} + +__rust_helper void rust_helper_list_add_tail(struct list_head *new, struct list_head *head) +{ + list_add_tail(new, head); +} diff --git a/rust/kernel/interop.rs b/rust/kernel/interop.rs new file mode 100644 index 000000000000..3b371d782a59 --- /dev/null +++ b/rust/kernel/interop.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Infrastructure for interfacing Rust code with C kernel subsystems. +//! +//! This module is intended for low-level, unsafe Rust infrastructure code +//! that interoperates between Rust and C. It is *not* for use directly in +//! Rust drivers. + +pub mod list; diff --git a/rust/kernel/interop/list.rs b/rust/kernel/interop/list.rs new file mode 100644 index 000000000000..54265ea036bb --- /dev/null +++ b/rust/kernel/interop/list.rs @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust interface for C doubly circular intrusive linked lists. +//! +//! This module provides Rust abstractions for iterating over C `list_head`-based +//! linked lists. It should only be used for cases where C and Rust code share +//! direct access to the same linked list through a C interop interface. +//! +//! Note: This *must not* be used by Rust components that just need a linked list +//! primitive. Use [`kernel::list::List`] instead. +//! +//! # Examples +//! +//! ``` +//! use kernel::{ +//! bindings, +//! interop::list::clist_create, +//! types::Opaque, +//! }; +//! # // Create test list with values (0, 10, 20) - normally done by C code but it is +//! # // emulated here for doctests using the C bindings. +//! # use core::mem::MaybeUninit; +//! # +//! # /// C struct with embedded `list_head` (typically will be allocated by C code). +//! # #[repr(C)] +//! # pub struct SampleItemC { +//! # pub value: i32, +//! # pub link: bindings::list_head, +//! # } +//! # +//! # let mut head = MaybeUninit::::uninit(); +//! # +//! # let head = head.as_mut_ptr(); +//! # // SAFETY: `head` and all the items are test objects allocated in this scope. +//! # unsafe { bindings::INIT_LIST_HEAD(head) }; +//! # +//! # let mut items = [ +//! # MaybeUninit::::uninit(), +//! # MaybeUninit::::uninit(), +//! # MaybeUninit::::uninit(), +//! # ]; +//! # +//! # for (i, item) in items.iter_mut().enumerate() { +//! # let ptr = item.as_mut_ptr(); +//! # // SAFETY: `ptr` points to a valid `MaybeUninit`. +//! # unsafe { (*ptr).value = i as i32 * 10 }; +//! # // SAFETY: `&raw mut` creates a pointer valid for `INIT_LIST_HEAD`. +//! # unsafe { bindings::INIT_LIST_HEAD(&raw mut (*ptr).link) }; +//! # // SAFETY: `link` was just initialized and `head` is a valid list head. +//! # unsafe { bindings::list_add_tail(&mut (*ptr).link, head) }; +//! # } +//! +//! /// Rust wrapper for the C struct. +//! /// +//! /// The list item struct in this example is defined in C code as: +//! /// +//! /// ```c +//! /// struct SampleItemC { +//! /// int value; +//! /// struct list_head link; +//! /// }; +//! /// ``` +//! #[repr(transparent)] +//! pub struct Item(Opaque); +//! +//! impl Item { +//! pub fn value(&self) -> i32 { +//! // SAFETY: `Item` has the same layout as `SampleItemC`. +//! unsafe { (*self.0.get()).value } +//! } +//! } +//! +//! // Create typed [`CList`] from sentinel head. +//! // SAFETY: `head` is valid and initialized, items are `SampleItemC` with +//! // embedded `link` field, and `Item` is `#[repr(transparent)]` over `SampleItemC`. +//! let list = unsafe { clist_create!(head, Item, SampleItemC, link) }; +//! +//! // Iterate directly over typed items. +//! let mut found_0 = false; +//! let mut found_10 = false; +//! let mut found_20 = false; +//! +//! for item in list.iter() { +//! let val = item.value(); +//! if val == 0 { found_0 = true; } +//! if val == 10 { found_10 = true; } +//! if val == 20 { found_20 = true; } +//! } +//! +//! assert!(found_0 && found_10 && found_20); +//! ``` + +use core::{ + iter::FusedIterator, + marker::PhantomData, // +}; + +use crate::{ + bindings, + types::Opaque, // +}; + +use pin_init::{ + pin_data, + pin_init, + PinInit, // +}; + +/// FFI wrapper for a C `list_head` object used in intrusive linked lists. +/// +/// # Invariants +/// +/// - The underlying `list_head` is initialized with valid non-`NULL` `next`/`prev` pointers. +#[pin_data] +#[repr(transparent)] +pub struct CListHead { + #[pin] + inner: Opaque, +} + +impl CListHead { + /// Create a `&CListHead` reference from a raw `list_head` pointer. + /// + /// # Safety + /// + /// - `ptr` must be a valid pointer to an initialized `list_head` (e.g. via + /// `INIT_LIST_HEAD()`), with valid non-`NULL` `next`/`prev` pointers. + /// - `ptr` must remain valid for the lifetime `'a`. + /// - The list and all linked `list_head` nodes must not be modified from + /// anywhere for the lifetime `'a`, unless done so via any [`CListHead`] APIs. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self { + // SAFETY: + // - `CListHead` has the same layout as `list_head`. + // - `ptr` is valid and unmodified for `'a` per caller guarantees. + unsafe { &*ptr.cast() } + } + + /// Get the raw `list_head` pointer. + #[inline] + pub fn as_raw(&self) -> *mut bindings::list_head { + self.inner.get() + } + + /// Get the next [`CListHead`] in the list. + #[inline] + pub fn next(&self) -> &Self { + let raw = self.as_raw(); + // SAFETY: + // - `self.as_raw()` is valid and initialized per type invariants. + // - The `next` pointer is valid and non-`NULL` per type invariants + // (initialized via `INIT_LIST_HEAD()` or equivalent). + unsafe { Self::from_raw((*raw).next) } + } + + /// Check if this node is linked in a list (not isolated). + #[inline] + pub fn is_linked(&self) -> bool { + let raw = self.as_raw(); + // SAFETY: `self.as_raw()` is valid per type invariants. + unsafe { (*raw).next != raw && (*raw).prev != raw } + } + + /// Returns a pin-initializer for the list head. + pub fn new() -> impl PinInit { + pin_init!(Self { + // SAFETY: `INIT_LIST_HEAD` initializes `slot` to a valid empty list. + inner <- Opaque::ffi_init(|slot| unsafe { bindings::INIT_LIST_HEAD(slot) }), + }) + } +} + +// SAFETY: `list_head` contains no thread-bound state; it only holds +// `next`/`prev` pointers. +unsafe impl Send for CListHead {} + +// SAFETY: `CListHead` can be shared among threads as modifications are +// not allowed at the moment. +unsafe impl Sync for CListHead {} + +impl PartialEq for CListHead { + #[inline] + fn eq(&self, other: &Self) -> bool { + core::ptr::eq(self, other) + } +} + +impl Eq for CListHead {} + +/// Low-level iterator over `list_head` nodes. +/// +/// An iterator used to iterate over a C intrusive linked list (`list_head`). The caller has to +/// perform conversion of returned [`CListHead`] to an item (using [`container_of`] or similar). +/// +/// # Invariants +/// +/// `current` and `sentinel` are valid references into an initialized linked list. +struct CListHeadIter<'a> { + /// Current position in the list. + current: &'a CListHead, + /// The sentinel head (used to detect end of iteration). + sentinel: &'a CListHead, +} + +impl<'a> Iterator for CListHeadIter<'a> { + type Item = &'a CListHead; + + #[inline] + fn next(&mut self) -> Option { + // Check if we've reached the sentinel (end of list). + if self.current == self.sentinel { + return None; + } + + let item = self.current; + self.current = item.next(); + Some(item) + } +} + +impl<'a> FusedIterator for CListHeadIter<'a> {} + +/// A typed C linked list with a sentinel head intended for FFI use-cases where +/// a C subsystem manages a linked list that Rust code needs to read. Generally +/// required only for special cases. +/// +/// A sentinel head [`CListHead`] represents the entire linked list and can be used +/// for iteration over items of type `T`; it is not associated with a specific item. +/// +/// The const generic `OFFSET` specifies the byte offset of the `list_head` field within +/// the struct that `T` wraps. +/// +/// # Invariants +/// +/// - The sentinel [`CListHead`] has valid non-`NULL` `next`/`prev` pointers. +/// - `OFFSET` is the byte offset of the `list_head` field within the struct that `T` wraps. +/// - All the list's `list_head` nodes have valid non-`NULL` `next`/`prev` pointers. +#[repr(transparent)] +pub struct CList(CListHead, PhantomData); + +impl CList { + /// Create a typed [`CList`] reference from a raw sentinel `list_head` pointer. + /// + /// # Safety + /// + /// - `ptr` must be a valid pointer to an initialized sentinel `list_head` (e.g. via + /// `INIT_LIST_HEAD()`), with valid non-`NULL` `next`/`prev` pointers. + /// - `ptr` must remain valid for the lifetime `'a`. + /// - The list and all linked nodes must not be concurrently modified for the lifetime `'a`. + /// - The list must contain items where the `list_head` field is at byte offset `OFFSET`. + /// - `T` must be `#[repr(transparent)]` over the C struct. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self { + // SAFETY: + // - `CList` has the same layout as `CListHead` due to `#[repr(transparent)]`. + // - Caller guarantees `ptr` is a valid, sentinel `list_head` object. + unsafe { &*ptr.cast() } + } + + /// Check if the list is empty. + #[inline] + pub fn is_empty(&self) -> bool { + !self.0.is_linked() + } + + /// Create an iterator over typed items. + #[inline] + pub fn iter(&self) -> CListIter<'_, T, OFFSET> { + let head = &self.0; + CListIter { + head_iter: CListHeadIter { + current: head.next(), + sentinel: head, + }, + _phantom: PhantomData, + } + } +} + +/// High-level iterator over typed list items. +pub struct CListIter<'a, T, const OFFSET: usize> { + head_iter: CListHeadIter<'a>, + _phantom: PhantomData<&'a T>, +} + +impl<'a, T, const OFFSET: usize> Iterator for CListIter<'a, T, OFFSET> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option { + let head = self.head_iter.next()?; + + // Convert to item using `OFFSET`. + // + // SAFETY: The pointer calculation is valid because `OFFSET` is derived + // from `offset_of!` per type invariants. + Some(unsafe { &*head.as_raw().byte_sub(OFFSET).cast::() }) + } +} + +impl<'a, T, const OFFSET: usize> FusedIterator for CListIter<'a, T, OFFSET> {} + +/// Create a C doubly-circular linked list interface [`CList`] from a raw `list_head` pointer. +/// +/// This macro creates a `CList` that can iterate over items of type `$rust_type` +/// linked via the `$field` field in the underlying C struct `$c_type`. +/// +/// # Arguments +/// +/// - `$head`: Raw pointer to the sentinel `list_head` object (`*mut bindings::list_head`). +/// - `$rust_type`: Each item's Rust wrapper type. +/// - `$c_type`: Each item's C struct type that contains the embedded `list_head`. +/// - `$field`: The name of the `list_head` field within the C struct. +/// +/// # Safety +/// +/// The caller must ensure: +/// +/// - `$head` is a valid, initialized sentinel `list_head` (e.g. via `INIT_LIST_HEAD()`) +/// pointing to a list that is not concurrently modified for the lifetime of the [`CList`]. +/// - The list contains items of type `$c_type` linked via an embedded `$field`. +/// - `$rust_type` is `#[repr(transparent)]` over `$c_type` or has compatible layout. +/// +/// # Examples +/// +/// Refer to the examples in the [`crate::interop::list`] module documentation. +#[macro_export] +macro_rules! clist_create { + ($head:expr, $rust_type:ty, $c_type:ty, $($field:tt).+) => {{ + // Compile-time check that field path is a `list_head`. + let _: fn(*const $c_type) -> *const $crate::bindings::list_head = + |p| &raw const (*p).$($field).+; + + // Calculate offset and create `CList`. + const OFFSET: usize = ::core::mem::offset_of!($c_type, $($field).+); + $crate::interop::list::CList::<$rust_type, OFFSET>::from_raw($head) + }}; +} +pub use clist_create; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 34b924819288..b8eb0d217f85 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -29,6 +29,7 @@ #![feature(lint_reasons)] // // Stable since Rust 1.82.0. +#![feature(offset_of_nested)] #![feature(raw_ref_op)] // // Stable since Rust 1.83.0. @@ -110,6 +111,7 @@ #[doc(hidden)] pub mod impl_flags; pub mod init; +pub mod interop; pub mod io; pub mod ioctl; pub mod iommu; From b9616d9721bf8a56d5038e85d2ebbe0ec9d56a94 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Fri, 20 Mar 2026 00:57:10 -0400 Subject: [PATCH 166/712] rust: gpu: Add GPU buddy allocator bindings Add safe Rust abstractions over the Linux kernel's GPU buddy allocator for physical memory management. The GPU buddy allocator implements a binary buddy system useful for GPU physical memory allocation. nova-core will use it for physical memory allocation. Cc: Nikola Djukic Signed-off-by: Joel Fernandes Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320045711.43494-2-joelagnelf@nvidia.com [ * Use doc-comments for GpuBuddyAllocMode methods and GpuBuddyGuard, * Fix comma splice in GpuBuddyParams::chunk_size doc-comment, * Remove redundant summary in GpuBuddy::new doc-comment, * Drop Rust helper for gpu_buddy_block_size(). - Danilo ] Signed-off-by: Danilo Krummrich --- MAINTAINERS | 6 + rust/bindings/bindings_helper.h | 11 + rust/helpers/gpu.c | 17 + rust/helpers/helpers.c | 1 + rust/kernel/gpu.rs | 6 + rust/kernel/gpu/buddy.rs | 614 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 7 files changed, 657 insertions(+) create mode 100644 rust/helpers/gpu.c create mode 100644 rust/kernel/gpu.rs create mode 100644 rust/kernel/gpu/buddy.rs diff --git a/MAINTAINERS b/MAINTAINERS index fa30c515d1d3..13069ef9857b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8505,7 +8505,10 @@ T: git https://gitlab.freedesktop.org/drm/rust/kernel.git F: drivers/gpu/drm/nova/ F: drivers/gpu/drm/tyr/ F: drivers/gpu/nova-core/ +F: rust/helpers/gpu.c F: rust/kernel/drm/ +F: rust/kernel/gpu.rs +F: rust/kernel/gpu/ DRM DRIVERS FOR ALLWINNER A10 M: Chen-Yu Tsai @@ -8926,6 +8929,9 @@ F: drivers/gpu/drm/drm_buddy.c F: drivers/gpu/tests/gpu_buddy_test.c F: include/drm/drm_buddy.h F: include/linux/gpu_buddy.h +F: rust/helpers/gpu.c +F: rust/kernel/gpu.rs +F: rust/kernel/gpu/ DRM AUTOMATED TESTING M: Helen Koike diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 083cc44aa952..dbb765a9fdbd 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -146,6 +147,16 @@ const vm_flags_t RUST_CONST_HELPER_VM_MIXEDMAP = VM_MIXEDMAP; const vm_flags_t RUST_CONST_HELPER_VM_HUGEPAGE = VM_HUGEPAGE; const vm_flags_t RUST_CONST_HELPER_VM_NOHUGEPAGE = VM_NOHUGEPAGE; +#if IS_ENABLED(CONFIG_GPU_BUDDY) +const unsigned long RUST_CONST_HELPER_GPU_BUDDY_RANGE_ALLOCATION = GPU_BUDDY_RANGE_ALLOCATION; +const unsigned long RUST_CONST_HELPER_GPU_BUDDY_TOPDOWN_ALLOCATION = GPU_BUDDY_TOPDOWN_ALLOCATION; +const unsigned long RUST_CONST_HELPER_GPU_BUDDY_CONTIGUOUS_ALLOCATION = + GPU_BUDDY_CONTIGUOUS_ALLOCATION; +const unsigned long RUST_CONST_HELPER_GPU_BUDDY_CLEAR_ALLOCATION = GPU_BUDDY_CLEAR_ALLOCATION; +const unsigned long RUST_CONST_HELPER_GPU_BUDDY_CLEARED = GPU_BUDDY_CLEARED; +const unsigned long RUST_CONST_HELPER_GPU_BUDDY_TRIM_DISABLE = GPU_BUDDY_TRIM_DISABLE; +#endif + #if IS_ENABLED(CONFIG_ANDROID_BINDER_IPC_RUST) #include "../../drivers/android/binder/rust_binder.h" #include "../../drivers/android/binder/rust_binder_events.h" diff --git a/rust/helpers/gpu.c b/rust/helpers/gpu.c new file mode 100644 index 000000000000..a25448d54d72 --- /dev/null +++ b/rust/helpers/gpu.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#ifdef CONFIG_GPU_BUDDY + +__rust_helper u64 rust_helper_gpu_buddy_block_offset(const struct gpu_buddy_block *block) +{ + return gpu_buddy_block_offset(block); +} + +__rust_helper unsigned int rust_helper_gpu_buddy_block_order(struct gpu_buddy_block *block) +{ + return gpu_buddy_block_order(block); +} + +#endif /* CONFIG_GPU_BUDDY */ diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 724fcb8240ac..a53929ce52a3 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -32,6 +32,7 @@ #include "err.c" #include "irq.c" #include "fs.c" +#include "gpu.c" #include "io.c" #include "jump_label.c" #include "kunit.c" diff --git a/rust/kernel/gpu.rs b/rust/kernel/gpu.rs new file mode 100644 index 000000000000..1dc5d0c8c09d --- /dev/null +++ b/rust/kernel/gpu.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! GPU subsystem abstractions. + +#[cfg(CONFIG_GPU_BUDDY = "y")] +pub mod buddy; diff --git a/rust/kernel/gpu/buddy.rs b/rust/kernel/gpu/buddy.rs new file mode 100644 index 000000000000..d502ada6ebbd --- /dev/null +++ b/rust/kernel/gpu/buddy.rs @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! GPU buddy allocator bindings. +//! +//! C header: [`include/linux/gpu_buddy.h`](srctree/include/linux/gpu_buddy.h) +//! +//! This module provides Rust abstractions over the Linux kernel's GPU buddy +//! allocator, which implements a binary buddy memory allocator. +//! +//! The buddy allocator manages a contiguous address space and allocates blocks +//! in power-of-two sizes, useful for GPU physical memory management. +//! +//! # Examples +//! +//! Create a buddy allocator and perform a basic range allocation: +//! +//! ``` +//! use kernel::{ +//! gpu::buddy::{ +//! GpuBuddy, +//! GpuBuddyAllocFlags, +//! GpuBuddyAllocMode, +//! GpuBuddyParams, // +//! }, +//! prelude::*, +//! ptr::Alignment, +//! sizes::*, // +//! }; +//! +//! // Create a 1GB buddy allocator with 4KB minimum chunk size. +//! let buddy = GpuBuddy::new(GpuBuddyParams { +//! base_offset: 0, +//! size: SZ_1G as u64, +//! chunk_size: Alignment::new::(), +//! })?; +//! +//! assert_eq!(buddy.size(), SZ_1G as u64); +//! assert_eq!(buddy.chunk_size(), Alignment::new::()); +//! let initial_free = buddy.avail(); +//! +//! // Allocate 16MB. Block lands at the top of the address range. +//! let allocated = KBox::pin_init( +//! buddy.alloc_blocks( +//! GpuBuddyAllocMode::Simple, +//! SZ_16M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlags::default(), +//! ), +//! GFP_KERNEL, +//! )?; +//! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64); +//! +//! let block = allocated.iter().next().expect("expected one block"); +//! assert_eq!(block.offset(), (SZ_1G - SZ_16M) as u64); +//! assert_eq!(block.order(), 12); // 2^12 pages = 16MB +//! assert_eq!(block.size(), SZ_16M as u64); +//! assert_eq!(allocated.iter().count(), 1); +//! +//! // Dropping the allocation returns the range to the buddy allocator. +//! drop(allocated); +//! assert_eq!(buddy.avail(), initial_free); +//! # Ok::<(), Error>(()) +//! ``` +//! +//! Top-down allocation allocates from the highest addresses: +//! +//! ``` +//! # use kernel::{ +//! # gpu::buddy::{GpuBuddy, GpuBuddyAllocMode, GpuBuddyAllocFlags, GpuBuddyParams}, +//! # prelude::*, +//! # ptr::Alignment, +//! # sizes::*, // +//! # }; +//! # let buddy = GpuBuddy::new(GpuBuddyParams { +//! # base_offset: 0, +//! # size: SZ_1G as u64, +//! # chunk_size: Alignment::new::(), +//! # })?; +//! # let initial_free = buddy.avail(); +//! let topdown = KBox::pin_init( +//! buddy.alloc_blocks( +//! GpuBuddyAllocMode::TopDown, +//! SZ_16M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlags::default(), +//! ), +//! GFP_KERNEL, +//! )?; +//! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64); +//! +//! let block = topdown.iter().next().expect("expected one block"); +//! assert_eq!(block.offset(), (SZ_1G - SZ_16M) as u64); +//! assert_eq!(block.order(), 12); +//! assert_eq!(block.size(), SZ_16M as u64); +//! +//! // Dropping the allocation returns the range to the buddy allocator. +//! drop(topdown); +//! assert_eq!(buddy.avail(), initial_free); +//! # Ok::<(), Error>(()) +//! ``` +//! +//! Non-contiguous allocation can fill fragmented memory by returning multiple +//! blocks: +//! +//! ``` +//! # use kernel::{ +//! # gpu::buddy::{ +//! # GpuBuddy, GpuBuddyAllocFlags, GpuBuddyAllocMode, GpuBuddyParams, +//! # }, +//! # prelude::*, +//! # ptr::Alignment, +//! # sizes::*, // +//! # }; +//! # let buddy = GpuBuddy::new(GpuBuddyParams { +//! # base_offset: 0, +//! # size: SZ_1G as u64, +//! # chunk_size: Alignment::new::(), +//! # })?; +//! # let initial_free = buddy.avail(); +//! // Create fragmentation by allocating 4MB blocks at [0,4M) and [8M,12M). +//! let frag1 = KBox::pin_init( +//! buddy.alloc_blocks( +//! GpuBuddyAllocMode::Range(0..SZ_4M as u64), +//! SZ_4M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlags::default(), +//! ), +//! GFP_KERNEL, +//! )?; +//! assert_eq!(buddy.avail(), initial_free - SZ_4M as u64); +//! +//! let frag2 = KBox::pin_init( +//! buddy.alloc_blocks( +//! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64), +//! SZ_4M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlags::default(), +//! ), +//! GFP_KERNEL, +//! )?; +//! assert_eq!(buddy.avail(), initial_free - SZ_8M as u64); +//! +//! // Allocate 8MB, this returns 2 blocks from the holes. +//! let fragmented = KBox::pin_init( +//! buddy.alloc_blocks( +//! GpuBuddyAllocMode::Range(0..SZ_16M as u64), +//! SZ_8M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlags::default(), +//! ), +//! GFP_KERNEL, +//! )?; +//! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64); +//! +//! let (mut count, mut total) = (0u32, 0u64); +//! for block in fragmented.iter() { +//! assert_eq!(block.size(), SZ_4M as u64); +//! total += block.size(); +//! count += 1; +//! } +//! assert_eq!(total, SZ_8M as u64); +//! assert_eq!(count, 2); +//! # Ok::<(), Error>(()) +//! ``` +//! +//! Contiguous allocation fails when only fragmented space is available: +//! +//! ``` +//! # use kernel::{ +//! # gpu::buddy::{ +//! # GpuBuddy, GpuBuddyAllocFlag, GpuBuddyAllocFlags, GpuBuddyAllocMode, GpuBuddyParams, +//! # }, +//! # prelude::*, +//! # ptr::Alignment, +//! # sizes::*, // +//! # }; +//! // Create a small 16MB buddy allocator with fragmented memory. +//! let small = GpuBuddy::new(GpuBuddyParams { +//! base_offset: 0, +//! size: SZ_16M as u64, +//! chunk_size: Alignment::new::(), +//! })?; +//! +//! let _hole1 = KBox::pin_init( +//! small.alloc_blocks( +//! GpuBuddyAllocMode::Range(0..SZ_4M as u64), +//! SZ_4M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlags::default(), +//! ), +//! GFP_KERNEL, +//! )?; +//! +//! let _hole2 = KBox::pin_init( +//! small.alloc_blocks( +//! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64), +//! SZ_4M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlags::default(), +//! ), +//! GFP_KERNEL, +//! )?; +//! +//! // 8MB contiguous should fail, only two non-contiguous 4MB holes exist. +//! let result = KBox::pin_init( +//! small.alloc_blocks( +//! GpuBuddyAllocMode::Simple, +//! SZ_8M as u64, +//! Alignment::new::(), +//! GpuBuddyAllocFlag::Contiguous, +//! ), +//! GFP_KERNEL, +//! ); +//! assert!(result.is_err()); +//! # Ok::<(), Error>(()) +//! ``` + +use core::ops::Range; + +use crate::{ + bindings, + clist_create, + error::to_result, + interop::list::CListHead, + new_mutex, + prelude::*, + ptr::Alignment, + sync::{ + lock::mutex::MutexGuard, + Arc, + Mutex, // + }, + types::Opaque, // +}; + +/// Allocation mode for the GPU buddy allocator. +/// +/// The mode determines the primary allocation strategy. Modes are mutually +/// exclusive: an allocation is either simple, range-constrained, or top-down. +/// +/// Orthogonal modifier flags (e.g., contiguous, clear) are specified separately +/// via [`GpuBuddyAllocFlags`]. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GpuBuddyAllocMode { + /// Simple allocation without constraints. + Simple, + /// Range-based allocation within the given address range. + Range(Range), + /// Allocate from top of address space downward. + TopDown, +} + +impl GpuBuddyAllocMode { + /// Returns the C flags corresponding to the allocation mode. + fn as_flags(&self) -> usize { + match self { + Self::Simple => 0, + Self::Range(_) => bindings::GPU_BUDDY_RANGE_ALLOCATION, + Self::TopDown => bindings::GPU_BUDDY_TOPDOWN_ALLOCATION, + } + } + + /// Extracts the range start/end, defaulting to `(0, 0)` for non-range modes. + fn range(&self) -> (u64, u64) { + match self { + Self::Range(range) => (range.start, range.end), + _ => (0, 0), + } + } +} + +crate::impl_flags!( + /// Modifier flags for GPU buddy allocation. + /// + /// These flags can be combined with any [`GpuBuddyAllocMode`] to control + /// additional allocation behavior. + #[derive(Clone, Copy, Default, PartialEq, Eq)] + pub struct GpuBuddyAllocFlags(usize); + + /// Individual modifier flag for GPU buddy allocation. + #[derive(Clone, Copy, PartialEq, Eq)] + pub enum GpuBuddyAllocFlag { + /// Allocate physically contiguous blocks. + Contiguous = bindings::GPU_BUDDY_CONTIGUOUS_ALLOCATION, + + /// Request allocation from cleared (zeroed) memory. + Clear = bindings::GPU_BUDDY_CLEAR_ALLOCATION, + + /// Disable trimming of partially used blocks. + TrimDisable = bindings::GPU_BUDDY_TRIM_DISABLE, + } +); + +/// Parameters for creating a GPU buddy allocator. +pub struct GpuBuddyParams { + /// Base offset (in bytes) where the managed memory region starts. + /// Allocations will be offset by this value. + pub base_offset: u64, + /// Total size (in bytes) of the address space managed by the allocator. + pub size: u64, + /// Minimum allocation unit / chunk size; must be >= 4KB. + pub chunk_size: Alignment, +} + +/// Inner structure holding the actual buddy allocator. +/// +/// # Synchronization +/// +/// The C `gpu_buddy` API requires synchronization (see `include/linux/gpu_buddy.h`). +/// Internal locking ensures all allocator and free operations are properly +/// synchronized, preventing races between concurrent allocations and the +/// freeing that occurs when [`AllocatedBlocks`] is dropped. +/// +/// # Invariants +/// +/// The inner [`Opaque`] contains an initialized buddy allocator. +#[pin_data(PinnedDrop)] +struct GpuBuddyInner { + #[pin] + inner: Opaque, + + // TODO: Replace `Mutex<()>` with `Mutex>` once `Mutex::new()` + // accepts `impl PinInit`. + #[pin] + lock: Mutex<()>, + /// Cached creation parameters (do not change after init). + params: GpuBuddyParams, +} + +impl GpuBuddyInner { + /// Create a pin-initializer for the buddy allocator. + fn new(params: GpuBuddyParams) -> impl PinInit { + let size = params.size; + let chunk_size = params.chunk_size; + + // INVARIANT: `gpu_buddy_init` returns 0 on success, at which point the + // `gpu_buddy` structure is initialized and ready for use with all + // `gpu_buddy_*` APIs. `try_pin_init!` only completes if all fields succeed, + // so the invariant holds when construction finishes. + try_pin_init!(Self { + inner <- Opaque::try_ffi_init(|ptr| { + // SAFETY: `ptr` points to valid uninitialized memory from the pin-init + // infrastructure. `gpu_buddy_init` will initialize the structure. + to_result(unsafe { + bindings::gpu_buddy_init(ptr, size, chunk_size.as_usize() as u64) + }) + }), + lock <- new_mutex!(()), + params, + }) + } + + /// Lock the mutex and return a guard for accessing the allocator. + fn lock(&self) -> GpuBuddyGuard<'_> { + GpuBuddyGuard { + inner: self, + _guard: self.lock.lock(), + } + } +} + +#[pinned_drop] +impl PinnedDrop for GpuBuddyInner { + fn drop(self: Pin<&mut Self>) { + let guard = self.lock(); + + // SAFETY: Per the type invariant, `inner` contains an initialized + // allocator. `guard` provides exclusive access. + unsafe { bindings::gpu_buddy_fini(guard.as_raw()) }; + } +} + +// SAFETY: `GpuBuddyInner` can be sent between threads. +unsafe impl Send for GpuBuddyInner {} + +// SAFETY: `GpuBuddyInner` is `Sync` because `GpuBuddyInner::lock` +// serializes all access to the C allocator, preventing data races. +unsafe impl Sync for GpuBuddyInner {} + +/// Guard that proves the lock is held, enabling access to the allocator. +/// +/// The `_guard` holds the lock for the duration of this guard's lifetime. +struct GpuBuddyGuard<'a> { + inner: &'a GpuBuddyInner, + _guard: MutexGuard<'a, ()>, +} + +impl GpuBuddyGuard<'_> { + /// Get a raw pointer to the underlying C `gpu_buddy` structure. + fn as_raw(&self) -> *mut bindings::gpu_buddy { + self.inner.inner.get() + } +} + +/// GPU buddy allocator instance. +/// +/// This structure wraps the C `gpu_buddy` allocator using reference counting. +/// The allocator is automatically cleaned up when all references are dropped. +/// +/// Refer to the module-level documentation for usage examples. +pub struct GpuBuddy(Arc); + +impl GpuBuddy { + /// Create a new buddy allocator. + /// + /// The allocator manages a contiguous address space of the given size, with the + /// specified minimum allocation unit (chunk_size must be at least 4KB). + pub fn new(params: GpuBuddyParams) -> Result { + Arc::pin_init(GpuBuddyInner::new(params), GFP_KERNEL).map(Self) + } + + /// Get the base offset for allocations. + pub fn base_offset(&self) -> u64 { + self.0.params.base_offset + } + + /// Get the chunk size (minimum allocation unit). + pub fn chunk_size(&self) -> Alignment { + self.0.params.chunk_size + } + + /// Get the total managed size. + pub fn size(&self) -> u64 { + self.0.params.size + } + + /// Get the available (free) memory in bytes. + pub fn avail(&self) -> u64 { + let guard = self.0.lock(); + + // SAFETY: Per the type invariant, `inner` contains an initialized allocator. + // `guard` provides exclusive access. + unsafe { (*guard.as_raw()).avail } + } + + /// Allocate blocks from the buddy allocator. + /// + /// Returns a pin-initializer for [`AllocatedBlocks`]. + pub fn alloc_blocks( + &self, + mode: GpuBuddyAllocMode, + size: u64, + min_block_size: Alignment, + flags: impl Into, + ) -> impl PinInit { + let buddy_arc = Arc::clone(&self.0); + let (start, end) = mode.range(); + let mode_flags = mode.as_flags(); + let modifier_flags = flags.into(); + + // Create pin-initializer that initializes list and allocates blocks. + try_pin_init!(AllocatedBlocks { + buddy: buddy_arc, + list <- CListHead::new(), + _: { + // Reject zero-sized or inverted ranges. + if let GpuBuddyAllocMode::Range(range) = &mode { + if range.is_empty() { + Err::<(), Error>(EINVAL)?; + } + } + + // Lock while allocating to serialize with concurrent frees. + let guard = buddy.lock(); + + // SAFETY: Per the type invariant, `inner` contains an initialized + // allocator. `guard` provides exclusive access. + to_result(unsafe { + bindings::gpu_buddy_alloc_blocks( + guard.as_raw(), + start, + end, + size, + min_block_size.as_usize() as u64, + list.as_raw(), + mode_flags | usize::from(modifier_flags), + ) + })? + } + }) + } +} + +/// Allocated blocks from the buddy allocator with automatic cleanup. +/// +/// This structure owns a list of allocated blocks and ensures they are +/// automatically freed when dropped. Use `iter()` to iterate over all +/// allocated blocks. +/// +/// # Invariants +/// +/// - `list` is an initialized, valid list head containing allocated blocks. +#[pin_data(PinnedDrop)] +pub struct AllocatedBlocks { + #[pin] + list: CListHead, + buddy: Arc, +} + +impl AllocatedBlocks { + /// Check if the block list is empty. + pub fn is_empty(&self) -> bool { + // An empty list head points to itself. + !self.list.is_linked() + } + + /// Iterate over allocated blocks. + /// + /// Returns an iterator yielding [`AllocatedBlock`] values. Each [`AllocatedBlock`] + /// borrows `self` and is only valid for the duration of that borrow. + pub fn iter(&self) -> impl Iterator> + '_ { + let head = self.list.as_raw(); + // SAFETY: Per the type invariant, `list` is an initialized sentinel `list_head` + // and is not concurrently modified (we hold a `&self` borrow). The list contains + // `gpu_buddy_block` items linked via `__bindgen_anon_1.link`. `Block` is + // `#[repr(transparent)]` over `gpu_buddy_block`. + let clist = unsafe { + clist_create!( + head, + Block, + bindings::gpu_buddy_block, + __bindgen_anon_1.link + ) + }; + + clist + .iter() + .map(|this| AllocatedBlock { this, blocks: self }) + } +} + +#[pinned_drop] +impl PinnedDrop for AllocatedBlocks { + fn drop(self: Pin<&mut Self>) { + let guard = self.buddy.lock(); + + // SAFETY: + // - list is valid per the type's invariants. + // - guard provides exclusive access to the allocator. + unsafe { + bindings::gpu_buddy_free_list(guard.as_raw(), self.list.as_raw(), 0); + } + } +} + +/// A GPU buddy block. +/// +/// Transparent wrapper over C `gpu_buddy_block` structure. This type is returned +/// as references during iteration over [`AllocatedBlocks`]. +/// +/// # Invariants +/// +/// The inner [`Opaque`] contains a valid, allocated `gpu_buddy_block`. +#[repr(transparent)] +struct Block(Opaque); + +impl Block { + /// Get a raw pointer to the underlying C block. + fn as_raw(&self) -> *mut bindings::gpu_buddy_block { + self.0.get() + } + + /// Get the block's raw offset in the buddy address space (without base offset). + fn offset(&self) -> u64 { + // SAFETY: `self.as_raw()` is valid per the type's invariants. + unsafe { bindings::gpu_buddy_block_offset(self.as_raw()) } + } + + /// Get the block order. + fn order(&self) -> u32 { + // SAFETY: `self.as_raw()` is valid per the type's invariants. + unsafe { bindings::gpu_buddy_block_order(self.as_raw()) } + } +} + +// SAFETY: `Block` is a wrapper around `gpu_buddy_block` which can be +// sent across threads safely. +unsafe impl Send for Block {} + +// SAFETY: `Block` is only accessed through shared references after +// allocation, and thus safe to access concurrently across threads. +unsafe impl Sync for Block {} + +/// A buddy block paired with its owning [`AllocatedBlocks`] context. +/// +/// Unlike a raw block, which only knows its offset within the buddy address +/// space, an [`AllocatedBlock`] also has access to the allocator's `base_offset` +/// and `chunk_size`, enabling it to compute absolute offsets and byte sizes. +/// +/// Returned by [`AllocatedBlocks::iter()`]. +pub struct AllocatedBlock<'a> { + this: &'a Block, + blocks: &'a AllocatedBlocks, +} + +impl AllocatedBlock<'_> { + /// Get the block's offset in the address space. + /// + /// Returns the absolute offset including the allocator's base offset. + /// This is the actual address to use for accessing the allocated memory. + pub fn offset(&self) -> u64 { + self.blocks.buddy.params.base_offset + self.this.offset() + } + + /// Get the block order (size = chunk_size << order). + pub fn order(&self) -> u32 { + self.this.order() + } + + /// Get the block's size in bytes. + pub fn size(&self) -> u64 { + (self.blocks.buddy.params.chunk_size.as_usize() as u64) << self.this.order() + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b8eb0d217f85..e0837ffc91bf 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -105,6 +105,8 @@ pub mod firmware; pub mod fmt; pub mod fs; +#[cfg(CONFIG_GPU_BUDDY = "y")] +pub mod gpu; #[cfg(CONFIG_I2C = "y")] pub mod i2c; pub mod id_pool; From 4b1948ef1d9802b61cff8ec1e212b18d0af152c1 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Fri, 20 Mar 2026 00:57:11 -0400 Subject: [PATCH 167/712] MAINTAINERS: gpu: buddy: Update reviewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Christian Koenig mentioned he'd like to step down from the reviewer role for the GPU buddy allocator. Joel Fernandes is stepping in as reviewer with agreement from Matthew Auld and Arun Pravin. Signed-off-by: Joel Fernandes Acked-by: Christian König Acked-by: Matthew Auld Link: https://patch.msgid.link/20260320045711.43494-3-joelagnelf@nvidia.com Signed-off-by: Danilo Krummrich --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 13069ef9857b..55bf6bf26107 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8920,7 +8920,7 @@ F: include/drm/ttm/ GPU BUDDY ALLOCATOR M: Matthew Auld M: Arun Pravin -R: Christian Koenig +R: Joel Fernandes L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git From 7ea1a61129b26709fe85cf5d50da5c47458deb3a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 20 Mar 2026 20:45:36 +0100 Subject: [PATCH 168/712] rust: dma: use "kernel vertical" style for imports Convert all imports to use "kernel vertical" style. With this, subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index a396f8435739..2eea7e2f8f04 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -5,12 +5,20 @@ //! C header: [`include/linux/dma-mapping.h`](srctree/include/linux/dma-mapping.h) use crate::{ - bindings, build_assert, device, - device::{Bound, Core}, - error::{to_result, Result}, + bindings, + build_assert, + device::{ + self, + Bound, + Core, // + }, + error::to_result, prelude::*, sync::aref::ARef, - transmute::{AsBytes, FromBytes}, + transmute::{ + AsBytes, + FromBytes, // + }, // }; use core::ptr::NonNull; From d9aee73c56ee971b08173071ad93fa5ebf00a32e Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 20 Mar 2026 20:45:37 +0100 Subject: [PATCH 169/712] rust: dma: add generalized container for types other than slices Currently, `CoherentAllocation` is concecptually a DMA coherent container of a slice of `[T]` of runtime-checked length. Generalize it by creating `dma::Coherent` which can hold any value of `T`. `Coherent::alloc_with_attrs` is implemented but not yet exposed, as I believe we should not expose the way to obtain an uninitialized coherent region. `Coherent<[T]>` provides a `len` method instead of the previous `count()` method to be consistent with methods on slices. The existing type is re-defined as a type alias of `Coherent<[T]>` to ease transition. Methods in use are not yet removed. Signed-off-by: Gary Guo Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 4 +- rust/kernel/dma.rs | 377 ++++++++++++++++++++++++++---------------- 2 files changed, 234 insertions(+), 147 deletions(-) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 94e0548e7687..379058eca2ed 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -575,7 +575,7 @@ pub trait DeviceContext: private::Sealed {} /// The bound context indicates that for the entire duration of the lifetime of a [`Device`] /// reference, the [`Device`] is guaranteed to be bound to a driver. /// -/// Some APIs, such as [`dma::CoherentAllocation`] or [`Devres`] rely on the [`Device`] to be bound, +/// Some APIs, such as [`dma::Coherent`] or [`Devres`] rely on the [`Device`] to be bound, /// which can be proven with the [`Bound`] device context. /// /// Any abstraction that can guarantee a scope where the corresponding bus device is bound, should @@ -584,7 +584,7 @@ pub trait DeviceContext: private::Sealed {} /// /// [`Devres`]: kernel::devres::Devres /// [`Devres::access`]: kernel::devres::Devres::access -/// [`dma::CoherentAllocation`]: kernel::dma::CoherentAllocation +/// [`dma::Coherent`]: kernel::dma::Coherent pub struct Bound; mod private { diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 2eea7e2f8f04..ff3e147f1a23 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -6,7 +6,6 @@ use crate::{ bindings, - build_assert, device::{ self, Bound, @@ -14,6 +13,7 @@ }, error::to_result, prelude::*, + ptr::KnownSize, sync::aref::ARef, transmute::{ AsBytes, @@ -357,18 +357,17 @@ fn from(direction: DataDirection) -> Self { /// This is an abstraction around the `dma_alloc_coherent` API which is used to allocate and map /// large coherent DMA regions. /// -/// A [`CoherentAllocation`] instance contains a pointer to the allocated region (in the +/// A [`Coherent`] instance contains a pointer to the allocated region (in the /// processor's virtual address space) and the device address which can be given to the device -/// as the DMA address base of the region. The region is released once [`CoherentAllocation`] +/// as the DMA address base of the region. The region is released once [`Coherent`] /// is dropped. /// /// # Invariants /// -/// - For the lifetime of an instance of [`CoherentAllocation`], the `cpu_addr` is a valid pointer +/// - For the lifetime of an instance of [`Coherent`], the `cpu_addr` is a valid pointer /// to an allocated region of coherent memory and `dma_handle` is the DMA address base of the /// region. -/// - The size in bytes of the allocation is equal to `size_of:: * count`. -/// - `size_of:: * count` fits into a `usize`. +/// - The size in bytes of the allocation is equal to size information via pointer. // TODO // // DMA allocations potentially carry device resources (e.g.IOMMU mappings), hence for soundness @@ -379,16 +378,218 @@ fn from(direction: DataDirection) -> Self { // allocation from surviving device unbind; it would require RCU read side critical sections to // access the memory, which may require subsequent unnecessary copies. // -// Hence, find a way to revoke the device resources of a `CoherentAllocation`, but not the -// entire `CoherentAllocation` including the allocated memory itself. -pub struct CoherentAllocation { +// Hence, find a way to revoke the device resources of a `Coherent`, but not the +// entire `Coherent` including the allocated memory itself. +pub struct Coherent { dev: ARef, dma_handle: DmaAddress, - count: usize, cpu_addr: NonNull, dma_attrs: Attrs, } +impl Coherent { + /// Returns the size in bytes of this allocation. + #[inline] + pub fn size(&self) -> usize { + T::size(self.cpu_addr.as_ptr()) + } + + /// Returns the raw pointer to the allocated region in the CPU's virtual address space. + #[inline] + pub fn as_ptr(&self) -> *const T { + self.cpu_addr.as_ptr() + } + + /// Returns the raw pointer to the allocated region in the CPU's virtual address space as + /// a mutable pointer. + #[inline] + pub fn as_mut_ptr(&self) -> *mut T { + self.cpu_addr.as_ptr() + } + + /// Returns a DMA handle which may be given to the device as the DMA address base of + /// the region. + #[inline] + pub fn dma_handle(&self) -> DmaAddress { + self.dma_handle + } + + /// Returns a reference to the data in the region. + /// + /// # Safety + /// + /// * Callers must ensure that the device does not read/write to/from memory while the returned + /// slice is live. + /// * Callers must ensure that this call does not race with a write to the same region while + /// the returned slice is live. + #[inline] + pub unsafe fn as_ref(&self) -> &T { + // SAFETY: per safety requirement. + unsafe { &*self.as_ptr() } + } + + /// Returns a mutable reference to the data in the region. + /// + /// # Safety + /// + /// * Callers must ensure that the device does not read/write to/from memory while the returned + /// slice is live. + /// * Callers must ensure that this call does not race with a read or write to the same region + /// while the returned slice is live. + #[expect(clippy::mut_from_ref, reason = "unsafe to use API")] + #[inline] + pub unsafe fn as_mut(&self) -> &mut T { + // SAFETY: per safety requirement. + unsafe { &mut *self.as_mut_ptr() } + } + + /// Reads the value of `field` and ensures that its type is [`FromBytes`]. + /// + /// # Safety + /// + /// This must be called from the [`dma_read`] macro which ensures that the `field` pointer is + /// validated beforehand. + /// + /// Public but hidden since it should only be used from [`dma_read`] macro. + #[doc(hidden)] + pub unsafe fn field_read(&self, field: *const F) -> F { + // SAFETY: + // - By the safety requirements field is valid. + // - Using read_volatile() here is not sound as per the usual rules, the usage here is + // a special exception with the following notes in place. When dealing with a potential + // race from a hardware or code outside kernel (e.g. user-space program), we need that + // read on a valid memory is not UB. Currently read_volatile() is used for this, and the + // rationale behind is that it should generate the same code as READ_ONCE() which the + // kernel already relies on to avoid UB on data races. Note that the usage of + // read_volatile() is limited to this particular case, it cannot be used to prevent + // the UB caused by racing between two kernel functions nor do they provide atomicity. + unsafe { field.read_volatile() } + } + + /// Writes a value to `field` and ensures that its type is [`AsBytes`]. + /// + /// # Safety + /// + /// This must be called from the [`dma_write`] macro which ensures that the `field` pointer is + /// validated beforehand. + /// + /// Public but hidden since it should only be used from [`dma_write`] macro. + #[doc(hidden)] + pub unsafe fn field_write(&self, field: *mut F, val: F) { + // SAFETY: + // - By the safety requirements field is valid. + // - Using write_volatile() here is not sound as per the usual rules, the usage here is + // a special exception with the following notes in place. When dealing with a potential + // race from a hardware or code outside kernel (e.g. user-space program), we need that + // write on a valid memory is not UB. Currently write_volatile() is used for this, and the + // rationale behind is that it should generate the same code as WRITE_ONCE() which the + // kernel already relies on to avoid UB on data races. Note that the usage of + // write_volatile() is limited to this particular case, it cannot be used to prevent + // the UB caused by racing between two kernel functions nor do they provide atomicity. + unsafe { field.write_volatile(val) } + } +} + +impl Coherent { + /// Allocates a region of `T` of coherent memory. + #[expect(unused)] + fn alloc_with_attrs( + dev: &device::Device, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result { + const { + assert!( + core::mem::size_of::() > 0, + "It doesn't make sense for the allocated type to be a ZST" + ); + } + + let mut dma_handle = 0; + // SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`. + let addr = unsafe { + bindings::dma_alloc_attrs( + dev.as_raw(), + core::mem::size_of::(), + &mut dma_handle, + gfp_flags.as_raw(), + dma_attrs.as_raw(), + ) + }; + let cpu_addr = NonNull::new(addr.cast()).ok_or(ENOMEM)?; + // INVARIANT: + // - We just successfully allocated a coherent region which is adequately sized for `T`, + // hence the cpu address is valid. + // - We also hold a refcounted reference to the device. + Ok(Self { + dev: dev.into(), + dma_handle, + cpu_addr, + dma_attrs, + }) + } + + /// Allocates a region of `[T; len]` of coherent memory. + fn alloc_slice_with_attrs( + dev: &device::Device, + len: usize, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result> { + const { + assert!( + core::mem::size_of::() > 0, + "It doesn't make sense for the allocated type to be a ZST" + ); + } + + // `dma_alloc_attrs` cannot handle zero-length allocation, bail early. + if len == 0 { + Err(EINVAL)?; + } + + let size = core::mem::size_of::().checked_mul(len).ok_or(ENOMEM)?; + let mut dma_handle = 0; + // SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`. + let addr = unsafe { + bindings::dma_alloc_attrs( + dev.as_raw(), + size, + &mut dma_handle, + gfp_flags.as_raw(), + dma_attrs.as_raw(), + ) + }; + let cpu_addr = NonNull::slice_from_raw_parts(NonNull::new(addr.cast()).ok_or(ENOMEM)?, len); + // INVARIANT: + // - We just successfully allocated a coherent region which is adequately sized for + // `[T; len]`, hence the cpu address is valid. + // - We also hold a refcounted reference to the device. + Ok(Coherent { + dev: dev.into(), + dma_handle, + cpu_addr, + dma_attrs, + }) + } +} + +impl Coherent<[T]> { + /// Returns the number of elements `T` in this allocation. + /// + /// Note that this is not the size of the allocation in bytes, which is provided by + /// [`Self::size`]. + #[inline] + #[expect(clippy::len_without_is_empty, reason = "Coherent slice is never empty")] + pub fn len(&self) -> usize { + self.cpu_addr.len() + } +} + +// Type alias for compatibility. +#[doc(hidden)] +pub type CoherentAllocation = Coherent<[T]>; + impl CoherentAllocation { /// Allocates a region of `size_of:: * count` of coherent memory. /// @@ -409,39 +610,7 @@ pub fn alloc_attrs( gfp_flags: kernel::alloc::Flags, dma_attrs: Attrs, ) -> Result> { - build_assert!( - core::mem::size_of::() > 0, - "It doesn't make sense for the allocated type to be a ZST" - ); - - let size = count - .checked_mul(core::mem::size_of::()) - .ok_or(EOVERFLOW)?; - let mut dma_handle = 0; - // SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`. - let addr = unsafe { - bindings::dma_alloc_attrs( - dev.as_raw(), - size, - &mut dma_handle, - gfp_flags.as_raw(), - dma_attrs.as_raw(), - ) - }; - let addr = NonNull::new(addr).ok_or(ENOMEM)?; - // INVARIANT: - // - We just successfully allocated a coherent region which is accessible for - // `count` elements, hence the cpu address is valid. We also hold a refcounted reference - // to the device. - // - The allocated `size` is equal to `size_of:: * count`. - // - The allocated `size` fits into a `usize`. - Ok(Self { - dev: dev.into(), - dma_handle, - count, - cpu_addr: addr.cast(), - dma_attrs, - }) + Coherent::alloc_slice_with_attrs(dev, count, gfp_flags, dma_attrs) } /// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the @@ -454,49 +623,15 @@ pub fn alloc_coherent( CoherentAllocation::alloc_attrs(dev, count, gfp_flags, Attrs(0)) } - /// Returns the number of elements `T` in this allocation. - /// - /// Note that this is not the size of the allocation in bytes, which is provided by - /// [`Self::size`]. - pub fn count(&self) -> usize { - self.count - } - - /// Returns the size in bytes of this allocation. - pub fn size(&self) -> usize { - // INVARIANT: The type invariant of `Self` guarantees that `size_of:: * count` fits into - // a `usize`. - self.count * core::mem::size_of::() - } - - /// Returns the raw pointer to the allocated region in the CPU's virtual address space. - #[inline] - pub fn as_ptr(&self) -> *const [T] { - core::ptr::slice_from_raw_parts(self.cpu_addr.as_ptr(), self.count) - } - - /// Returns the raw pointer to the allocated region in the CPU's virtual address space as - /// a mutable pointer. - #[inline] - pub fn as_mut_ptr(&self) -> *mut [T] { - core::ptr::slice_from_raw_parts_mut(self.cpu_addr.as_ptr(), self.count) - } - /// Returns the base address to the allocated region in the CPU's virtual address space. pub fn start_ptr(&self) -> *const T { - self.cpu_addr.as_ptr() + self.as_ptr().cast() } /// Returns the base address to the allocated region in the CPU's virtual address space as /// a mutable pointer. pub fn start_ptr_mut(&mut self) -> *mut T { - self.cpu_addr.as_ptr() - } - - /// Returns a DMA handle which may be given to the device as the DMA address base of - /// the region. - pub fn dma_handle(&self) -> DmaAddress { - self.dma_handle + self.as_mut_ptr().cast() } /// Returns a DMA handle starting at `offset` (in units of `T`) which may be given to the @@ -504,11 +639,9 @@ pub fn dma_handle(&self) -> DmaAddress { /// /// Returns `EINVAL` if `offset` is not within the bounds of the allocation. pub fn dma_handle_with_offset(&self, offset: usize) -> Result { - if offset >= self.count { + if offset >= self.len() { Err(EINVAL) } else { - // INVARIANT: The type invariant of `Self` guarantees that `size_of:: * count` fits - // into a `usize`, and `offset` is inferior to `count`. Ok(self.dma_handle + (offset * core::mem::size_of::()) as DmaAddress) } } @@ -516,7 +649,7 @@ pub fn dma_handle_with_offset(&self, offset: usize) -> Result { /// Common helper to validate a range applied from the allocated region in the CPU's virtual /// address space. fn validate_range(&self, offset: usize, count: usize) -> Result { - if offset.checked_add(count).ok_or(EOVERFLOW)? > self.count { + if offset.checked_add(count).ok_or(EOVERFLOW)? > self.len() { return Err(EINVAL); } Ok(()) @@ -601,66 +734,20 @@ pub unsafe fn write(&mut self, src: &[T], offset: usize) -> Result { }; Ok(()) } - - /// Reads the value of `field` and ensures that its type is [`FromBytes`]. - /// - /// # Safety - /// - /// This must be called from the [`dma_read`] macro which ensures that the `field` pointer is - /// validated beforehand. - /// - /// Public but hidden since it should only be used from [`dma_read`] macro. - #[doc(hidden)] - pub unsafe fn field_read(&self, field: *const F) -> F { - // SAFETY: - // - By the safety requirements field is valid. - // - Using read_volatile() here is not sound as per the usual rules, the usage here is - // a special exception with the following notes in place. When dealing with a potential - // race from a hardware or code outside kernel (e.g. user-space program), we need that - // read on a valid memory is not UB. Currently read_volatile() is used for this, and the - // rationale behind is that it should generate the same code as READ_ONCE() which the - // kernel already relies on to avoid UB on data races. Note that the usage of - // read_volatile() is limited to this particular case, it cannot be used to prevent - // the UB caused by racing between two kernel functions nor do they provide atomicity. - unsafe { field.read_volatile() } - } - - /// Writes a value to `field` and ensures that its type is [`AsBytes`]. - /// - /// # Safety - /// - /// This must be called from the [`dma_write`] macro which ensures that the `field` pointer is - /// validated beforehand. - /// - /// Public but hidden since it should only be used from [`dma_write`] macro. - #[doc(hidden)] - pub unsafe fn field_write(&self, field: *mut F, val: F) { - // SAFETY: - // - By the safety requirements field is valid. - // - Using write_volatile() here is not sound as per the usual rules, the usage here is - // a special exception with the following notes in place. When dealing with a potential - // race from a hardware or code outside kernel (e.g. user-space program), we need that - // write on a valid memory is not UB. Currently write_volatile() is used for this, and the - // rationale behind is that it should generate the same code as WRITE_ONCE() which the - // kernel already relies on to avoid UB on data races. Note that the usage of - // write_volatile() is limited to this particular case, it cannot be used to prevent - // the UB caused by racing between two kernel functions nor do they provide atomicity. - unsafe { field.write_volatile(val) } - } } /// Note that the device configured to do DMA must be halted before this object is dropped. -impl Drop for CoherentAllocation { +impl Drop for Coherent { fn drop(&mut self) { - let size = self.count * core::mem::size_of::(); + let size = T::size(self.cpu_addr.as_ptr()); // SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`. // The cpu address, and the dma handle are valid due to the type invariants on - // `CoherentAllocation`. + // `Coherent`. unsafe { bindings::dma_free_attrs( self.dev.as_raw(), size, - self.start_ptr_mut().cast(), + self.cpu_addr.as_ptr().cast(), self.dma_handle, self.dma_attrs.as_raw(), ) @@ -668,20 +755,20 @@ fn drop(&mut self) { } } -// SAFETY: It is safe to send a `CoherentAllocation` to another thread if `T` +// SAFETY: It is safe to send a `Coherent` to another thread if `T` // can be sent to another thread. -unsafe impl Send for CoherentAllocation {} +unsafe impl Send for Coherent {} /// Reads a field of an item from an allocated region of structs. /// /// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating -/// to a [`CoherentAllocation`] and `proj` is a [projection specification](kernel::ptr::project!). +/// to a [`Coherent`] and `proj` is a [projection specification](kernel::ptr::project!). /// /// # Examples /// /// ``` /// use kernel::device::Device; -/// use kernel::dma::{attrs::*, CoherentAllocation}; +/// use kernel::dma::{attrs::*, Coherent}; /// /// struct MyStruct { field: u32, } /// @@ -690,7 +777,7 @@ unsafe impl Send for CoherentAllocation {} /// // SAFETY: Instances of `MyStruct` have no uninitialized portions. /// unsafe impl kernel::transmute::AsBytes for MyStruct{}; /// -/// # fn test(alloc: &kernel::dma::CoherentAllocation) -> Result { +/// # fn test(alloc: &kernel::dma::Coherent<[MyStruct]>) -> Result { /// let whole = kernel::dma_read!(alloc, [2]?); /// let field = kernel::dma_read!(alloc, [1]?.field); /// # Ok::<(), Error>(()) } @@ -700,17 +787,17 @@ macro_rules! dma_read { ($dma:expr, $($proj:tt)*) => {{ let dma = &$dma; let ptr = $crate::ptr::project!( - $crate::dma::CoherentAllocation::as_ptr(dma), $($proj)* + $crate::dma::Coherent::as_ptr(dma), $($proj)* ); // SAFETY: The pointer created by the projection is within the DMA region. - unsafe { $crate::dma::CoherentAllocation::field_read(dma, ptr) } + unsafe { $crate::dma::Coherent::field_read(dma, ptr) } }}; } /// Writes to a field of an item from an allocated region of structs. /// /// The syntax is of the form `kernel::dma_write!(dma, proj, val)` where `dma` is an expression -/// evaluating to a [`CoherentAllocation`], `proj` is a +/// evaluating to a [`Coherent`], `proj` is a /// [projection specification](kernel::ptr::project!), and `val` is the value to be written to the /// projected location. /// @@ -718,7 +805,7 @@ macro_rules! dma_read { /// /// ``` /// use kernel::device::Device; -/// use kernel::dma::{attrs::*, CoherentAllocation}; +/// use kernel::dma::{attrs::*, Coherent}; /// /// struct MyStruct { member: u32, } /// @@ -727,7 +814,7 @@ macro_rules! dma_read { /// // SAFETY: Instances of `MyStruct` have no uninitialized portions. /// unsafe impl kernel::transmute::AsBytes for MyStruct{}; /// -/// # fn test(alloc: &kernel::dma::CoherentAllocation) -> Result { +/// # fn test(alloc: &kernel::dma::Coherent<[MyStruct]>) -> Result { /// kernel::dma_write!(alloc, [2]?.member, 0xf); /// kernel::dma_write!(alloc, [1]?, MyStruct { member: 0xf }); /// # Ok::<(), Error>(()) } @@ -737,11 +824,11 @@ macro_rules! dma_write { (@parse [$dma:expr] [$($proj:tt)*] [, $val:expr]) => {{ let dma = &$dma; let ptr = $crate::ptr::project!( - mut $crate::dma::CoherentAllocation::as_mut_ptr(dma), $($proj)* + mut $crate::dma::Coherent::as_mut_ptr(dma), $($proj)* ); let val = $val; // SAFETY: The pointer created by the projection is within the DMA region. - unsafe { $crate::dma::CoherentAllocation::field_write(dma, ptr, val) } + unsafe { $crate::dma::Coherent::field_write(dma, ptr, val) } }}; (@parse [$dma:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) => { $crate::dma_write!(@parse [$dma] [$($proj)* .$field] [$($rest)*]) From f84ecffa3f745572164c1269f20eec2589d432c9 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 20 Mar 2026 20:45:38 +0100 Subject: [PATCH 170/712] rust: dma: add zeroed constructor to `Coherent` These constructors create a coherent container of a single object instead of slice. They are named `zeroed` and `zeroed_with_attrs` to emphasis that they are created initialized zeroed. It is intended that there'll be new constructors that take `PinInit` instead of zeroing. Signed-off-by: Gary Guo Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-4-dakr@kernel.org [ Use kernel import style. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 93 ++++++++++++++++++++++++++++++++++++---- samples/rust/rust_dma.rs | 13 ++++-- 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index ff3e147f1a23..59477c865dd3 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -47,7 +47,7 @@ pub trait Device: AsRef> { /// # Safety /// /// This method must not be called concurrently with any DMA allocation or mapping primitives, - /// such as [`CoherentAllocation::alloc_attrs`]. + /// such as [`Coherent::zeroed`]. unsafe fn dma_set_mask(&self, mask: DmaMask) -> Result { // SAFETY: // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. @@ -64,7 +64,7 @@ unsafe fn dma_set_mask(&self, mask: DmaMask) -> Result { /// # Safety /// /// This method must not be called concurrently with any DMA allocation or mapping primitives, - /// such as [`CoherentAllocation::alloc_attrs`]. + /// such as [`Coherent::zeroed`]. unsafe fn dma_set_coherent_mask(&self, mask: DmaMask) -> Result { // SAFETY: // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. @@ -83,7 +83,7 @@ unsafe fn dma_set_coherent_mask(&self, mask: DmaMask) -> Result { /// # Safety /// /// This method must not be called concurrently with any DMA allocation or mapping primitives, - /// such as [`CoherentAllocation::alloc_attrs`]. + /// such as [`Coherent::zeroed`]. unsafe fn dma_set_mask_and_coherent(&self, mask: DmaMask) -> Result { // SAFETY: // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. @@ -102,7 +102,7 @@ unsafe fn dma_set_mask_and_coherent(&self, mask: DmaMask) -> Result { /// # Safety /// /// This method must not be called concurrently with any DMA allocation or mapping primitives, - /// such as [`CoherentAllocation::alloc_attrs`]. + /// such as [`Coherent::zeroed`]. unsafe fn dma_set_max_seg_size(&self, size: u32) { // SAFETY: // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. @@ -202,12 +202,12 @@ pub const fn value(&self) -> u64 { /// /// ``` /// # use kernel::device::{Bound, Device}; -/// use kernel::dma::{attrs::*, CoherentAllocation}; +/// use kernel::dma::{attrs::*, Coherent}; /// /// # fn test(dev: &Device) -> Result { /// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN; -/// let c: CoherentAllocation = -/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, attribs)?; +/// let c: Coherent<[u64]> = +/// Coherent::zeroed_slice_with_attrs(dev, 4, GFP_KERNEL, attribs)?; /// # Ok::<(), Error>(()) } /// ``` #[derive(Clone, Copy, PartialEq)] @@ -492,7 +492,6 @@ pub unsafe fn field_write(&self, field: *mut F, val: F) { impl Coherent { /// Allocates a region of `T` of coherent memory. - #[expect(unused)] fn alloc_with_attrs( dev: &device::Device, gfp_flags: kernel::alloc::Flags, @@ -529,6 +528,41 @@ fn alloc_with_attrs( }) } + /// Allocates a region of type `T` of coherent memory. + /// + /// # Examples + /// + /// ``` + /// # use kernel::device::{ + /// # Bound, + /// # Device, + /// # }; + /// use kernel::dma::{ + /// attrs::*, + /// Coherent, + /// }; + /// + /// # fn test(dev: &Device) -> Result { + /// let c: Coherent<[u64; 4]> = + /// Coherent::zeroed_with_attrs(dev, GFP_KERNEL, DMA_ATTR_NO_WARN)?; + /// # Ok::<(), Error>(()) } + /// ``` + #[inline] + pub fn zeroed_with_attrs( + dev: &device::Device, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result { + Self::alloc_with_attrs(dev, gfp_flags | __GFP_ZERO, dma_attrs) + } + + /// Performs the same functionality as [`Coherent::zeroed_with_attrs`], except the + /// `dma_attrs` is 0 by default. + #[inline] + pub fn zeroed(dev: &device::Device, gfp_flags: kernel::alloc::Flags) -> Result { + Self::zeroed_with_attrs(dev, gfp_flags, Attrs(0)) + } + /// Allocates a region of `[T; len]` of coherent memory. fn alloc_slice_with_attrs( dev: &device::Device, @@ -572,6 +606,49 @@ fn alloc_slice_with_attrs( dma_attrs, }) } + + /// Allocates a zeroed region of type `T` of coherent memory. + /// + /// Unlike `Coherent::<[T; N]>::zeroed_with_attrs`, `Coherent::::zeroed_slices` support + /// a runtime length. + /// + /// # Examples + /// + /// ``` + /// # use kernel::device::{ + /// # Bound, + /// # Device, + /// # }; + /// use kernel::dma::{ + /// attrs::*, + /// Coherent, + /// }; + /// + /// # fn test(dev: &Device) -> Result { + /// let c: Coherent<[u64]> = + /// Coherent::zeroed_slice_with_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?; + /// # Ok::<(), Error>(()) } + /// ``` + #[inline] + pub fn zeroed_slice_with_attrs( + dev: &device::Device, + len: usize, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result> { + Coherent::alloc_slice_with_attrs(dev, len, gfp_flags | __GFP_ZERO, dma_attrs) + } + + /// Performs the same functionality as [`Coherent::zeroed_slice_with_attrs`], except the + /// `dma_attrs` is 0 by default. + #[inline] + pub fn zeroed_slice( + dev: &device::Device, + len: usize, + gfp_flags: kernel::alloc::Flags, + ) -> Result> { + Self::zeroed_slice_with_attrs(dev, len, gfp_flags, Attrs(0)) + } } impl Coherent<[T]> { diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index ce39b5545097..129bb4b39c04 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -6,7 +6,12 @@ use kernel::{ device::Core, - dma::{CoherentAllocation, DataDirection, Device, DmaMask}, + dma::{ + Coherent, + DataDirection, + Device, + DmaMask, // + }, page, pci, prelude::*, scatterlist::{Owned, SGTable}, @@ -16,7 +21,7 @@ #[pin_data(PinnedDrop)] struct DmaSampleDriver { pdev: ARef, - ca: CoherentAllocation, + ca: Coherent<[MyStruct]>, #[pin] sgt: SGTable>>, } @@ -64,8 +69,8 @@ fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit = - CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; + let ca: Coherent<[MyStruct]> = + Coherent::zeroed_slice(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; for (i, value) in TEST_VALUES.into_iter().enumerate() { kernel::dma_write!(ca, [i]?, MyStruct::new(value.0, value.1)); From 80f4a7b5138d1c427ee5626e8a796aa6b2994a95 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 20 Mar 2026 20:45:39 +0100 Subject: [PATCH 171/712] rust: dma: introduce dma::CoherentBox for memory initialization Currently, dma::Coherent cannot safely provide (mutable) access to its underlying memory because the memory might be concurrently accessed by a DMA device. This makes it difficult to safely initialize the memory before handing it over to the hardware. Introduce dma::CoherentBox, a type that encapsulates a dma::Coherent before its DMA address is exposed to the device. dma::CoherentBox can guarantee exclusive access to the inner dma::Coherent and implement Deref and DerefMut. Once the memory is properly initialized, dma::CoherentBox can be converted into a regular dma::Coherent. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-5-dakr@kernel.org [ Remove unnecessary trait bounds. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 154 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 59477c865dd3..5b41603cee2e 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -20,7 +20,13 @@ FromBytes, // }, // }; -use core::ptr::NonNull; +use core::{ + ops::{ + Deref, + DerefMut, // + }, + ptr::NonNull, // +}; /// DMA address type. /// @@ -352,6 +358,152 @@ fn from(direction: DataDirection) -> Self { } } +/// CPU-owned DMA allocation that can be converted into a device-shared [`Coherent`] object. +/// +/// Unlike [`Coherent`], a [`CoherentBox`] is guaranteed to be fully owned by the CPU -- its DMA +/// address is not exposed and it cannot be accessed by a device. This means it can safely be used +/// like a normal boxed allocation (e.g. direct reads, writes, and mutable slices are all safe). +/// +/// A typical use is to allocate a [`CoherentBox`], populate it with normal CPU access, and then +/// convert it into a [`Coherent`] object to share it with the device. +/// +/// # Examples +/// +/// `CoherentBox`: +/// +/// ``` +/// # use kernel::device::{ +/// # Bound, +/// # Device, +/// # }; +/// use kernel::dma::{attrs::*, +/// Coherent, +/// CoherentBox, +/// }; +/// +/// # fn test(dev: &Device) -> Result { +/// let mut dmem: CoherentBox = CoherentBox::zeroed(dev, GFP_KERNEL)?; +/// *dmem = 42; +/// let dmem: Coherent = dmem.into(); +/// # Ok::<(), Error>(()) } +/// ``` +/// +/// `CoherentBox<[T]>`: +/// +/// +/// ``` +/// # use kernel::device::{ +/// # Bound, +/// # Device, +/// # }; +/// use kernel::dma::{attrs::*, +/// Coherent, +/// CoherentBox, +/// }; +/// +/// # fn test(dev: &Device) -> Result { +/// let mut dmem: CoherentBox<[u64]> = CoherentBox::zeroed_slice(dev, 4, GFP_KERNEL)?; +/// dmem.fill(42); +/// let dmem: Coherent<[u64]> = dmem.into(); +/// # Ok::<(), Error>(()) } +/// ``` +pub struct CoherentBox(Coherent); + +impl CoherentBox<[T]> { + /// [`CoherentBox`] variant of [`Coherent::zeroed_slice_with_attrs`]. + #[inline] + pub fn zeroed_slice_with_attrs( + dev: &device::Device, + count: usize, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result { + Coherent::zeroed_slice_with_attrs(dev, count, gfp_flags, dma_attrs).map(Self) + } + + /// Same as [CoherentBox::zeroed_slice_with_attrs], but with `dma::Attrs(0)`. + #[inline] + pub fn zeroed_slice( + dev: &device::Device, + count: usize, + gfp_flags: kernel::alloc::Flags, + ) -> Result { + Self::zeroed_slice_with_attrs(dev, count, gfp_flags, Attrs(0)) + } + + /// Initializes the element at `i` using the given initializer. + /// + /// Returns `EINVAL` if `i` is out of bounds. + pub fn init_at(&mut self, i: usize, init: impl Init) -> Result + where + Error: From, + { + if i >= self.0.len() { + return Err(EINVAL); + } + + let ptr = &raw mut self[i]; + + // SAFETY: + // - `ptr` is valid, properly aligned, and within this allocation. + // - `T: AsBytes + FromBytes` guarantees all bit patterns are valid, so partial writes on + // error cannot leave the element in an invalid state. + // - The DMA address has not been exposed yet, so there is no concurrent device access. + unsafe { init.__init(ptr)? }; + + Ok(()) + } +} + +impl CoherentBox { + /// Same as [`CoherentBox::zeroed_slice_with_attrs`], but for a single element. + #[inline] + pub fn zeroed_with_attrs( + dev: &device::Device, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result { + Coherent::zeroed_with_attrs(dev, gfp_flags, dma_attrs).map(Self) + } + + /// Same as [`CoherentBox::zeroed_slice`], but for a single element. + #[inline] + pub fn zeroed(dev: &device::Device, gfp_flags: kernel::alloc::Flags) -> Result { + Self::zeroed_with_attrs(dev, gfp_flags, Attrs(0)) + } +} + +impl Deref for CoherentBox { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + // SAFETY: + // - We have not exposed the DMA address yet, so there can't be any concurrent access by a + // device. + // - We have exclusive access to `self.0`. + unsafe { self.0.as_ref() } + } +} + +impl DerefMut for CoherentBox { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: + // - We have not exposed the DMA address yet, so there can't be any concurrent access by a + // device. + // - We have exclusive access to `self.0`. + unsafe { self.0.as_mut() } + } +} + +impl From> for Coherent { + #[inline] + fn from(value: CoherentBox) -> Self { + value.0 + } +} + /// An abstraction of the `dma_alloc_coherent` API. /// /// This is an abstraction around the `dma_alloc_coherent` API which is used to allocate and map From 40251bac0e00818b0b632d2d154e4f1f815849eb Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 20 Mar 2026 20:45:40 +0100 Subject: [PATCH 172/712] rust: dma: add Coherent:init() and Coherent::init_with_attrs() Analogous to Coherent::zeroed() and Coherent::zeroed_with_attrs(), add Coherent:init() and Coherent::init_with_attrs() which both take an impl Init argument initializing the DMA coherent memory. Compared to CoherentInit, Coherent::init() is a one-shot constructor that runs an Init closure and immediately exposes the DMA handle, whereas CoherentInit is a multi-stage initializer that provides safe &mut T access by withholding the DMA address until converted to Coherent. Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-6-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 5b41603cee2e..be44ec99af5f 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -715,6 +715,44 @@ pub fn zeroed(dev: &device::Device, gfp_flags: kernel::alloc::Flags) -> R Self::zeroed_with_attrs(dev, gfp_flags, Attrs(0)) } + /// Same as [`Coherent::zeroed_with_attrs`], but instead of a zero-initialization the memory is + /// initialized with `init`. + pub fn init_with_attrs( + dev: &device::Device, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + init: impl Init, + ) -> Result + where + Error: From, + { + let dmem = Self::alloc_with_attrs(dev, gfp_flags, dma_attrs)?; + let ptr = dmem.as_mut_ptr(); + + // SAFETY: + // - `ptr` is valid, properly aligned, and points to exclusively owned memory. + // - If `__init` fails, `self` is dropped, which safely frees the underlying `Coherent`'s + // DMA memory. `T: AsBytes + FromBytes` ensures there are no complex `Drop` requirements + // we are bypassing. + unsafe { init.__init(ptr)? }; + + Ok(dmem) + } + + /// Same as [`Coherent::zeroed`], but instead of a zero-initialization the memory is initialized + /// with `init`. + #[inline] + pub fn init( + dev: &device::Device, + gfp_flags: kernel::alloc::Flags, + init: impl Init, + ) -> Result + where + Error: From, + { + Self::init_with_attrs(dev, gfp_flags, Attrs(0), init) + } + /// Allocates a region of `[T; len]` of coherent memory. fn alloc_slice_with_attrs( dev: &device::Device, From e21ad5e51c889e4b40a2a3d48363cbed9b047a68 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 20 Mar 2026 20:45:41 +0100 Subject: [PATCH 173/712] gpu: nova-core: use Coherent::init to initialize GspFwWprMeta Convert wpr_meta to use Coherent::init() and simplify the initialization. It also avoids a separate initialization of GspFwWprMeta on the stack. Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-7-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gsp/boot.rs | 7 ++----- drivers/gpu/nova-core/gsp/fw.rs | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 5e73bd769dcc..e55210ebb6d1 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -2,8 +2,7 @@ use kernel::{ device, - dma::CoherentAllocation, - dma_write, + dma::Coherent, io::poll::read_poll_timeout, pci, prelude::*, @@ -164,9 +163,7 @@ pub(crate) fn boot( bar, )?; - let wpr_meta = - CoherentAllocation::::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; - dma_write!(wpr_meta, [0]?, GspFwWprMeta::new(&gsp_fw, &fb_layout)); + let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?; self.cmdq .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?; diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index a061131b5412..4e3bfc6c4c47 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -204,7 +204,9 @@ pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 { /// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA /// addresses of the GSP bootloader and firmware. #[repr(transparent)] -pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta); +pub(crate) struct GspFwWprMeta { + inner: bindings::GspFwWprMeta, +} // SAFETY: Padding is explicit and does not contain uninitialized data. unsafe impl AsBytes for GspFwWprMeta {} @@ -217,10 +219,14 @@ unsafe impl FromBytes for GspFwWprMeta {} type GspFwWprMetaBootInfo = bindings::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1; impl GspFwWprMeta { - /// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the + /// Returns an initializer for a `GspFwWprMeta` suitable for booting `gsp_firmware` using the /// `fb_layout` layout. - pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self { - Self(bindings::GspFwWprMeta { + pub(crate) fn new<'a>( + gsp_firmware: &'a GspFirmware, + fb_layout: &'a FbLayout, + ) -> impl Init + 'a { + #[allow(non_snake_case)] + let init_inner = init!(bindings::GspFwWprMeta { // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified. magic: bindings::GSP_FW_WPR_META_MAGIC as u64, revision: u64::from(bindings::GSP_FW_WPR_META_REVISION), @@ -255,7 +261,11 @@ pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self { fbSize: fb_layout.fb.end - fb_layout.fb.start, vgaWorkspaceOffset: fb_layout.vga_workspace.start, vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start, - ..Default::default() + ..Zeroable::init_zeroed() + }); + + init!(GspFwWprMeta { + inner <- init_inner, }) } } From 7f3e836e4306c2026975fefc150cc0e5c569d5f3 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 20 Mar 2026 20:45:42 +0100 Subject: [PATCH 174/712] gpu: nova-core: convert Gsp::new() to use CoherentBox Convert libos (LibosMemoryRegionInitArgument) and rmargs (GspArgumentsPadded) to use CoherentBox / Coherent::init() and simplify the initialization. This also avoids separate initialization on the stack. Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-8-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gsp.rs | 47 +++++++++++-------------- drivers/gpu/nova-core/gsp/fw.rs | 62 +++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 72f173726f87..f0a50bdc4c00 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -5,10 +5,11 @@ use kernel::{ device, dma::{ + Coherent, CoherentAllocation, + CoherentBox, DmaAddress, // }, - dma_write, pci, prelude::*, transmute::AsBytes, // @@ -106,7 +107,7 @@ fn new(dev: &device::Device) -> Result { #[pin_data] pub(crate) struct Gsp { /// Libos arguments. - pub(crate) libos: CoherentAllocation, + pub(crate) libos: Coherent<[LibosMemoryRegionInitArgument]>, /// Init log buffer. loginit: LogBuffer, /// Interrupts log buffer. @@ -117,7 +118,7 @@ pub(crate) struct Gsp { #[pin] pub(crate) cmdq: Cmdq, /// RM arguments. - rmargs: CoherentAllocation, + rmargs: Coherent, } impl Gsp { @@ -126,34 +127,28 @@ pub(crate) fn new(pdev: &pci::Device) -> impl PinInit::alloc_coherent( - dev, - GSP_PAGE_SIZE / size_of::(), - GFP_KERNEL | __GFP_ZERO, - )?, loginit: LogBuffer::new(dev)?, logintr: LogBuffer::new(dev)?, logrm: LogBuffer::new(dev)?, cmdq <- Cmdq::new(dev), - rmargs: CoherentAllocation::::alloc_coherent( - dev, - 1, - GFP_KERNEL | __GFP_ZERO, - )?, - _: { - // Initialise the logging structures. The OpenRM equivalents are in: - // _kgspInitLibosLoggingStructures (allocates memory for buffers) - // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) - dma_write!( - libos, [0]?, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0) - ); - dma_write!( - libos, [1]?, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0) - ); - dma_write!(libos, [2]?, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0)); - dma_write!(rmargs, [0]?.inner, fw::GspArgumentsCached::new(&cmdq)); - dma_write!(libos, [3]?, LibosMemoryRegionInitArgument::new("RMARGS", rmargs)); + rmargs: Coherent::init(dev, GFP_KERNEL, GspArgumentsPadded::new(&cmdq))?, + libos: { + let mut libos = CoherentBox::zeroed_slice( + dev, + GSP_PAGE_SIZE / size_of::(), + GFP_KERNEL, + )?; + + libos.init_at(0, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; + libos.init_at(1, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; + libos.init_at(2, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; + libos.init_at(3, LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; + + libos.into() }, })) }) diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 4e3bfc6c4c47..0d8daf6a80b7 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -9,11 +9,12 @@ use core::ops::Range; use kernel::{ - dma::CoherentAllocation, + dma::Coherent, prelude::*, ptr::{ Alignable, - Alignment, // + Alignment, + KnownSize, // }, sizes::{ SZ_128K, @@ -648,7 +649,9 @@ unsafe impl AsBytes for RunCpuSequencer {} /// The memory allocated for the arguments must remain until the GSP sends the /// init_done RPC. #[repr(transparent)] -pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument); +pub(crate) struct LibosMemoryRegionInitArgument { + inner: bindings::LibosMemoryRegionInitArgument, +} // SAFETY: Padding is explicit and does not contain uninitialized data. unsafe impl AsBytes for LibosMemoryRegionInitArgument {} @@ -658,10 +661,10 @@ unsafe impl AsBytes for LibosMemoryRegionInitArgument {} unsafe impl FromBytes for LibosMemoryRegionInitArgument {} impl LibosMemoryRegionInitArgument { - pub(crate) fn new( + pub(crate) fn new<'a, A: AsBytes + FromBytes + KnownSize + ?Sized>( name: &'static str, - obj: &CoherentAllocation, - ) -> Self { + obj: &'a Coherent, + ) -> impl Init + 'a { /// Generates the `ID8` identifier required for some GSP objects. fn id8(name: &str) -> u64 { let mut bytes = [0u8; core::mem::size_of::()]; @@ -673,7 +676,8 @@ fn id8(name: &str) -> u64 { u64::from_ne_bytes(bytes) } - Self(bindings::LibosMemoryRegionInitArgument { + #[allow(non_snake_case)] + let init_inner = init!(bindings::LibosMemoryRegionInitArgument { id8: id8(name), pa: obj.dma_handle(), size: num::usize_as_u64(obj.size()), @@ -683,7 +687,11 @@ fn id8(name: &str) -> u64 { loc: num::u32_into_u8::< { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM }, >(), - ..Default::default() + ..Zeroable::init_zeroed() + }); + + init!(LibosMemoryRegionInitArgument { + inner <- init_inner, }) } } @@ -862,15 +870,23 @@ unsafe impl FromBytes for GspMsgElement {} /// Arguments for GSP startup. #[repr(transparent)] -pub(crate) struct GspArgumentsCached(bindings::GSP_ARGUMENTS_CACHED); +#[derive(Zeroable)] +pub(crate) struct GspArgumentsCached { + inner: bindings::GSP_ARGUMENTS_CACHED, +} impl GspArgumentsCached { /// Creates the arguments for starting the GSP up using `cmdq` as its command queue. - pub(crate) fn new(cmdq: &Cmdq) -> Self { - Self(bindings::GSP_ARGUMENTS_CACHED { - messageQueueInitArguments: MessageQueueInitArguments::new(cmdq).0, + pub(crate) fn new(cmdq: &Cmdq) -> impl Init + '_ { + #[allow(non_snake_case)] + let init_inner = init!(bindings::GSP_ARGUMENTS_CACHED { + messageQueueInitArguments <- MessageQueueInitArguments::new(cmdq), bDmemStack: 1, - ..Default::default() + ..Zeroable::init_zeroed() + }); + + init!(GspArgumentsCached { + inner <- init_inner, }) } } @@ -882,11 +898,21 @@ unsafe impl AsBytes for GspArgumentsCached {} /// must all be a multiple of GSP_PAGE_SIZE in size, so add padding to force it /// to that size. #[repr(C)] +#[derive(Zeroable)] pub(crate) struct GspArgumentsPadded { pub(crate) inner: GspArgumentsCached, _padding: [u8; GSP_PAGE_SIZE - core::mem::size_of::()], } +impl GspArgumentsPadded { + pub(crate) fn new(cmdq: &Cmdq) -> impl Init + '_ { + init!(GspArgumentsPadded { + inner <- GspArgumentsCached::new(cmdq), + ..Zeroable::init_zeroed() + }) + } +} + // SAFETY: Padding is explicit and will not contain uninitialized data. unsafe impl AsBytes for GspArgumentsPadded {} @@ -895,18 +921,18 @@ unsafe impl AsBytes for GspArgumentsPadded {} unsafe impl FromBytes for GspArgumentsPadded {} /// Init arguments for the message queue. -#[repr(transparent)] -struct MessageQueueInitArguments(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS); +type MessageQueueInitArguments = bindings::MESSAGE_QUEUE_INIT_ARGUMENTS; impl MessageQueueInitArguments { /// Creates a new init arguments structure for `cmdq`. - fn new(cmdq: &Cmdq) -> Self { - Self(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS { + #[allow(non_snake_case)] + fn new(cmdq: &Cmdq) -> impl Init + '_ { + init!(MessageQueueInitArguments { sharedMemPhysAddr: cmdq.dma_handle(), pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(), cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET), statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET), - ..Default::default() + ..Zeroable::init_zeroed() }) } } From f343012ebe80fdd93ed487f41b987a1507894cda Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 20 Mar 2026 20:45:43 +0100 Subject: [PATCH 175/712] gpu: nova-core: convert to new dma::Coherent API Remove all usages of dma::CoherentAllocation and use the new dma::Coherent type instead. Signed-off-by: Gary Guo Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260320194626.36263-9-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/dma.rs | 19 ++++++------- drivers/gpu/nova-core/falcon.rs | 5 ++-- drivers/gpu/nova-core/gsp.rs | 21 ++++++++------ drivers/gpu/nova-core/gsp/cmdq.rs | 21 ++++++-------- drivers/gpu/nova-core/gsp/fw.rs | 46 ++++++++++--------------------- 5 files changed, 47 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/nova-core/dma.rs b/drivers/gpu/nova-core/dma.rs index 7215398969da..3c19d5ffcfe8 100644 --- a/drivers/gpu/nova-core/dma.rs +++ b/drivers/gpu/nova-core/dma.rs @@ -9,13 +9,13 @@ use kernel::{ device, - dma::CoherentAllocation, + dma::Coherent, page::PAGE_SIZE, prelude::*, // }; pub(crate) struct DmaObject { - dma: CoherentAllocation, + dma: Coherent<[u8]>, } impl DmaObject { @@ -24,23 +24,22 @@ pub(crate) fn new(dev: &device::Device, len: usize) -> Result, data: &[u8]) -> Result { - Self::new(dev, data.len()).and_then(|mut dma_obj| { - // SAFETY: We have just allocated the DMA memory, we are the only users and - // we haven't made the device aware of the handle yet. - unsafe { dma_obj.write(data, 0)? } - Ok(dma_obj) - }) + let dma_obj = Self::new(dev, data.len())?; + // SAFETY: We have just allocated the DMA memory, we are the only users and + // we haven't made the device aware of the handle yet. + unsafe { dma_obj.as_mut()[..data.len()].copy_from_slice(data) }; + Ok(dma_obj) } } impl Deref for DmaObject { - type Target = CoherentAllocation; + type Target = Coherent<[u8]>; fn deref(&self) -> &Self::Target { &self.dma diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 7097a206ec3c..5bf8da8760bf 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -26,8 +26,7 @@ gpu::Chipset, num::{ self, - FromSafeCast, - IntoSafeCast, // + FromSafeCast, // }, regs, regs::macros::RegisterBase, // @@ -653,7 +652,7 @@ fn dma_wr( } FalconMem::Dmem => ( 0, - dma_obj.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, + dma_obj.dma_handle() + DmaAddress::from(load_offsets.src_start), ), }; if dma_start % DmaAddress::from(DMA_LEN) > 0 { diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index f0a50bdc4c00..a045c4189989 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -6,13 +6,15 @@ device, dma::{ Coherent, - CoherentAllocation, CoherentBox, DmaAddress, // }, pci, prelude::*, - transmute::AsBytes, // + transmute::{ + AsBytes, + FromBytes, // + }, // }; pub(crate) mod cmdq; @@ -44,6 +46,9 @@ #[repr(C)] struct PteArray([u64; NUM_ENTRIES]); +/// SAFETY: arrays of `u64` implement `FromBytes` and we are but a wrapper around one. +unsafe impl FromBytes for PteArray {} + /// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one. unsafe impl AsBytes for PteArray {} @@ -71,26 +76,24 @@ fn entry(start: DmaAddress, index: usize) -> Result { /// then pp points to index into the buffer where the next logging entry will /// be written. Therefore, the logging data is valid if: /// 1 <= pp < sizeof(buffer)/sizeof(u64) -struct LogBuffer(CoherentAllocation); +struct LogBuffer(Coherent<[u8]>); impl LogBuffer { /// Creates a new `LogBuffer` mapped on `dev`. fn new(dev: &device::Device) -> Result { const NUM_PAGES: usize = RM_LOG_BUFFER_NUM_PAGES; - let mut obj = Self(CoherentAllocation::::alloc_coherent( + let obj = Self(Coherent::::zeroed_slice( dev, NUM_PAGES * GSP_PAGE_SIZE, - GFP_KERNEL | __GFP_ZERO, + GFP_KERNEL, )?); let start_addr = obj.0.dma_handle(); // SAFETY: `obj` has just been created and we are its sole user. - let pte_region = unsafe { - obj.0 - .as_slice_mut(size_of::(), NUM_PAGES * size_of::())? - }; + let pte_region = + unsafe { &mut obj.0.as_mut()[size_of::()..][..NUM_PAGES * size_of::()] }; // Write values one by one to avoid an on-stack instance of `PteArray`. for (i, chunk) in pte_region.chunks_exact_mut(size_of::()).enumerate() { diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index d36a62ba1c60..f38790601a0f 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -7,7 +7,7 @@ use kernel::{ device, dma::{ - CoherentAllocation, + Coherent, DmaAddress, // }, dma_write, @@ -207,7 +207,7 @@ unsafe impl AsBytes for GspMem {} // that is not a problem because they are not used outside the kernel. unsafe impl FromBytes for GspMem {} -/// Wrapper around [`GspMem`] to share it with the GPU using a [`CoherentAllocation`]. +/// Wrapper around [`GspMem`] to share it with the GPU using a [`Coherent`]. /// /// This provides the low-level functionality to communicate with the GSP, including allocation of /// queue space to write messages to and management of read/write pointers. @@ -218,7 +218,7 @@ unsafe impl FromBytes for GspMem {} /// pointer and the GSP read pointer. This region is returned by [`Self::driver_write_area`]. /// * The driver owns (i.e. can read from) the part of the GSP message queue between the CPU read /// pointer and the GSP write pointer. This region is returned by [`Self::driver_read_area`]. -struct DmaGspMem(CoherentAllocation); +struct DmaGspMem(Coherent); impl DmaGspMem { /// Allocate a new instance and map it for `dev`. @@ -226,21 +226,20 @@ fn new(dev: &device::Device) -> Result { const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::() }>(); const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>(); - let gsp_mem = - CoherentAllocation::::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; + let gsp_mem = Coherent::::zeroed(dev, GFP_KERNEL)?; let start = gsp_mem.dma_handle(); // Write values one by one to avoid an on-stack instance of `PteArray`. for i in 0..GspMem::PTE_ARRAY_SIZE { - dma_write!(gsp_mem, [0]?.ptes.0[i], PteArray::<0>::entry(start, i)?); + dma_write!(gsp_mem, .ptes.0[i], PteArray::<0>::entry(start, i)?); } dma_write!( gsp_mem, - [0]?.cpuq.tx, + .cpuq.tx, MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES) ); - dma_write!(gsp_mem, [0]?.cpuq.rx, MsgqRxHeader::new()); + dma_write!(gsp_mem, .cpuq.rx, MsgqRxHeader::new()); Ok(Self(gsp_mem)) } @@ -255,10 +254,9 @@ fn new(dev: &device::Device) -> Result { let rx = self.gsp_read_ptr() as usize; // SAFETY: - // - The `CoherentAllocation` contains exactly one object. // - We will only access the driver-owned part of the shared memory. // - Per the safety statement of the function, no concurrent access will be performed. - let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0]; + let gsp_mem = unsafe { &mut *self.0.as_mut() }; // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`. let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx); @@ -309,10 +307,9 @@ fn driver_write_area_size(&self) -> usize { let rx = self.cpu_read_ptr() as usize; // SAFETY: - // - The `CoherentAllocation` contains exactly one object. // - We will only access the driver-owned part of the shared memory. // - Per the safety statement of the function, no concurrent access will be performed. - let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0]; + let gsp_mem = unsafe { &*self.0.as_ptr() }; let data = &gsp_mem.gspq.msgq.data; // The area starting at `rx` and ending at `tx - 1` modulo MSGQ_NUM_PAGES, inclusive, diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 0d8daf6a80b7..847b5eb215d4 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -40,8 +40,7 @@ }, }; -// TODO: Replace with `IoView` projections once available; the `unwrap()` calls go away once we -// switch to the new `dma::Coherent` API. +// TODO: Replace with `IoView` projections once available. pub(super) mod gsp_mem { use core::sync::atomic::{ fence, @@ -49,10 +48,9 @@ pub(super) mod gsp_mem { }; use kernel::{ - dma::CoherentAllocation, + dma::Coherent, dma_read, - dma_write, - prelude::*, // + dma_write, // }; use crate::gsp::cmdq::{ @@ -60,49 +58,35 @@ pub(super) mod gsp_mem { MSGQ_NUM_PAGES, // }; - pub(in crate::gsp) fn gsp_write_ptr(qs: &CoherentAllocation) -> u32 { - // PANIC: A `dma::CoherentAllocation` always contains at least one element. - || -> Result { Ok(dma_read!(qs, [0]?.gspq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap() + pub(in crate::gsp) fn gsp_write_ptr(qs: &Coherent) -> u32 { + dma_read!(qs, .gspq.tx.0.writePtr) % MSGQ_NUM_PAGES } - pub(in crate::gsp) fn gsp_read_ptr(qs: &CoherentAllocation) -> u32 { - // PANIC: A `dma::CoherentAllocation` always contains at least one element. - || -> Result { Ok(dma_read!(qs, [0]?.gspq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap() + pub(in crate::gsp) fn gsp_read_ptr(qs: &Coherent) -> u32 { + dma_read!(qs, .gspq.rx.0.readPtr) % MSGQ_NUM_PAGES } - pub(in crate::gsp) fn cpu_read_ptr(qs: &CoherentAllocation) -> u32 { - // PANIC: A `dma::CoherentAllocation` always contains at least one element. - || -> Result { Ok(dma_read!(qs, [0]?.cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap() + pub(in crate::gsp) fn cpu_read_ptr(qs: &Coherent) -> u32 { + dma_read!(qs, .cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES } - pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &CoherentAllocation, count: u32) { + pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &Coherent, count: u32) { let rptr = cpu_read_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES; // Ensure read pointer is properly ordered. fence(Ordering::SeqCst); - // PANIC: A `dma::CoherentAllocation` always contains at least one element. - || -> Result { - dma_write!(qs, [0]?.cpuq.rx.0.readPtr, rptr); - Ok(()) - }() - .unwrap() + dma_write!(qs, .cpuq.rx.0.readPtr, rptr); } - pub(in crate::gsp) fn cpu_write_ptr(qs: &CoherentAllocation) -> u32 { - // PANIC: A `dma::CoherentAllocation` always contains at least one element. - || -> Result { Ok(dma_read!(qs, [0]?.cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap() + pub(in crate::gsp) fn cpu_write_ptr(qs: &Coherent) -> u32 { + dma_read!(qs, .cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES } - pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &CoherentAllocation, count: u32) { + pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &Coherent, count: u32) { let wptr = cpu_write_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES; - // PANIC: A `dma::CoherentAllocation` always contains at least one element. - || -> Result { - dma_write!(qs, [0]?.cpuq.tx.0.writePtr, wptr); - Ok(()) - }() - .unwrap(); + dma_write!(qs, .cpuq.tx.0.writePtr, wptr); // Ensure all command data is visible before triggering the GSP read. fence(Ordering::SeqCst); From 55fd681cdd8599edc82013674cbf87c2f22d58f8 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Sat, 21 Mar 2026 19:18:08 +0100 Subject: [PATCH 176/712] rust: dma: remove dma::CoherentAllocation Now that everything has been converted to the new dma::Coherent API, remove dma::CoherentAllocation. Suggested-by: Gary Guo Reviewed-by: Gary Guo Link: https://patch.msgid.link/DH8O47F2GM1Z.3H3E13RSKIV22@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 150 --------------------------------------------- 1 file changed, 150 deletions(-) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index be44ec99af5f..bf823818a67d 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -853,156 +853,6 @@ pub fn len(&self) -> usize { } } -// Type alias for compatibility. -#[doc(hidden)] -pub type CoherentAllocation = Coherent<[T]>; - -impl CoherentAllocation { - /// Allocates a region of `size_of:: * count` of coherent memory. - /// - /// # Examples - /// - /// ``` - /// # use kernel::device::{Bound, Device}; - /// use kernel::dma::{attrs::*, CoherentAllocation}; - /// - /// # fn test(dev: &Device) -> Result { - /// let c: CoherentAllocation = - /// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?; - /// # Ok::<(), Error>(()) } - /// ``` - pub fn alloc_attrs( - dev: &device::Device, - count: usize, - gfp_flags: kernel::alloc::Flags, - dma_attrs: Attrs, - ) -> Result> { - Coherent::alloc_slice_with_attrs(dev, count, gfp_flags, dma_attrs) - } - - /// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the - /// `dma_attrs` is 0 by default. - pub fn alloc_coherent( - dev: &device::Device, - count: usize, - gfp_flags: kernel::alloc::Flags, - ) -> Result> { - CoherentAllocation::alloc_attrs(dev, count, gfp_flags, Attrs(0)) - } - - /// Returns the base address to the allocated region in the CPU's virtual address space. - pub fn start_ptr(&self) -> *const T { - self.as_ptr().cast() - } - - /// Returns the base address to the allocated region in the CPU's virtual address space as - /// a mutable pointer. - pub fn start_ptr_mut(&mut self) -> *mut T { - self.as_mut_ptr().cast() - } - - /// Returns a DMA handle starting at `offset` (in units of `T`) which may be given to the - /// device as the DMA address base of the region. - /// - /// Returns `EINVAL` if `offset` is not within the bounds of the allocation. - pub fn dma_handle_with_offset(&self, offset: usize) -> Result { - if offset >= self.len() { - Err(EINVAL) - } else { - Ok(self.dma_handle + (offset * core::mem::size_of::()) as DmaAddress) - } - } - - /// Common helper to validate a range applied from the allocated region in the CPU's virtual - /// address space. - fn validate_range(&self, offset: usize, count: usize) -> Result { - if offset.checked_add(count).ok_or(EOVERFLOW)? > self.len() { - return Err(EINVAL); - } - Ok(()) - } - - /// Returns the data from the region starting from `offset` as a slice. - /// `offset` and `count` are in units of `T`, not the number of bytes. - /// - /// For ringbuffer type of r/w access or use-cases where the pointer to the live data is needed, - /// [`CoherentAllocation::start_ptr`] or [`CoherentAllocation::start_ptr_mut`] could be used - /// instead. - /// - /// # Safety - /// - /// * Callers must ensure that the device does not read/write to/from memory while the returned - /// slice is live. - /// * Callers must ensure that this call does not race with a write to the same region while - /// the returned slice is live. - pub unsafe fn as_slice(&self, offset: usize, count: usize) -> Result<&[T]> { - self.validate_range(offset, count)?; - // SAFETY: - // - The pointer is valid due to type invariant on `CoherentAllocation`, - // we've just checked that the range and index is within bounds. The immutability of the - // data is also guaranteed by the safety requirements of the function. - // - `offset + count` can't overflow since it is smaller than `self.count` and we've checked - // that `self.count` won't overflow early in the constructor. - Ok(unsafe { core::slice::from_raw_parts(self.start_ptr().add(offset), count) }) - } - - /// Performs the same functionality as [`CoherentAllocation::as_slice`], except that a mutable - /// slice is returned. - /// - /// # Safety - /// - /// * Callers must ensure that the device does not read/write to/from memory while the returned - /// slice is live. - /// * Callers must ensure that this call does not race with a read or write to the same region - /// while the returned slice is live. - pub unsafe fn as_slice_mut(&mut self, offset: usize, count: usize) -> Result<&mut [T]> { - self.validate_range(offset, count)?; - // SAFETY: - // - The pointer is valid due to type invariant on `CoherentAllocation`, - // we've just checked that the range and index is within bounds. The immutability of the - // data is also guaranteed by the safety requirements of the function. - // - `offset + count` can't overflow since it is smaller than `self.count` and we've checked - // that `self.count` won't overflow early in the constructor. - Ok(unsafe { core::slice::from_raw_parts_mut(self.start_ptr_mut().add(offset), count) }) - } - - /// Writes data to the region starting from `offset`. `offset` is in units of `T`, not the - /// number of bytes. - /// - /// # Safety - /// - /// * Callers must ensure that this call does not race with a read or write to the same region - /// that overlaps with this write. - /// - /// # Examples - /// - /// ``` - /// # fn test(alloc: &mut kernel::dma::CoherentAllocation) -> Result { - /// let somedata: [u8; 4] = [0xf; 4]; - /// let buf: &[u8] = &somedata; - /// // SAFETY: There is no concurrent HW operation on the device and no other R/W access to the - /// // region. - /// unsafe { alloc.write(buf, 0)?; } - /// # Ok::<(), Error>(()) } - /// ``` - pub unsafe fn write(&mut self, src: &[T], offset: usize) -> Result { - self.validate_range(offset, src.len())?; - // SAFETY: - // - The pointer is valid due to type invariant on `CoherentAllocation` - // and we've just checked that the range and index is within bounds. - // - `offset + count` can't overflow since it is smaller than `self.count` and we've checked - // that `self.count` won't overflow early in the constructor. - unsafe { - core::ptr::copy_nonoverlapping( - src.as_ptr(), - self.start_ptr_mut().add(offset), - src.len(), - ) - }; - Ok(()) - } -} - /// Note that the device configured to do DMA must be halted before this object is dropped. impl Drop for Coherent { fn drop(&mut self) { From 651c27d6a7e2b92eb30f56738dc45e861c975921 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 19 Mar 2026 15:00:40 +0900 Subject: [PATCH 177/712] gpu: nova-core: gsp: move Cmdq's DMA handle to a struct member The command-queue structure has a `dma_handle` method that returns the DMA handle to the memory segment shared with the GSP. This works, but is not ideal for the following reasons: - That method is effectively only ever called once, and is technically an accessor method since the handle doesn't change over time, - It feels a bit out-of-place with the other methods of `Cmdq` which only deal with the sending or receiving of messages, - The method has `pub(crate)` visibility, allowing other driver code to access this highly-sensitive handle. Address all these issues by turning `dma_handle` into a struct member with `pub(super)` visibility. This keeps the method space focused, and also ensures the member is not visible outside of the modules that need it. Reviewed-by: Eliot Courtney Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260319-b4-cmdq-dma-handle-v1-1-57840b4a4f90@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/cmdq.rs | 26 +++++++++++++++----------- drivers/gpu/nova-core/gsp/fw.rs | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index f38790601a0f..c853be23e3a5 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -30,6 +30,8 @@ SplitState, // }; +use pin_init::pin_init_scope; + use crate::{ driver::Bar0, gsp::{ @@ -452,6 +454,8 @@ pub(crate) struct Cmdq { /// Inner mutex-protected state. #[pin] inner: Mutex, + /// DMA handle of the command queue's shared memory region. + pub(super) dma_handle: DmaAddress, } impl Cmdq { @@ -476,12 +480,17 @@ impl Cmdq { /// Creates a new command queue for `dev`. pub(crate) fn new(dev: &device::Device) -> impl PinInit + '_ { - try_pin_init!(Self { - inner <- new_mutex!(CmdqInner { - dev: dev.into(), - gsp_mem: DmaGspMem::new(dev)?, - seq: 0, - }), + pin_init_scope(move || { + let gsp_mem = DmaGspMem::new(dev)?; + + Ok(try_pin_init!(Self { + dma_handle: gsp_mem.0.dma_handle(), + inner <- new_mutex!(CmdqInner { + dev: dev.into(), + gsp_mem, + seq: 0, + }), + })) }) } @@ -567,11 +576,6 @@ pub(crate) fn receive_msg(&self, timeout: Delta) -> Result { self.inner.lock().receive_msg(timeout) } - - /// Returns the DMA handle of the command queue's shared memory region. - pub(crate) fn dma_handle(&self) -> DmaAddress { - self.inner.lock().gsp_mem.0.dma_handle() - } } /// Inner mutex protected state of [`Cmdq`]. diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index 847b5eb215d4..0c8a74f0e8ac 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -912,7 +912,7 @@ impl MessageQueueInitArguments { #[allow(non_snake_case)] fn new(cmdq: &Cmdq) -> impl Init + '_ { init!(MessageQueueInitArguments { - sharedMemPhysAddr: cmdq.dma_handle(), + sharedMemPhysAddr: cmdq.dma_handle, pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(), cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET), statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET), From 3df7e2feb8f5706eb6d00d043b9613d15c140d38 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:34 +0000 Subject: [PATCH 178/712] drm/i915/lt_phy: Dump missing PLL state parameters Dump missing PLL structure members ssc_enabled and tbt_mode in order to enhance debugging. v2: Drop addr_lsb and addr_msb printouts Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-2-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index eced8493e566..f768804122c1 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2141,7 +2141,9 @@ void intel_lt_phy_dump_hw_state(struct intel_display *display, { int i, j; - drm_dbg_kms(display->drm, "lt_phy_pll_hw_state:\n"); + drm_dbg_kms(display->drm, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", + hw_state->ssc_enabled, hw_state->tbt_mode); + for (i = 0; i < 3; i++) { drm_dbg_kms(display->drm, "config[%d] = 0x%.4x,\n", i, hw_state->config[i]); From 78ca669ca5f742eea84495f5f5e2e3c1aed82372 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:35 +0000 Subject: [PATCH 179/712] drm/i915/lt_phy: Add check if PLL is enabled Add check for PLL enabling and return early if PLL is not enabled. v2: Use PCLK PLL ACK bit to check if PLL is enabled (Suraj) v3: Check only if PCLK PLL ACK bit for lane 0 is enabled (Suraj) Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-3-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index f768804122c1..e1c95f58b6ae 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2176,6 +2176,14 @@ intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, return false; } +static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + + return intel_de_read(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port)) & + XELPDP_LANE_PCLK_PLL_ACK(0); +} + void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, struct intel_lt_phy_pll_state *pll_state) @@ -2185,6 +2193,9 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct ref_tracker *wakeref; int i, j, k; + if (!intel_lt_phy_pll_is_enabled(encoder)) + return; + pll_state->tbt_mode = intel_tc_port_in_tbt_alt_mode(enc_to_dig_port(encoder)); if (pll_state->tbt_mode) return; From 0bd5b45c92c67ca43263d324d153de855e1fc6ba Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:36 +0000 Subject: [PATCH 180/712] drm/i915/lt_phy: Add PLL information for xe3plpd Start bringing in xe3plpd as part of dpll framework. The work is started by adding PLL information and related function hooks. v2: Fix xe3plpd type (Suraj) Remove empty line between BSpec link and Signed-off-by (Suraj) BSpec: 74304 Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-4-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index f35a9252f4e1..4185c8e136da 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4571,6 +4571,25 @@ static const struct intel_dpll_mgr mtl_pll_mgr = { .compare_hw_state = mtl_compare_hw_state, }; +static const struct intel_dpll_funcs xe3plpd_pll_funcs = { +}; + +static const struct dpll_info xe3plpd_plls[] = { + { .name = "DPLL 0", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + /* TODO: Add TBT */ + { .name = "TC PLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL1, }, + { .name = "TC PLL 2", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL2, }, + { .name = "TC PLL 3", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL3, }, + { .name = "TC PLL 4", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL4, }, + {} +}; + +__maybe_unused +static const struct intel_dpll_mgr xe3plpd_pll_mgr = { + .dpll_info = xe3plpd_plls, +}; + /** * intel_dpll_init - Initialize DPLLs * @display: intel_display device From f52bbb00deaaa137271217e158537151f6c792b6 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:37 +0000 Subject: [PATCH 181/712] drm/i915/lt_phy: Refactor LT PHY PLL handling to use explicit PLL state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LT PHY implementation currently pulls PLL and port_clock information directly from the CRTC state. This ties the PHY programming logic too tightly to the CRTC state and makes it harder to clearly express the PHY’s own PLL configuration. Introduce an explicit "struct intel_lt_phy_pll_state" argument for the PHY functions and update callers accordingly. No functional change is intended — this is a preparatory cleanup for to bring LT PHY PLL handling as part of PLL framework. v2: DP, HDMI 2.0, and HDMI FRL modes are port of the VDR configuration 0 register. These modes are defined by bits 2:0. Decode these to differentiate DP and HDMI modes when programming PLL's. (Imre, Suraj) v3: Pass port_clock as argument instead of recalculating it (Suraj) v4: Fix checkpatch warning of line length exceeding 100 columns BSpec: 744921 Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-5-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 67 ++++++++++++++------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index e1c95f58b6ae..2d52242cb3fc 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -32,6 +32,7 @@ INTEL_LT_PHY_LANE0) #define MODE_DP 3 #define MODE_HDMI_20 4 +#define MODE_HDMI_FRL 5 #define Q32_TO_INT(x) ((x) >> 32) #define Q32_TO_FRAC(x) ((x) & 0xFFFFFFFF) #define DCO_MIN_FREQ_MHZ 11850 @@ -1176,9 +1177,30 @@ intel_lt_phy_lane_reset(struct intel_encoder *encoder, intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_phy_pulse_status, 0); } +static bool intel_lt_phy_is_hdmi(const struct intel_lt_phy_pll_state *ltpll) +{ + u8 mode = REG_FIELD_GET8(LT_PHY_VDR_MODE_ENCODING_MASK, ltpll->config[0]); + + if (mode == MODE_HDMI_20 || mode == MODE_HDMI_FRL) + return true; + + return false; +} + +static bool intel_lt_phy_is_dp(const struct intel_lt_phy_pll_state *ltpll) +{ + u8 mode = REG_FIELD_GET8(LT_PHY_VDR_MODE_ENCODING_MASK, ltpll->config[0]); + + if (mode == MODE_DP) + return true; + + return false; +} + static void intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state, + const struct intel_lt_phy_pll_state *ltpll, + int port_clock, bool lane_reversal) { struct intel_display *display = to_intel_display(encoder); @@ -1195,17 +1217,16 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, * but since the register bits still remain the same we use * the same definition */ - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) && - intel_hdmi_is_frl(crtc_state->port_clock)) + if (intel_lt_phy_is_hdmi(ltpll) && intel_hdmi_is_frl(port_clock)) val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_DIV18CLK); else val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); /* DP2.0 10G and 20G rates enable MPLLA*/ - if (crtc_state->port_clock == 1000000 || crtc_state->port_clock == 2000000) + if (port_clock == 1000000 || port_clock == 2000000) val |= XELPDP_SSC_ENABLE_PLLA; else - val |= crtc_state->dpll_hw_state.ltpll.ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | @@ -1248,7 +1269,8 @@ static u32 intel_lt_phy_get_dp_clock(u8 rate) static bool intel_lt_phy_config_changed(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + const struct intel_lt_phy_pll_state *ltpll, + u32 port_clock) { u8 val, rate; u32 clock; @@ -1262,9 +1284,9 @@ intel_lt_phy_config_changed(struct intel_encoder *encoder, * using 1.62 Gbps clock since PHY PLL defaults to that * otherwise we always need to reconfigure it. */ - if (intel_crtc_has_dp_encoder(crtc_state)) { + if (intel_lt_phy_is_dp(ltpll)) { clock = intel_lt_phy_get_dp_clock(rate); - if (crtc_state->port_clock == 1620000 && crtc_state->port_clock == clock) + if (port_clock == 1620000 && port_clock == clock) return false; } @@ -1759,41 +1781,41 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, static void intel_lt_phy_program_pll(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + const struct intel_lt_phy_pll_state *ltpll) { u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); int i, j, k; intel_lt_phy_write(encoder, owned_lane_mask, LT_PHY_VDR_0_CONFIG, - crtc_state->dpll_hw_state.ltpll.config[0], MB_WRITE_COMMITTED); + ltpll->config[0], MB_WRITE_COMMITTED); intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_1_CONFIG, - crtc_state->dpll_hw_state.ltpll.config[1], MB_WRITE_COMMITTED); + ltpll->config[1], MB_WRITE_COMMITTED); intel_lt_phy_write(encoder, owned_lane_mask, LT_PHY_VDR_2_CONFIG, - crtc_state->dpll_hw_state.ltpll.config[2], MB_WRITE_COMMITTED); + ltpll->config[2], MB_WRITE_COMMITTED); for (i = 0; i <= 12; i++) { intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_ADDR_MSB(i), - crtc_state->dpll_hw_state.ltpll.addr_msb[i], + ltpll->addr_msb[i], MB_WRITE_COMMITTED); intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_ADDR_LSB(i), - crtc_state->dpll_hw_state.ltpll.addr_lsb[i], + ltpll->addr_lsb[i], MB_WRITE_COMMITTED); for (j = 3, k = 0; j >= 0; j--, k++) intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_DATAY(i, j), - crtc_state->dpll_hw_state.ltpll.data[i][k], + ltpll->data[i][k], MB_WRITE_COMMITTED); } } static void intel_lt_phy_enable_disable_tx(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + const struct intel_lt_phy_pll_state *ltpll, + u8 lane_count) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); bool lane_reversal = dig_port->lane_reversal; - u8 lane_count = crtc_state->lane_count; bool is_dp_alt = intel_tc_port_in_dp_alt_mode(dig_port); enum intel_tc_pin_assignment tc_pin = @@ -1895,7 +1917,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_lane_reset(encoder, crtc_state->lane_count); /* 2. Program PORT_CLOCK_CTL register to configure clock muxes, gating, and SSC. */ - intel_lt_phy_program_port_clock_ctl(encoder, crtc_state, lane_reversal); + intel_lt_phy_program_port_clock_ctl(encoder, &crtc_state->dpll_hw_state.ltpll, + crtc_state->port_clock, lane_reversal); /* 3. Change owned PHY lanes power to Ready state. */ intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, @@ -1905,12 +1928,13 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * 4. Read the PHY message bus VDR register PHY_VDR_0_Config check enabled PLL type, * encoded rate and encoded mode. */ - if (intel_lt_phy_config_changed(encoder, crtc_state)) { + if (intel_lt_phy_config_changed(encoder, &crtc_state->dpll_hw_state.ltpll, + crtc_state->port_clock)) { /* * 5. Program the PHY internal PLL registers over PHY message bus for the desired * frequency and protocol type */ - intel_lt_phy_program_pll(encoder, crtc_state); + intel_lt_phy_program_pll(encoder, &crtc_state->dpll_hw_state.ltpll); /* 6. Use the P2P transaction flow */ /* @@ -2001,7 +2025,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, XELPDP_P0_STATE_ACTIVE); - intel_lt_phy_enable_disable_tx(encoder, crtc_state); + intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll, + crtc_state->lane_count); intel_lt_phy_transaction_end(encoder, wakeref); } From 6e1c3b80ee801d1450a20a5420e79f9460bc5f0b Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:38 +0000 Subject: [PATCH 182/712] drm/i915/lt_phy: Add lane_count to PLL state Cache lane count as part of PLL state. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-6-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.h | 1 + drivers/gpu/drm/i915/display/intel_lt_phy.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index 4cc14ce5eebe..d408ccf6f902 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -278,6 +278,7 @@ struct intel_lt_phy_pll_state { u8 config[3]; bool ssc_enabled; bool tbt_mode; + int lane_count; }; struct intel_dpll_hw_state { diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 2d52242cb3fc..3e83ac775d84 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1767,11 +1767,13 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, } crtc_state->dpll_hw_state.ltpll.ssc_enabled = intel_lt_phy_pll_is_ssc_enabled(crtc_state, encoder); + crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; return 0; } } if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) { + crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; return intel_lt_phy_calculate_hdmi_state(&crtc_state->dpll_hw_state.ltpll, crtc_state->port_clock); } @@ -1811,11 +1813,11 @@ intel_lt_phy_program_pll(struct intel_encoder *encoder, static void intel_lt_phy_enable_disable_tx(struct intel_encoder *encoder, - const struct intel_lt_phy_pll_state *ltpll, - u8 lane_count) + const struct intel_lt_phy_pll_state *ltpll) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); bool lane_reversal = dig_port->lane_reversal; + u8 lane_count = ltpll->lane_count; bool is_dp_alt = intel_tc_port_in_dp_alt_mode(dig_port); enum intel_tc_pin_assignment tc_pin = @@ -2025,8 +2027,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, XELPDP_P0_STATE_ACTIVE); - intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll, - crtc_state->lane_count); + intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll); intel_lt_phy_transaction_end(encoder, wakeref); } From 3ce06de38959fdadb2da7350918ff963069463fb Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:39 +0000 Subject: [PATCH 183/712] drm/i915/lt_phy: Add xe3plpd .compute_dplls hook Add compute dpll hook for xe3plpd platform and bring PLL state calculation to support PLL framework. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-7-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 65 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 17 +++-- drivers/gpu/drm/i915/display/intel_lt_phy.h | 3 +- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 8433e3ff0319..147baa777856 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1222,7 +1222,7 @@ static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *state, struct intel_display *display = to_intel_display(encoder); int ret; - ret = intel_lt_phy_pll_calc_state(crtc_state, encoder); + ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &crtc_state->dpll_hw_state); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 4185c8e136da..58c24e2164ca 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4585,9 +4585,74 @@ static const struct dpll_info xe3plpd_plls[] = { {} }; +static int xe3plpd_compute_non_tc_phy_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct icl_port_dpll *port_dpll = + &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + int ret; + + ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &port_dpll->hw_state); + if (ret) + return ret; + + /* this is mainly for the fastset check */ + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + + crtc_state->port_clock = intel_lt_phy_calc_port_clock(display, &port_dpll->hw_state.ltpll); + + return 0; +} + +static int xe3plpd_compute_tc_phy_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + struct icl_port_dpll *port_dpll; + int ret; + + /* TODO: Add state calculation for TBT PLL */ + + port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_MG_PHY]; + ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &port_dpll->hw_state); + if (ret) + return ret; + + /* this is mainly for the fastset check */ + if (old_crtc_state->intel_dpll && + old_crtc_state->intel_dpll->info->id == DPLL_ID_ICL_TBTPLL) + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + else + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_MG_PHY); + + crtc_state->port_clock = intel_lt_phy_calc_port_clock(display, &port_dpll->hw_state.ltpll); + + return 0; +} + +static int xe3plpd_compute_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + if (intel_encoder_is_tc(encoder)) + return xe3plpd_compute_tc_phy_dplls(state, crtc, encoder); + else + return xe3plpd_compute_non_tc_phy_dpll(state, crtc, encoder); +} + __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, + .compute_dplls = xe3plpd_compute_dplls, }; /** diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 3e83ac775d84..ed5cfd6641b1 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1745,12 +1745,15 @@ intel_lt_phy_calc_port_clock(struct intel_display *display, int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder) + struct intel_encoder *encoder, + struct intel_dpll_hw_state *hw_state) { struct intel_display *display = to_intel_display(crtc_state); const struct intel_lt_phy_pll_params *tables; int i; + memset(hw_state, 0, sizeof(*hw_state)); + tables = intel_lt_phy_pll_tables_get(crtc_state, encoder); if (!tables) return -EINVAL; @@ -1760,21 +1763,21 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, drm_WARN_ON(display->drm, !intel_dpll_clock_matches(clock, tables[i].clock_rate)); if (intel_dpll_clock_matches(crtc_state->port_clock, clock)) { - crtc_state->dpll_hw_state.ltpll = *tables[i].state; + hw_state->ltpll = *tables[i].state; if (intel_crtc_has_dp_encoder(crtc_state)) { if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) - crtc_state->dpll_hw_state.ltpll.config[2] = 1; + hw_state->ltpll.config[2] = 1; } - crtc_state->dpll_hw_state.ltpll.ssc_enabled = + hw_state->ltpll.ssc_enabled = intel_lt_phy_pll_is_ssc_enabled(crtc_state, encoder); - crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; + hw_state->ltpll.lane_count = crtc_state->lane_count; return 0; } } if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) { - crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; - return intel_lt_phy_calculate_hdmi_state(&crtc_state->dpll_hw_state.ltpll, + hw_state->ltpll.lane_count = crtc_state->lane_count; + return intel_lt_phy_calculate_hdmi_state(&hw_state->ltpll, crtc_state->port_clock); } diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index db905668f86d..61ec0e5d8888 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -20,7 +20,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, void intel_lt_phy_pll_disable(struct intel_encoder *encoder); int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder); + struct intel_encoder *encoder, + struct intel_dpll_hw_state *hw_state); int intel_lt_phy_calc_port_clock(struct intel_display *display, const struct intel_lt_phy_pll_state *lt_state); void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, From 565604328606bf6c187007dc1b7dfe151bbc6ba9 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:40 +0000 Subject: [PATCH 184/712] drm/i915/lt_phy: Add xe3plpd .get_dplls hook Add .get_dplls function pointer for xe3plpd platforms to support dpll framework. Reuse the ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-8-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 58c24e2164ca..9aa8eb0a7d4a 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4653,6 +4653,7 @@ __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, .compute_dplls = xe3plpd_compute_dplls, + .get_dplls = mtl_get_dplls, }; /** From a04b8125db835418560fdab82ed47685ab77644d Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:41 +0000 Subject: [PATCH 185/712] drm/i915/lt_phy: Add xe3plpd .put_dplls hook Add .put_dplls function pointer to support xe3plpd platform on dpll framework. Reuse ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-9-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 9aa8eb0a7d4a..af2613eeaf92 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4654,6 +4654,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, .compute_dplls = xe3plpd_compute_dplls, .get_dplls = mtl_get_dplls, + .put_dplls = icl_put_dplls, }; /** From 1e8988c328e09550f683fa8adc51eb8553ab6fb8 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:42 +0000 Subject: [PATCH 186/712] drm/i915/lt_phy: Add xe3plpd .update_active_dpll hook Add .update_active_dpll function pointer to support dpll framework for xe3plpd platform. Reuse ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-10-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index af2613eeaf92..c1ed44b23bba 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4655,6 +4655,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .compute_dplls = xe3plpd_compute_dplls, .get_dplls = mtl_get_dplls, .put_dplls = icl_put_dplls, + .update_active_dpll = icl_update_active_dpll, }; /** From 658b554a8b0c6e8306d11f1e074e8445dc002d54 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:43 +0000 Subject: [PATCH 187/712] drm/i915/lt_phy: Add xe3plpd .update_dpll_ref_clks hook Add .update_dpll_ref_clks function pointer to xe3plpd platform to support dpll framework. Reuse ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-11-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index c1ed44b23bba..b50f02303356 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4656,6 +4656,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .get_dplls = mtl_get_dplls, .put_dplls = icl_put_dplls, .update_active_dpll = icl_update_active_dpll, + .update_ref_clks = icl_update_dpll_ref_clks, }; /** From a8acd1a61a03191c6426e2bcd2f0b3b576d0d789 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:44 +0000 Subject: [PATCH 188/712] drm/i915/lt_phy: Add xe3plpd .dump_hw_state hook Add .dump_hw_state function pointer for xe3plpd platform to support dpll framework. While at it, switch to use drm_printer structure to print hw state information. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-12-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 5 ++--- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 7 +++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 16 ++++++++-------- drivers/gpu/drm/i915/display/intel_lt_phy.h | 3 ++- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 882db77c0bbc..dfc28af1ef88 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5070,15 +5070,14 @@ pipe_config_lt_phy_pll_mismatch(struct drm_printer *p, bool fastset, const struct intel_lt_phy_pll_state *a, const struct intel_lt_phy_pll_state *b) { - struct intel_display *display = to_intel_display(crtc); char *chipname = "LTPHY"; pipe_config_mismatch(p, fastset, crtc, name, chipname); drm_printf(p, "expected:\n"); - intel_lt_phy_dump_hw_state(display, a); + intel_lt_phy_dump_hw_state(p, a); drm_printf(p, "found:\n"); - intel_lt_phy_dump_hw_state(display, b); + intel_lt_phy_dump_hw_state(p, b); } bool diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index b50f02303356..26b78063dd94 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4649,6 +4649,12 @@ static int xe3plpd_compute_dplls(struct intel_atomic_state *state, return xe3plpd_compute_non_tc_phy_dpll(state, crtc, encoder); } +static void xe3plpd_dump_hw_state(struct drm_printer *p, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + intel_lt_phy_dump_hw_state(p, &dpll_hw_state->ltpll); +} + __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, @@ -4657,6 +4663,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .put_dplls = icl_put_dplls, .update_active_dpll = icl_update_active_dpll, .update_ref_clks = icl_update_dpll_ref_clks, + .dump_hw_state = xe3plpd_dump_hw_state, }; /** diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index ed5cfd6641b1..e9a0631a2012 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2165,23 +2165,23 @@ void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, intel_lt_phy_transaction_end(encoder, wakeref); } -void intel_lt_phy_dump_hw_state(struct intel_display *display, +void intel_lt_phy_dump_hw_state(struct drm_printer *p, const struct intel_lt_phy_pll_state *hw_state) { int i, j; - drm_dbg_kms(display->drm, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", - hw_state->ssc_enabled, hw_state->tbt_mode); + drm_printf(p, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", + hw_state->ssc_enabled, hw_state->tbt_mode); for (i = 0; i < 3; i++) { - drm_dbg_kms(display->drm, "config[%d] = 0x%.4x,\n", - i, hw_state->config[i]); + drm_printf(p, "config[%d] = 0x%.4x,\n", + i, hw_state->config[i]); } for (i = 0; i <= 12; i++) for (j = 3; j >= 0; j--) - drm_dbg_kms(display->drm, "vdr_data[%d][%d] = 0x%.4x,\n", - i, j, hw_state->data[i][j]); + drm_printf(p, "vdr_data[%d][%d] = 0x%.4x,\n", + i, j, hw_state->data[i][j]); } bool @@ -2336,7 +2336,7 @@ static void intel_lt_phy_pll_verify_clock(struct intel_display *display, drm_printf(&p, "PLL state %s (%s):\n", pll_state_name, is_precomputed_state ? "precomputed" : "computed"); - intel_lt_phy_dump_hw_state(display, pll_state); + intel_lt_phy_dump_hw_state(&p, pll_state); } static void intel_lt_phy_pll_verify_params(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index 61ec0e5d8888..b208bbd6f8ca 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -8,6 +8,7 @@ #include +struct drm_printer; struct intel_atomic_state; struct intel_display; struct intel_encoder; @@ -26,7 +27,7 @@ int intel_lt_phy_calc_port_clock(struct intel_display *display, const struct intel_lt_phy_pll_state *lt_state); void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); -void intel_lt_phy_dump_hw_state(struct intel_display *display, +void intel_lt_phy_dump_hw_state(struct drm_printer *p, const struct intel_lt_phy_pll_state *hw_state); bool intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, From 37115f773c8c16eca0dbcb9399aa435111a7b34e Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:45 +0000 Subject: [PATCH 189/712] drm/i915/lt_phy: Add xe3plpd .compare_hw_state hook Add .compare_hw_state function pointer for xe3plpd platform to support dpll framework. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-13-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 26b78063dd94..c1d7d9909544 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4655,6 +4655,15 @@ static void xe3plpd_dump_hw_state(struct drm_printer *p, intel_lt_phy_dump_hw_state(p, &dpll_hw_state->ltpll); } +static bool xe3plpd_compare_hw_state(const struct intel_dpll_hw_state *_a, + const struct intel_dpll_hw_state *_b) +{ + const struct intel_lt_phy_pll_state *a = &_a->ltpll; + const struct intel_lt_phy_pll_state *b = &_b->ltpll; + + return intel_lt_phy_pll_compare_hw_state(a, b); +} + __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, @@ -4664,6 +4673,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .update_active_dpll = icl_update_active_dpll, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = xe3plpd_dump_hw_state, + .compare_hw_state = xe3plpd_compare_hw_state, }; /** From c62ba60b1098ff05528f13e75eca811b13e2c0c5 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:46 +0000 Subject: [PATCH 190/712] drm/i915/lt_phy: Add xe3plpd .get_hw_state hook Add .get_hw_state hook to xe3plpd platform for dpll framework and update intel_lt_phy_pll_readout_hw_state() function accordingly to support dpll framework. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-14-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 +++++++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 11 ++++++----- drivers/gpu/drm/i915/display/intel_lt_phy.h | 3 +-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 7f1576bfe4b0..dbf3f344e014 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4248,7 +4248,7 @@ static void xe3plpd_ddi_get_config(struct intel_encoder *encoder, { struct intel_display *display = to_intel_display(encoder); - intel_lt_phy_pll_readout_hw_state(encoder, crtc_state, &crtc_state->dpll_hw_state.ltpll); + intel_lt_phy_pll_readout_hw_state(encoder, &crtc_state->dpll_hw_state.ltpll); if (crtc_state->dpll_hw_state.ltpll.tbt_mode) crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index c1d7d9909544..6502916793f5 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4571,7 +4571,20 @@ static const struct intel_dpll_mgr mtl_pll_mgr = { .compare_hw_state = mtl_compare_hw_state, }; +static bool xe3plpd_pll_get_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (!encoder) + return false; + + return intel_lt_phy_pll_readout_hw_state(encoder, &dpll_hw_state->ltpll); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { + .get_hw_state = xe3plpd_pll_get_hw_state, }; static const struct dpll_info xe3plpd_plls[] = { diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index e9a0631a2012..1760e1a732dc 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2213,8 +2213,7 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) XELPDP_LANE_PCLK_PLL_ACK(0); } -void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state, +bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state) { u8 owned_lane_mask; @@ -2223,11 +2222,11 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, int i, j, k; if (!intel_lt_phy_pll_is_enabled(encoder)) - return; + return false; pll_state->tbt_mode = intel_tc_port_in_tbt_alt_mode(enc_to_dig_port(encoder)); if (pll_state->tbt_mode) - return; + return false; owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1; @@ -2245,6 +2244,8 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, } intel_lt_phy_transaction_end(encoder, wakeref); + + return true; } void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, @@ -2270,7 +2271,7 @@ void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, return; encoder = intel_get_crtc_new_encoder(state, new_crtc_state); - intel_lt_phy_pll_readout_hw_state(encoder, new_crtc_state, &pll_hw_state); + intel_lt_phy_pll_readout_hw_state(encoder, &pll_hw_state); dig_port = enc_to_dig_port(encoder); if (intel_tc_port_in_tbt_alt_mode(dig_port)) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index b208bbd6f8ca..0053bb5489e5 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -32,8 +32,7 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, bool intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, const struct intel_lt_phy_pll_state *b); -void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state, +bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state); void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, struct intel_crtc *crtc); From ff684c7eaca3da6f4436ef268da9ded7d0c4c4d4 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:47 +0000 Subject: [PATCH 191/712] drm/i915/lt_phy: Add xe3plpd .get_freq hook Add .get_freq function hook to support dpll framework for xe3plpd platform. v2: Restore port clock calculation (Suraj) Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-15-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 6502916793f5..412582e29ca6 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4583,8 +4583,21 @@ static bool xe3plpd_pll_get_hw_state(struct intel_display *display, return intel_lt_phy_pll_readout_hw_state(encoder, &dpll_hw_state->ltpll); } +static int xe3plpd_pll_get_freq(struct intel_display *display, + const struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return -EINVAL; + + return intel_lt_phy_calc_port_clock(display, &dpll_hw_state->ltpll); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { .get_hw_state = xe3plpd_pll_get_hw_state, + .get_freq = xe3plpd_pll_get_freq, }; static const struct dpll_info xe3plpd_plls[] = { From 27d911f87f96cd55461acf2468527f98eefce3ed Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:48 +0000 Subject: [PATCH 192/712] drm/i915/lt_phy: Add xe3plpd .crtc_get_dpll Add .crtc_get_dpll function pointer to support xe3plpd platform. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-16-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 147baa777856..e13a5e12109d 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1696,6 +1696,7 @@ static int i8xx_crtc_compute_clock(struct intel_atomic_state *state, static const struct intel_dpll_global_funcs xe3plpd_dpll_funcs = { .crtc_compute_clock = xe3plpd_crtc_compute_clock, + .crtc_get_dpll = hsw_crtc_get_dpll, }; static const struct intel_dpll_global_funcs mtl_dpll_funcs = { From 5ec58d714935c2671ffab58d8ac23b9224b66936 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:49 +0000 Subject: [PATCH 193/712] drm/i915/lt_phy: Add .enable_clock hook on DDI Enable PLL clock on DDI by moving part of the PLL enabling sequence into a DDI clock enabling function. v2: Reuse intel_mtl_pll_enable_clock for DDI clock enabling Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-17-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 ++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 32 ++++++++----------- drivers/gpu/drm/i915/display/intel_lt_phy.h | 8 +++-- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index dbf3f344e014..93f62d995e96 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -5298,7 +5298,7 @@ void intel_ddi_init(struct intel_display *display, encoder->pipe_mask = ~0; if (HAS_LT_PHY(display)) { - encoder->enable_clock = intel_xe3plpd_pll_enable; + encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_xe3plpd_pll_disable; encoder->port_pll_type = intel_mtl_port_pll_type; encoder->get_config = xe3plpd_ddi_get_config; diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 412582e29ca6..54c7a255b3a5 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4595,7 +4595,20 @@ static int xe3plpd_pll_get_freq(struct intel_display *display, return intel_lt_phy_calc_port_clock(display, &dpll_hw_state->ltpll); } +static void xe3plpd_pll_enable(struct intel_display *display, + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_xe3plpd_pll_enable(encoder, pll, dpll_hw_state); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { + .enable = xe3plpd_pll_enable, .get_hw_state = xe3plpd_pll_get_hw_state, .get_freq = xe3plpd_pll_get_freq, }; diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 1760e1a732dc..dfcff3d6ad33 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1901,9 +1901,11 @@ intel_lt_phy_enable_disable_tx(struct intel_encoder *encoder, } void intel_lt_phy_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) { struct intel_display *display = to_intel_display(encoder); + int port_clock = intel_lt_phy_calc_port_clock(display, &dpll_hw_state->ltpll); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); bool lane_reversal = dig_port->lane_reversal; u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); @@ -1919,11 +1921,11 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, wakeref = intel_lt_phy_transaction_begin(encoder); /* 1. Enable MacCLK at default 162 MHz frequency. */ - intel_lt_phy_lane_reset(encoder, crtc_state->lane_count); + intel_lt_phy_lane_reset(encoder, dpll_hw_state->ltpll.lane_count); /* 2. Program PORT_CLOCK_CTL register to configure clock muxes, gating, and SSC. */ - intel_lt_phy_program_port_clock_ctl(encoder, &crtc_state->dpll_hw_state.ltpll, - crtc_state->port_clock, lane_reversal); + intel_lt_phy_program_port_clock_ctl(encoder, &dpll_hw_state->ltpll, + port_clock, lane_reversal); /* 3. Change owned PHY lanes power to Ready state. */ intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, @@ -1933,13 +1935,12 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * 4. Read the PHY message bus VDR register PHY_VDR_0_Config check enabled PLL type, * encoded rate and encoded mode. */ - if (intel_lt_phy_config_changed(encoder, &crtc_state->dpll_hw_state.ltpll, - crtc_state->port_clock)) { + if (intel_lt_phy_config_changed(encoder, &dpll_hw_state->ltpll, port_clock)) { /* * 5. Program the PHY internal PLL registers over PHY message bus for the desired * frequency and protocol type */ - intel_lt_phy_program_pll(encoder, &crtc_state->dpll_hw_state.ltpll); + intel_lt_phy_program_pll(encoder, &dpll_hw_state->ltpll); /* 6. Use the P2P transaction flow */ /* @@ -1971,8 +1972,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * Change. We handle this step in bxt_set_cdclk(). */ /* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */ - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), - crtc_state->port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); /* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port), @@ -2019,7 +2019,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, lane_phy_pulse_status, lane_phy_pulse_status); } else { - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), crtc_state->port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); } /* @@ -2030,7 +2030,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, XELPDP_P0_STATE_ACTIVE); - intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll); + intel_lt_phy_enable_disable_tx(encoder, &dpll_hw_state->ltpll); intel_lt_phy_transaction_end(encoder, wakeref); } @@ -2288,14 +2288,10 @@ void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, } void intel_xe3plpd_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) { - struct intel_digital_port *dig_port = enc_to_dig_port(encoder); - - if (intel_tc_port_in_tbt_alt_mode(dig_port)) - intel_mtl_tbt_pll_enable_clock(encoder, crtc_state->port_clock); - else - intel_lt_phy_pll_enable(encoder, crtc_state); + intel_lt_phy_pll_enable(encoder, pll, dpll_hw_state); } void intel_xe3plpd_pll_disable(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index 0053bb5489e5..d8d5c2064b6b 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -11,13 +11,16 @@ struct drm_printer; struct intel_atomic_state; struct intel_display; +struct intel_dpll; +struct intel_dpll_hw_state; struct intel_encoder; struct intel_crtc_state; struct intel_crtc; struct intel_lt_phy_pll_state; void intel_lt_phy_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state); + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state); void intel_lt_phy_pll_disable(struct intel_encoder *encoder); int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, @@ -40,7 +43,8 @@ int intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state, u32 frequency_khz); void intel_xe3plpd_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state); + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state); void intel_xe3plpd_pll_disable(struct intel_encoder *encoder); void intel_lt_phy_verify_plls(struct intel_display *display); From cfe8715427dad345b3ce78096081354201ad18af Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:50 +0000 Subject: [PATCH 194/712] drm/i915/lt_phy: Add .disable_clock hook on DDI Add new pll_disable_clock functions so that they can be hooked up to dpll->disable. This is just a wrapper over the exitisting intel_xe3plpd_pll_disable to make it compatible With dpll->disable function v2: Revise commit message (Suraj) Drop wrapper for TBT clock disabling and reuse intel_mtl_pll_disable_clock() for DDI clock disabling hook (Suraj) Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-18-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 93f62d995e96..3cc8c681c352 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -5299,7 +5299,7 @@ void intel_ddi_init(struct intel_display *display, if (HAS_LT_PHY(display)) { encoder->enable_clock = intel_mtl_pll_enable_clock; - encoder->disable_clock = intel_xe3plpd_pll_disable; + encoder->disable_clock = intel_mtl_pll_disable_clock; encoder->port_pll_type = intel_mtl_port_pll_type; encoder->get_config = xe3plpd_ddi_get_config; } else if (DISPLAY_VER(display) >= 14) { diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 54c7a255b3a5..28c560417409 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4607,8 +4607,20 @@ static void xe3plpd_pll_enable(struct intel_display *display, intel_xe3plpd_pll_enable(encoder, pll, dpll_hw_state); } +static void xe3plpd_pll_disable(struct intel_display *display, + struct intel_dpll *pll) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_xe3plpd_pll_disable(encoder); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { .enable = xe3plpd_pll_enable, + .disable = xe3plpd_pll_disable, .get_hw_state = xe3plpd_pll_get_hw_state, .get_freq = xe3plpd_pll_get_freq, }; From 7049d9a773f3ec8a15eb873e2017ed5287b8c96c Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:51 +0000 Subject: [PATCH 195/712] drm/i915/lt_phy: Dump lane count for HW state To increase debuggability add lane count as part of HW state dump. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-19-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index dfcff3d6ad33..62719082efda 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2170,8 +2170,8 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, { int i, j; - drm_printf(p, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", - hw_state->ssc_enabled, hw_state->tbt_mode); + drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %d, tbt mode: %d\n", + hw_state->lane_count, hw_state->ssc_enabled, hw_state->tbt_mode); for (i = 0; i < 3; i++) { drm_printf(p, "config[%d] = 0x%.4x,\n", From ef5aa934a3c962fcd29b41926d41a86d8274239f Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:52 +0000 Subject: [PATCH 196/712] drm/i915/lt_phy: Readout lane count Readout lane count back from HW. Reuse existing function for Cx0 for LT PHY case with minor modification to add lanes as function parameters. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-20-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 10 ++++++---- drivers/gpu/drm/i915/display/intel_cx0_phy.h | 1 + drivers/gpu/drm/i915/display/intel_lt_phy.c | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 6a471c021c0e..7e59409bbf01 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2180,7 +2180,7 @@ static int intel_c10pll_calc_state(const struct intel_crtc_state *crtc_state, return 0; } -static int readout_enabled_lane_count(struct intel_encoder *encoder) +int intel_readout_lane_count(struct intel_encoder *encoder, int lane0, int lane1) { struct intel_display *display = to_intel_display(encoder); u8 enabled_tx_lane_count = 0; @@ -2212,7 +2212,7 @@ static int readout_enabled_lane_count(struct intel_encoder *encoder) max_tx_lane_count = round_up(max_tx_lane_count, 2); for (tx_lane = 0; tx_lane < max_tx_lane_count; tx_lane++) { - u8 phy_lane_mask = tx_lane < 2 ? INTEL_CX0_LANE0 : INTEL_CX0_LANE1; + u8 phy_lane_mask = tx_lane < 2 ? lane0 : lane1; int tx = tx_lane % 2 + 1; u8 val; @@ -2252,7 +2252,8 @@ static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, */ intel_c10_msgbus_access_begin(encoder, lane); - cx0pll_state->lane_count = readout_enabled_lane_count(encoder); + cx0pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_CX0_LANE0, + INTEL_CX0_LANE1); for (i = 0; i < ARRAY_SIZE(pll_state->pll); i++) pll_state->pll[i] = intel_cx0_read(encoder, lane, PHY_C10_VDR_PLL(i)); @@ -2707,7 +2708,8 @@ static void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, wakeref = intel_cx0_phy_transaction_begin(encoder); - cx0pll_state->lane_count = readout_enabled_lane_count(encoder); + cx0pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_CX0_LANE0, + INTEL_CX0_LANE1); /* 1. Read VDR params and current context selection */ intel_c20_readout_vdr_params(encoder, &pll_state->vdr, &cntx); diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 1d4480b8bf39..1428e7a5a318 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -28,6 +28,7 @@ struct intel_hdmi; void intel_cx0_clear_response_ready_flag(struct intel_encoder *encoder, int lane); bool intel_encoder_is_c10phy(struct intel_encoder *encoder); +int intel_readout_lane_count(struct intel_encoder *encoder, int lane0, int lane1); void intel_mtl_pll_enable(struct intel_encoder *encoder, struct intel_dpll *pll, const struct intel_dpll_hw_state *dpll_hw_state); diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 62719082efda..e9fce044b99d 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2232,6 +2232,8 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1; wakeref = intel_lt_phy_transaction_begin(encoder); + pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_LT_PHY_LANE0, + INTEL_LT_PHY_LANE1); pll_state->config[0] = intel_lt_phy_read(encoder, lane, LT_PHY_VDR_0_CONFIG); pll_state->config[1] = intel_lt_phy_read(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_1_CONFIG); pll_state->config[2] = intel_lt_phy_read(encoder, lane, LT_PHY_VDR_2_CONFIG); From c071495ccd89955ca6c54608bd8d5dc31574ed0a Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:53 +0000 Subject: [PATCH 197/712] drm/i915/lt_phy: Get encoder configuration for xe3plpd platform Reuse mtl_ddi_*_get_config functions now that all hooks are in place. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-21-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 3cc8c681c352..fe024250d350 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4243,21 +4243,6 @@ void intel_ddi_get_clock(struct intel_encoder *encoder, &crtc_state->dpll_hw_state); } -static void xe3plpd_ddi_get_config(struct intel_encoder *encoder, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(encoder); - - intel_lt_phy_pll_readout_hw_state(encoder, &crtc_state->dpll_hw_state.ltpll); - - if (crtc_state->dpll_hw_state.ltpll.tbt_mode) - crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder); - else - crtc_state->port_clock = - intel_lt_phy_calc_port_clock(display, &crtc_state->dpll_hw_state.ltpll); - intel_ddi_get_config(encoder, crtc_state); -} - static bool icl_ddi_tc_pll_is_tbt(const struct intel_dpll *pll) { return pll->info->id == DPLL_ID_ICL_TBTPLL; @@ -5301,7 +5286,10 @@ void intel_ddi_init(struct intel_display *display, encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_mtl_pll_disable_clock; encoder->port_pll_type = intel_mtl_port_pll_type; - encoder->get_config = xe3plpd_ddi_get_config; + if (intel_encoder_is_tc(encoder)) + encoder->get_config = mtl_ddi_tc_phy_get_config; + else + encoder->get_config = mtl_ddi_non_tc_phy_get_config; } else if (DISPLAY_VER(display) >= 14) { encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_mtl_pll_disable_clock; From a60d70847c5badbe624b0a6a175448ed4ad1073f Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:54 +0000 Subject: [PATCH 198/712] drm/i915/lt_phy: Add xe3plpd Thunderbolt PLL hooks Add the PLL hooks for the TBT PLL on xe3plpd. These are simple stubs similar to the TBT PLL on earlier platforms, since this PLL is always on from the display POV - so no PLL enable/disable programming is required as opposed to the non-TBT PLLs - and the clocks for different link rates are enabled/disabled at a different level, via the intel_encoder::enable_clock()/disable_clock() interface. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-22-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 +++++++++++-- drivers/gpu/drm/i915/display/intel_lt_phy.c | 18 ++++++++++++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.h | 4 ++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 28c560417409..534cc691979f 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4618,6 +4618,13 @@ static void xe3plpd_pll_disable(struct intel_display *display, intel_xe3plpd_pll_disable(encoder); } +static const struct intel_dpll_funcs xe3plpd_tbt_pll_funcs = { + .enable = mtl_tbt_pll_enable, + .disable = mtl_tbt_pll_disable, + .get_hw_state = intel_lt_phy_tbt_pll_readout_hw_state, + .get_freq = mtl_tbt_pll_get_freq, +}; + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { .enable = xe3plpd_pll_enable, .disable = xe3plpd_pll_disable, @@ -4628,7 +4635,8 @@ static const struct intel_dpll_funcs xe3plpd_pll_funcs = { static const struct dpll_info xe3plpd_plls[] = { { .name = "DPLL 0", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, { .name = "DPLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, - /* TODO: Add TBT */ + { .name = "TBT PLL", .funcs = &xe3plpd_tbt_pll_funcs, .id = DPLL_ID_ICL_TBTPLL, + .is_alt_port_dpll = true, .always_on = true }, { .name = "TC PLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL1, }, { .name = "TC PLL 2", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL2, }, { .name = "TC PLL 3", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL3, }, @@ -4671,7 +4679,8 @@ static int xe3plpd_compute_tc_phy_dplls(struct intel_atomic_state *state, struct icl_port_dpll *port_dpll; int ret; - /* TODO: Add state calculation for TBT PLL */ + port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + intel_lt_phy_tbt_pll_calc_state(&port_dpll->hw_state); port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_MG_PHY]; ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &port_dpll->hw_state); diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index e9fce044b99d..dd8b99f8821e 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1784,6 +1784,13 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, return -EINVAL; } +void intel_lt_phy_tbt_pll_calc_state(struct intel_dpll_hw_state *hw_state) +{ + memset(hw_state, 0, sizeof(*hw_state)); + + hw_state->ltpll.tbt_mode = true; +} + static void intel_lt_phy_program_pll(struct intel_encoder *encoder, const struct intel_lt_phy_pll_state *ltpll) @@ -2213,6 +2220,17 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) XELPDP_LANE_PCLK_PLL_ACK(0); } +bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *hw_state) +{ + memset(hw_state, 0, sizeof(*hw_state)); + + hw_state->ltpll.tbt_mode = true; + + return true; +} + bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state) { diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index d8d5c2064b6b..147ae431713d 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -26,6 +26,7 @@ int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder, struct intel_dpll_hw_state *hw_state); +void intel_lt_phy_tbt_pll_calc_state(struct intel_dpll_hw_state *hw_state); int intel_lt_phy_calc_port_clock(struct intel_display *display, const struct intel_lt_phy_pll_state *lt_state); void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, @@ -35,6 +36,9 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, bool intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, const struct intel_lt_phy_pll_state *b); +bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *hw_state); bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state); void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, From 35349695ada2d4888a267afbe1f920c069264aa5 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:55 +0000 Subject: [PATCH 199/712] drm/i915/lt_phy: Remove LT PHY specific state verification Remove LT PHY specific state verification as DPLL framework has state verification check. v2: Reuse intel_lt_phy_pll_compare_hw_state() as only config[0] and config[0] parameters are reliable with LT PHY (Suraj) v3: Rephrase handling of LT PHY case when verifying the state (CI) v4: Fix checkpatch warning of line length exceeding 100 columns Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-23-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 16 ++++++-- drivers/gpu/drm/i915/display/intel_lt_phy.c | 39 ------------------- drivers/gpu/drm/i915/display/intel_lt_phy.h | 2 - .../drm/i915/display/intel_modeset_verify.c | 1 - 4 files changed, 13 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 534cc691979f..421767dd8367 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -5075,6 +5075,7 @@ verify_single_dpll_state(struct intel_display *display, const struct intel_crtc_state *new_crtc_state) { struct intel_dpll_hw_state dpll_hw_state = {}; + bool pll_mismatch = false; u8 pipe_mask; bool active; @@ -5116,9 +5117,18 @@ verify_single_dpll_state(struct intel_display *display, "%s: pll enabled crtcs mismatch (expected 0x%x in 0x%x)\n", pll->info->name, pipe_mask, pll->state.pipe_mask); - if (INTEL_DISPLAY_STATE_WARN(display, - pll->on && memcmp(&pll->state.hw_state, &dpll_hw_state, - sizeof(dpll_hw_state)), + if (pll->on) { + const struct intel_dpll_mgr *dpll_mgr = display->dpll.mgr; + + if (HAS_LT_PHY(display)) + pll_mismatch = !dpll_mgr->compare_hw_state(&pll->state.hw_state, + &dpll_hw_state); + else + pll_mismatch = memcmp(&pll->state.hw_state, &dpll_hw_state, + sizeof(dpll_hw_state)); + } + + if (INTEL_DISPLAY_STATE_WARN(display, pll_mismatch, "%s: pll hw state mismatch\n", pll->info->name)) { struct drm_printer p = drm_dbg_printer(display->drm, DRM_UT_KMS, NULL); diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index dd8b99f8821e..5bbbc6182861 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2268,45 +2268,6 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, return true; } -void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct intel_display *display = to_intel_display(state); - struct intel_digital_port *dig_port; - const struct intel_crtc_state *new_crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_encoder *encoder; - struct intel_lt_phy_pll_state pll_hw_state = {}; - const struct intel_lt_phy_pll_state *pll_sw_state = &new_crtc_state->dpll_hw_state.ltpll; - - if (DISPLAY_VER(display) < 35) - return; - - if (!new_crtc_state->hw.active) - return; - - /* intel_get_crtc_new_encoder() only works for modeset/fastset commits */ - if (!intel_crtc_needs_modeset(new_crtc_state) && - !intel_crtc_needs_fastset(new_crtc_state)) - return; - - encoder = intel_get_crtc_new_encoder(state, new_crtc_state); - intel_lt_phy_pll_readout_hw_state(encoder, &pll_hw_state); - - dig_port = enc_to_dig_port(encoder); - if (intel_tc_port_in_tbt_alt_mode(dig_port)) - return; - - INTEL_DISPLAY_STATE_WARN(display, pll_hw_state.config[0] != pll_sw_state->config[0], - "[CRTC:%d:%s] mismatch in LT PHY PLL CONFIG 0: (expected 0x%04x, found 0x%04x)", - crtc->base.base.id, crtc->base.name, - pll_sw_state->config[0], pll_hw_state.config[0]); - INTEL_DISPLAY_STATE_WARN(display, pll_hw_state.config[2] != pll_sw_state->config[2], - "[CRTC:%d:%s] mismatch in LT PHY PLL CONFIG 2: (expected 0x%04x, found 0x%04x)", - crtc->base.base.id, crtc->base.name, - pll_sw_state->config[2], pll_hw_state.config[2]); -} - void intel_xe3plpd_pll_enable(struct intel_encoder *encoder, struct intel_dpll *pll, const struct intel_dpll_hw_state *dpll_hw_state) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index 147ae431713d..16de39484779 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -41,8 +41,6 @@ bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, struct intel_dpll_hw_state *hw_state); bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state); -void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, - struct intel_crtc *crtc); int intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state, u32 frequency_khz); diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c index 12a00121c274..2ec17c2bfe0f 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c @@ -246,7 +246,6 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state, verify_crtc_state(state, crtc); intel_dpll_state_verify(state, crtc); intel_mpllb_state_verify(state, crtc); - intel_lt_phy_pll_state_verify(state, crtc); } void intel_modeset_verify_disabled(struct intel_atomic_state *state) From 29b37427cdc6a9cc37b060e9e9eea90965936bb5 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 10:14:15 +0000 Subject: [PATCH 200/712] drm/i915/lt_phy: Enable dpll framework for xe3plpd xe3plpd platform is supported by dpll framework remove a separate check for hw comparison and rely solely on dpll framework hw comparison. Finally, all required hooks are now in place so initialize PLL manager for xe3plpd platform and remove the redirections to the legacy code paths for clock enable/disable as well as state mismatch checks that are no longer needed. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312101415.2669387-1-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_display.c | 31 ------------------- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 7 +++-- drivers/gpu/drm/i915/display/intel_lt_phy.c | 1 + 4 files changed, 6 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index fe024250d350..ebefa889bc8c 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -5285,7 +5285,7 @@ void intel_ddi_init(struct intel_display *display, if (HAS_LT_PHY(display)) { encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_mtl_pll_disable_clock; - encoder->port_pll_type = intel_mtl_port_pll_type; + encoder->port_pll_type = icl_ddi_tc_port_pll_type; if (intel_encoder_is_tc(encoder)) encoder->get_config = mtl_ddi_tc_phy_get_config; else diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index dfc28af1ef88..10b6c6fcb03f 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5063,23 +5063,6 @@ static bool allow_vblank_delay_fastset(const struct intel_crtc_state *old_crtc_s !intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DSI); } -static void -pipe_config_lt_phy_pll_mismatch(struct drm_printer *p, bool fastset, - const struct intel_crtc *crtc, - const char *name, - const struct intel_lt_phy_pll_state *a, - const struct intel_lt_phy_pll_state *b) -{ - char *chipname = "LTPHY"; - - pipe_config_mismatch(p, fastset, crtc, name, chipname); - - drm_printf(p, "expected:\n"); - intel_lt_phy_dump_hw_state(p, a); - drm_printf(p, "found:\n"); - intel_lt_phy_dump_hw_state(p, b); -} - bool intel_pipe_config_compare(const struct intel_crtc_state *current_config, const struct intel_crtc_state *pipe_config, @@ -5194,16 +5177,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, } \ } while (0) -#define PIPE_CONF_CHECK_PLL_LT(name) do { \ - if (!intel_lt_phy_pll_compare_hw_state(¤t_config->name, \ - &pipe_config->name)) { \ - pipe_config_lt_phy_pll_mismatch(&p, fastset, crtc, __stringify(name), \ - ¤t_config->name, \ - &pipe_config->name); \ - ret = false; \ - } \ -} while (0) - #define PIPE_CONF_CHECK_TIMINGS(name) do { \ PIPE_CONF_CHECK_I(name.crtc_hdisplay); \ PIPE_CONF_CHECK_I(name.crtc_htotal); \ @@ -5430,10 +5403,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, if (display->dpll.mgr || HAS_GMCH(display)) PIPE_CONF_CHECK_PLL(dpll_hw_state); - /* FIXME convert MTL+ platforms over to dpll_mgr */ - if (HAS_LT_PHY(display)) - PIPE_CONF_CHECK_PLL_LT(dpll_hw_state.ltpll); - PIPE_CONF_CHECK_X(dsi_pll.ctrl); PIPE_CONF_CHECK_X(dsi_pll.div); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 421767dd8367..f5d4f7146fbc 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4724,7 +4724,6 @@ static bool xe3plpd_compare_hw_state(const struct intel_dpll_hw_state *_a, return intel_lt_phy_pll_compare_hw_state(a, b); } -__maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, .compute_dplls = xe3plpd_compute_dplls, @@ -4750,9 +4749,11 @@ void intel_dpll_init(struct intel_display *display) mutex_init(&display->dpll.lock); - if (DISPLAY_VER(display) >= 35 || display->platform.dg2) - /* No shared DPLLs on NVL or DG2; port PLLs are part of the PHY */ + if (display->platform.dg2) + /* No shared DPLLs on DG2; port PLLs are part of the PHY */ dpll_mgr = NULL; + else if (DISPLAY_VER(display) >= 35) + dpll_mgr = &xe3plpd_pll_mgr; else if (DISPLAY_VER(display) >= 14) dpll_mgr = &mtl_pll_mgr; else if (display->platform.alderlake_p) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 5bbbc6182861..657ad5cb0eff 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -11,6 +11,7 @@ #include "intel_ddi_buf_trans.h" #include "intel_de.h" #include "intel_display.h" +#include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dpll.h" From 968e9fd188488d1a4b6981f39554d4117468b6ee Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:57 +0000 Subject: [PATCH 201/712] drm/i915/lt_phy: Replace crtc compute clock The existing DPLL compute clock callback for the XE3PLPD platform (`xe3plpd_crtc_compute_clock`) was specific to that platform. Replace it with the more generic Haswell (`hsw_crtc_compute_clock`) implementation so that the compute clock path does not rely on the XE3PLPD hook. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-25-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll.c | 25 +---------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index e13a5e12109d..c7d37e74fbe9 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1212,29 +1212,6 @@ static int dg2_crtc_compute_clock(struct intel_atomic_state *state, return 0; } -static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct intel_crtc_state *crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_encoder *encoder = - intel_get_crtc_new_encoder(state, crtc_state); - struct intel_display *display = to_intel_display(encoder); - int ret; - - ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &crtc_state->dpll_hw_state); - if (ret) - return ret; - - /* TODO: Do the readback via intel_compute_shared_dplls() */ - crtc_state->port_clock = - intel_lt_phy_calc_port_clock(display, &crtc_state->dpll_hw_state.ltpll); - - crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state); - - return 0; -} - static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); @@ -1695,7 +1672,7 @@ static int i8xx_crtc_compute_clock(struct intel_atomic_state *state, } static const struct intel_dpll_global_funcs xe3plpd_dpll_funcs = { - .crtc_compute_clock = xe3plpd_crtc_compute_clock, + .crtc_compute_clock = hsw_crtc_compute_clock, .crtc_get_dpll = hsw_crtc_get_dpll, }; From ec66ec6a5a8f53e7c70085749e8d68f4431c630f Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Tue, 24 Mar 2026 11:33:24 +0200 Subject: [PATCH 202/712] wifi: iwlwifi: mld: Fix MLO scan timing Calculate MLO scan start time based on actual scan start notification from firmware instead of recording time when scan command is sent. Currently, MLO scan start time was captured immediately after sending the scan command to firmware. However, the actual scan start time may differ due to the FW being busy with a previous scan. In that case, the link selection code will think that the MLO scan is too old, and will warn. To fix it, Implement start scan notification handling to capture the precise moment when firmware begins the scan operation. Fixes: 9324731b9985 ("wifi: iwlwifi: mld: avoid selecting bad links") Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260324113316.4c56b8bac533.I6e656d8cc30bb82c96aabadedd62bd67f4c46bf9@changeid --- .../wireless/intel/iwlwifi/fw/api/commands.h | 5 ++++ .../net/wireless/intel/iwlwifi/fw/api/scan.h | 10 +++++++ drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 + drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 4 +-- .../net/wireless/intel/iwlwifi/mld/notif.c | 5 ++++ drivers/net/wireless/intel/iwlwifi/mld/scan.c | 30 +++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mld/scan.h | 9 ++++-- 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 8d64a271bb94..36159a769916 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -296,6 +296,11 @@ enum iwl_legacy_cmds { */ SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, + /** + * @SCAN_START_NOTIFICATION_UMAC: uses &struct iwl_umac_scan_start + */ + SCAN_START_NOTIFICATION_UMAC = 0xb2, + /** * @MATCH_FOUND_NOTIFICATION: scan match found */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 60f0a4924ddf..46fcc32608e3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -1156,6 +1156,16 @@ enum iwl_umac_scan_abort_status { IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND, }; +/** + * struct iwl_umac_scan_start - scan start notification + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @reserved: for future use + */ +struct iwl_umac_scan_start { + __le32 uid; + __le32 reserved; +} __packed; /* SCAN_START_UMAC_API_S_VER_1 */ + /** * struct iwl_umac_scan_complete - scan complete notification * @uid: scan id, &enum iwl_umac_scan_uid_offsets diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 495e9d8f3af6..9af79297c3b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -171,6 +171,7 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = { HCMD_NAME(MISSED_BEACONS_NOTIFICATION), HCMD_NAME(MAC_PM_POWER_TABLE), HCMD_NAME(MFUART_LOAD_NOTIFICATION), + HCMD_NAME(SCAN_START_NOTIFICATION_UMAC), HCMD_NAME(RSS_CONFIG_CMD), HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), HCMD_NAME(REPLY_RX_MPDU_CMD), diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index f842f5183223..fbff5915f7fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -739,7 +739,7 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld, /* Ignore any BSS that was not seen in the last MLO scan */ if (ktime_before(link_conf->bss->ts_boottime, - mld->scan.last_mlo_scan_time)) + mld->scan.last_mlo_scan_start_time)) continue; data[n_data].link_id = link_id; @@ -945,7 +945,7 @@ static void _iwl_mld_select_links(struct iwl_mld *mld, if (!mld_vif->authorized || hweight16(usable_links) <= 1) return; - if (WARN(ktime_before(mld->scan.last_mlo_scan_time, + if (WARN(ktime_before(mld->scan.last_mlo_scan_start_time, ktime_sub_ns(ktime_get_boottime_ns(), 5ULL * NSEC_PER_SEC)), "Last MLO scan was too long ago, can't select links\n")) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index 240526d8b632..9c88a8579a75 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -287,6 +287,8 @@ static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld, * at least enough bytes to cover the structure listed in the CMD_VER_ENTRY. */ +CMD_VERSIONS(scan_start_notif, + CMD_VER_ENTRY(1, iwl_umac_scan_start)) CMD_VERSIONS(scan_complete_notif, CMD_VER_ENTRY(1, iwl_umac_scan_complete)) CMD_VERSIONS(scan_iter_complete_notif, @@ -360,6 +362,7 @@ DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif, link_id) DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity) DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid) +DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid) DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif, mac_id) DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif, @@ -402,6 +405,8 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { RX_HANDLER_SYNC) RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif, RX_HANDLER_SYNC) + RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_START_NOTIFICATION_UMAC, + scan_start_notif) RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC, scan_complete_notif) RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index a1a4cf3ab3d3..abd4281b4b0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -473,6 +473,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld, params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN; + if (scan_status == IWL_MLD_SCAN_INT_MLO) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTF_START; + if (params->enable_6ghz_passive) flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN; @@ -1817,9 +1820,6 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld, ret = _iwl_mld_single_scan_start(mld, vif, req, &ies, IWL_MLD_SCAN_INT_MLO); - if (!ret) - mld->scan.last_mlo_scan_time = ktime_get_boottime_ns(); - IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret); } @@ -1904,6 +1904,30 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, ieee80211_sched_scan_results(mld->hw); } +void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_umac_scan_complete *notif = (void *)pkt->data; + u32 uid = le32_to_cpu(notif->uid); + + if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + + if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status), + "FW reports scan UID %d we didn't trigger\n", uid)) + return; + + IWL_DEBUG_SCAN(mld, "Scan started: uid=%u type=%u\n", uid, + mld->scan.uid_status[uid]); + if (IWL_FW_CHECK(mld, mld->scan.uid_status[uid] != IWL_MLD_SCAN_INT_MLO, + "FW reports scan start notification %d we didn't trigger\n", + mld->scan.uid_status[uid])) + return; + + mld->scan.last_mlo_scan_start_time = ktime_get_boottime_ns(); +} + void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h index 69110f0cfc8e..de5620e7f463 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h @@ -27,6 +27,9 @@ int iwl_mld_sched_scan_start(struct iwl_mld *mld, void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); @@ -114,8 +117,8 @@ enum iwl_mld_traffic_load { * in jiffies. * @last_start_time_jiffies: stores the last start time in jiffies * (interface up/reset/resume). - * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since - * boot. + * @last_mlo_scan_start_time: start time of the last MLO scan in nanoseconds + * since boot. */ struct iwl_mld_scan { /* Add here fields that need clean up on restart */ @@ -136,7 +139,7 @@ struct iwl_mld_scan { void *cmd; unsigned long last_6ghz_passive_jiffies; unsigned long last_start_time_jiffies; - u64 last_mlo_scan_time; + u64 last_mlo_scan_start_time; }; /** From 323156c3541e23da7e582008a7ac30cd51b60acd Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Mar 2026 11:33:25 +0200 Subject: [PATCH 203/712] wifi: iwlwifi: mvm: don't send a 6E related command when not supported MCC_ALLOWED_AP_TYPE_CMD is related to 6E support. Do not send it if the device doesn't support 6E. Apparently, the firmware is mistakenly advertising support for this command even on AX201 which does not support 6E and then the firmware crashes. Fixes: 0d2fc8821a7d ("wifi: iwlwifi: nvm: parse the VLP/AFC bit from regulatory") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220804 Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260324113316.e171f0163f2a.I0c444d1f82d1773054e7ffc391ad49697d58f44e@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 43cf94c9a36b..6cc78661116e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -470,7 +470,8 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; - if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210 || + !mvm->trans->cfg->uhb_supported) { IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n"); return; } From 687a95d204e72e52f2e6bc7a994cc82f76b2678f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Mar 2026 11:33:26 +0200 Subject: [PATCH 204/712] wifi: iwlwifi: mld: correctly set wifi generation data In each MAC context, the firmware expects the wifi generation data, i.e. whether or not HE/EHT (and in the future UHR) is enabled on that MAC. However, this is currently handled wrong in two ways: - EHT is only enabled when the interface is also an MLD, but we currently allow (despite the spec) connecting with EHT but without MLO. - when HE or EHT are used by TDLS peers, the firmware needs to have them enabled regardless of the AP Fix this by iterating setting up the data depending on the interface type: - for AP, just set it according to the BSS configuration - for monitor, set it according to HW capabilities - otherwise, particularly for client, iterate all stations and then their links on the interface in question and set according to their capabilities, this handles the AP and TDLS peers. Re-calculate this whenever a TDLS station is marked associated or removed so that it's kept updated, for the AP it's already updated on assoc/disassoc. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260319110722.404713b22177.Ic972b5e557d011a5438f8f97c1e793cc829e2ea9@changeid Link: https://patch.msgid.link/20260324093333.2953495-1-miriam.rachel.korenblit@intel.com --- .../net/wireless/intel/iwlwifi/mld/iface.c | 101 ++++++++++++------ .../net/wireless/intel/iwlwifi/mld/mac80211.c | 19 ++++ 2 files changed, 88 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 29df747c8938..9215fc7e2eca 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -111,14 +111,75 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld, IEEE80211_HE_MAC_CAP2_ACK_EN); } -static void iwl_mld_set_he_support(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct iwl_mac_config_cmd *cmd) +struct iwl_mld_mac_wifi_gen_sta_iter_data { + struct ieee80211_vif *vif; + struct iwl_mac_wifi_gen_support *support; +}; + +static void iwl_mld_mac_wifi_gen_sta_iter(void *_data, + struct ieee80211_sta *sta) { - if (vif->type == NL80211_IFTYPE_AP) - cmd->wifi_gen.he_ap_support = 1; - else - cmd->wifi_gen.he_support = 1; + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_mac_wifi_gen_sta_iter_data *data = _data; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + if (mld_sta->vif != data->vif) + return; + + for_each_sta_active_link(data->vif, sta, link_sta, link_id) { + if (link_sta->he_cap.has_he) + data->support->he_support = 1; + if (link_sta->eht_cap.has_eht) + data->support->eht_support = 1; + } +} + +static void iwl_mld_set_wifi_gen(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_wifi_gen_support *support) +{ + struct iwl_mld_mac_wifi_gen_sta_iter_data sta_iter_data = { + .vif = vif, + .support = support, + }; + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; + + switch (vif->type) { + case NL80211_IFTYPE_MONITOR: + /* for sniffer, set to HW capabilities */ + support->he_support = 1; + support->eht_support = mld->trans->cfg->eht_supported; + break; + case NL80211_IFTYPE_AP: + /* for AP set according to the link configs */ + for_each_vif_active_link(vif, link_conf, link_id) { + support->he_ap_support |= link_conf->he_support; + support->eht_support |= link_conf->eht_support; + } + break; + default: + /* + * If we have MLO enabled, then the firmware needs to enable + * address translation for the station(s) we add. That depends + * on having EHT enabled in firmware, which in turn depends on + * mac80211 in the iteration below. + * However, mac80211 doesn't enable capabilities on the AP STA + * until it has parsed the association response successfully, + * so set EHT (and HE as a pre-requisite for EHT) when the vif + * is an MLD. + */ + if (ieee80211_vif_is_mld(vif)) { + support->he_support = 1; + support->eht_support = 1; + } + + ieee80211_iterate_stations_mtx(mld->hw, + iwl_mld_mac_wifi_gen_sta_iter, + &sta_iter_data); + break; + } } /* fill the common part for all interface types */ @@ -128,8 +189,6 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, u32 action) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct ieee80211_bss_conf *link_conf; - unsigned int link_id; lockdep_assert_wiphy(mld->wiphy); @@ -147,29 +206,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, cmd->nic_not_ack_enabled = cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif)); - /* If we have MLO enabled, then the firmware needs to enable - * address translation for the station(s) we add. That depends - * on having EHT enabled in firmware, which in turn depends on - * mac80211 in the code below. - * However, mac80211 doesn't enable HE/EHT until it has parsed - * the association response successfully, so just skip all that - * and enable both when we have MLO. - */ - if (ieee80211_vif_is_mld(vif)) { - iwl_mld_set_he_support(mld, vif, cmd); - cmd->wifi_gen.eht_support = 1; - return; - } - - for_each_vif_active_link(vif, link_conf, link_id) { - if (!link_conf->he_support) - continue; - - iwl_mld_set_he_support(mld, vif, cmd); - - /* EHT, if supported, was already set above */ - break; - } + iwl_mld_set_wifi_gen(mld, vif, &cmd->wifi_gen); } static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 0c53d6bd9651..71a9a72c9ac0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1761,6 +1761,16 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, if (vif->type == NL80211_IFTYPE_STATION) iwl_mld_link_set_2mhz_block(mld, vif, sta); + + if (sta->tdls) { + /* + * update MAC since wifi generation flags may change, + * we also update MAC on association to the AP via the + * vif assoc change + */ + iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + } + /* Now the link_sta's capabilities are set, update the FW */ iwl_mld_config_tlc(mld, vif, sta); @@ -1873,6 +1883,15 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, /* just removed last TDLS STA, so enable PM */ iwl_mld_update_mac_power(mld, vif, false); } + + if (sta->tdls) { + /* + * update MAC since wifi generation flags may change, + * we also update MAC on disassociation to the AP via + * the vif assoc change + */ + iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + } } else { return -EINVAL; } From e225b36f83d7926c1f2035923bb0359d851fdb73 Mon Sep 17 00:00:00 2001 From: Reshma Immaculate Rajkumar Date: Thu, 19 Mar 2026 12:26:08 +0530 Subject: [PATCH 205/712] wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session During ongoing traffic, a request to stop an AMPDU session for one TID could incorrectly affect other active sessions. This can happen because an incorrect TID reference would be passed when updating the BA session state, causing the wrong session to be stopped. As a result, the affected session would be reduced to a minimal BA size, leading to a noticeable throughput degradation. Fix this issue by passing the correct argument from ath11k_dp_rx_ampdu_stop() to ath11k_peer_rx_tid_reo_update() during a stop AMPDU session. Instead of passing peer->tx_tid, which is the base address of the array, corresponding to TID 0; pass the value of &peer->rx_tid[params->tid], where the different TID numbers are accounted for. Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1 Fixes: d5c65159f2895 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Reshma Immaculate Rajkumar Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260319065608.2408179-1-reshma.rajkumar@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/dp_rx.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 49d959b2e148..85defe11750d 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -1110,9 +1110,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, struct ath11k_base *ab = ar->ab; struct ath11k_peer *peer; struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta); + struct dp_rx_tid *rx_tid; int vdev_id = arsta->arvif->vdev_id; - dma_addr_t paddr; - bool active; int ret; spin_lock_bh(&ab->base_lock); @@ -1124,15 +1123,14 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, return -ENOENT; } - paddr = peer->rx_tid[params->tid].paddr; - active = peer->rx_tid[params->tid].active; + rx_tid = &peer->rx_tid[params->tid]; - if (!active) { + if (!rx_tid->active) { spin_unlock_bh(&ab->base_lock); return 0; } - ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false); + ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, 1, 0, false); spin_unlock_bh(&ab->base_lock); if (ret) { ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n", @@ -1141,7 +1139,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, } ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, - params->sta->addr, paddr, + params->sta->addr, + rx_tid->paddr, params->tid, 1, 1); if (ret) ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n", From 4242625f272974dd1947f73b10d884eab3b277cd Mon Sep 17 00:00:00 2001 From: Reshma Immaculate Rajkumar Date: Fri, 27 Feb 2026 16:31:23 +0530 Subject: [PATCH 206/712] wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session With traffic ongoing for data TID [TID 0], an DELBA request to stop AMPDU for the BA session was received on management TID [TID 4]. The corresponding TID number was incorrectly passed to stop the BA session, resulting in the BA session for data TIDs being stopped and the BA size being reduced to 1, causing an overall dip in TCP throughput. Fix this issue by passing the correct argument from ath12k_dp_rx_ampdu_stop() to ath12k_dp_arch_peer_rx_tid_reo_update() during an AMPDU stop session. Instead of passing peer->dp_peer->rx_tid, which is the base address of the array, corresponding to TID 0, pass the value of &peer->dp_peer->rx_tid[params->tid]. With this, the different TID numbers are accounted for. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: Reshma Immaculate Rajkumar Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260227110123.3726354-1-reshma.rajkumar@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 5a82ede65dd3..244d5230a5bd 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -735,6 +735,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct ath12k_dp_link_peer *peer; struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta); + struct ath12k_dp_rx_tid *rx_tid; struct ath12k_link_sta *arsta; int vdev_id; bool active; @@ -770,7 +771,8 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, return 0; } - ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, peer->dp_peer->rx_tid, + rx_tid = &peer->dp_peer->rx_tid[params->tid]; + ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, rx_tid, 1, 0, false); spin_unlock_bh(&dp->dp_lock); if (ret) { From 658b3c963de79ce802cd3b68bd0694ceeab4f105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Mar 2026 13:10:26 +0200 Subject: [PATCH 207/712] drm/i915/de: Introduce intel_de.c and move intel_de_{read,write}8() there MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_de_{read,write}8() aren't performance critical so having them as static inline is pointless. Introduce intel_de.c and move the implementation there. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260313111028.25159-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/display/intel_de.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_de.h | 22 +++------------------- drivers/gpu/drm/xe/Makefile | 1 + 4 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 drivers/gpu/drm/i915/display/intel_de.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index be976a90c5a6..b677720a1c2d 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -254,6 +254,7 @@ i915-y += \ display/intel_crtc_state_dump.o \ display/intel_cursor.o \ display/intel_dbuf_bw.o \ + display/intel_de.o \ display/intel_display.o \ display/intel_display_conversion.o \ display/intel_display_driver.o \ diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c new file mode 100644 index 000000000000..5348c1d51eb8 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include + +#include "intel_de.h" + +u8 intel_de_read8(struct intel_display *display, i915_reg_t reg) +{ + /* this is only used on VGA registers (possible on pre-g4x) */ + drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); + + return intel_uncore_read8(__to_uncore(display), reg); +} + +void intel_de_write8(struct intel_display *display, i915_reg_t reg, u8 val) +{ + drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); + + intel_uncore_write8(__to_uncore(display), reg, val); +} diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h index f30f3f8ebee1..8ca5904ba84e 100644 --- a/drivers/gpu/drm/i915/display/intel_de.h +++ b/drivers/gpu/drm/i915/display/intel_de.h @@ -6,8 +6,6 @@ #ifndef __INTEL_DE_H__ #define __INTEL_DE_H__ -#include - #include "intel_display_core.h" #include "intel_dmc_wl.h" #include "intel_dsb.h" @@ -19,6 +17,9 @@ static inline struct intel_uncore *__to_uncore(struct intel_display *display) return to_intel_uncore(display->drm); } +u8 intel_de_read8(struct intel_display *display, i915_reg_t reg); +void intel_de_write8(struct intel_display *display, i915_reg_t reg, u8 val); + static inline u32 intel_de_read(struct intel_display *display, i915_reg_t reg) { @@ -33,23 +34,6 @@ intel_de_read(struct intel_display *display, i915_reg_t reg) return val; } -static inline u8 -intel_de_read8(struct intel_display *display, i915_reg_t reg) -{ - /* this is only used on VGA registers (possible on pre-g4x) */ - drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); - - return intel_uncore_read8(__to_uncore(display), reg); -} - -static inline void -intel_de_write8(struct intel_display *display, i915_reg_t reg, u8 val) -{ - drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); - - intel_uncore_write8(__to_uncore(display), reg, val); -} - static inline u64 intel_de_read64_2x32(struct intel_display *display, i915_reg_t lower_reg, i915_reg_t upper_reg) diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 468599492af1..35d611adbb83 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -249,6 +249,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_dbuf_bw.o \ i915-display/intel_ddi.o \ i915-display/intel_ddi_buf_trans.o \ + i915-display/intel_de.o \ i915-display/intel_display.o \ i915-display/intel_display_conversion.o \ i915-display/intel_display_device.o \ From 56d2a47e6b495e7d382d00b91ce182ff2c6a3741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Mar 2026 13:10:27 +0200 Subject: [PATCH 208/712] drm/i915/de: Move intel_de_wait*() into intel_de.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_de_wait*() end up doing quite a bit of stuff, so the one function call overhead from them seems insignificant. Move the implementation intel_de.c. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260313111028.25159-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_de.c | 72 ++++++++++++++++++ drivers/gpu/drm/i915/display/intel_de.h | 99 +++++-------------------- 2 files changed, 92 insertions(+), 79 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c index 5348c1d51eb8..fce92535bd6a 100644 --- a/drivers/gpu/drm/i915/display/intel_de.c +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -7,6 +7,78 @@ #include "intel_de.h" +int intel_de_wait_us(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value) +{ + int ret; + + intel_dmc_wl_get(display, reg); + + ret = __intel_wait_for_register(__to_uncore(display), reg, mask, + value, timeout_us, 0, out_value); + + intel_dmc_wl_put(display, reg); + + return ret; +} + +int intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value) +{ + int ret; + + intel_dmc_wl_get(display, reg); + + ret = __intel_wait_for_register(__to_uncore(display), reg, mask, + value, 2, timeout_ms, out_value); + + intel_dmc_wl_put(display, reg); + + return ret; +} + +int intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value) +{ + return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, + value, 2, timeout_ms, out_value); +} + +int intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value) +{ + return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, + value, timeout_us, 0, out_value); +} + +int intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us) +{ + return intel_de_wait_us(display, reg, mask, mask, timeout_us, NULL); +} + +int intel_de_wait_for_clear_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us) +{ + return intel_de_wait_us(display, reg, mask, 0, timeout_us, NULL); +} + +int intel_de_wait_for_set_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms) +{ + return intel_de_wait_ms(display, reg, mask, mask, timeout_ms, NULL); +} + +int intel_de_wait_for_clear_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms) +{ + return intel_de_wait_ms(display, reg, mask, 0, timeout_ms, NULL); +} + u8 intel_de_read8(struct intel_display *display, i915_reg_t reg) { /* this is only used on VGA registers (possible on pre-g4x) */ diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h index 8ca5904ba84e..f87b84ab9d6d 100644 --- a/drivers/gpu/drm/i915/display/intel_de.h +++ b/drivers/gpu/drm/i915/display/intel_de.h @@ -86,85 +86,26 @@ intel_de_rmw(struct intel_display *display, i915_reg_t reg, u32 clear, u32 set) return val; } -static inline int -intel_de_wait_us(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_us, - u32 *out_value) -{ - int ret; - - intel_dmc_wl_get(display, reg); - - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); - - intel_dmc_wl_put(display, reg); - - return ret; -} - -static inline int -intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_ms, - u32 *out_value) -{ - int ret; - - intel_dmc_wl_get(display, reg); - - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); - - intel_dmc_wl_put(display, reg); - - return ret; -} - -static inline int -intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_ms, - u32 *out_value) -{ - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); -} - -static inline int -intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_us, - u32 *out_value) -{ - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); -} - -static inline int -intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_us) -{ - return intel_de_wait_us(display, reg, mask, mask, timeout_us, NULL); -} - -static inline int -intel_de_wait_for_clear_us(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_us) -{ - return intel_de_wait_us(display, reg, mask, 0, timeout_us, NULL); -} - -static inline int -intel_de_wait_for_set_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_ms) -{ - return intel_de_wait_ms(display, reg, mask, mask, timeout_ms, NULL); -} - -static inline int -intel_de_wait_for_clear_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_ms) -{ - return intel_de_wait_ms(display, reg, mask, 0, timeout_ms, NULL); -} +int intel_de_wait_us(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value); +int intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value); +int intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value); +int intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value); +int intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us); +int intel_de_wait_for_clear_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us); +int intel_de_wait_for_set_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms); +int intel_de_wait_for_clear_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms); /* * Unlocked mmio-accessors, think carefully before using these. From ff854b32b604526100c8468f8915150bd4387288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 23 Mar 2026 11:43:04 +0200 Subject: [PATCH 209/712] drm/i915/de: Implement register polling in the display code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan is to move all the mmio stuff into the display code itself. As a first step implement the register polling in intel_de.c. Currently i915 and xe implement this stuff in slightly different ways, so there are some functional changes here. Try to go for a reasonable middle ground between the i915 and xe implementations: - the exponential backoff limit is the simpler approach taken by i915 (== just clamp the max sleep duration to 1 ms) - the fast vs. slow timeout handling is similar to i915 where we first try the fast timeout and then again the slow timeout if the condition still isn't satisfied. xe just adds up the timeouts together, which is a bit weird. - the atomic wait variant uses udelay() like xe, whereas i915 has no udelay()s in its atomic loop. As a compromise go for a fixed 1 usec delay for short waits, instead of the somewhat peculiar xe behaviour where it effectively just does one iteration of the loop. - keep the "use udelay() for < 10 usec waits" logic (which more or less mirrors fsleep()), but include an explicit might_sleep() even for these short waits when called from a non-atomic intel_de_wait*() function. This should prevent people from calling the non-atomic functions from the wrong place. Eventually we may want to switch over to poll_timeout*(), but that lacks the exponential backoff, so a bit too radical to change in one go. v2: Initialize ret in intel_de_wait_for_register() to avoid a warning from the compiler. This is actually a false positive since we always have fast_timeout_us!=0 when slow_timeout_us!=0, but the compiler can't see that Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260323094304.8171-1-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_de.c | 99 +++++++++++++++++-- .../drm/xe/compat-i915-headers/intel_uncore.h | 31 ------ 2 files changed, 91 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c index fce92535bd6a..d2a418da2d54 100644 --- a/drivers/gpu/drm/i915/display/intel_de.c +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -3,10 +3,85 @@ * Copyright © 2026 Intel Corporation */ +#include + #include #include "intel_de.h" +static int __intel_de_wait_for_register(struct intel_display *display, + i915_reg_t reg, u32 mask, u32 value, + unsigned int timeout_us, + u32 (*read)(struct intel_display *display, i915_reg_t reg), + u32 *out_val, bool is_atomic) +{ + const ktime_t end = ktime_add_us(ktime_get_raw(), timeout_us); + int wait_max = 1000; + int wait = 10; + u32 reg_value; + int ret; + + might_sleep_if(!is_atomic); + + if (timeout_us <= 10) { + is_atomic = true; + wait = 1; + } + + for (;;) { + bool expired = ktime_after(ktime_get_raw(), end); + + /* guarantee the condition is evaluated after timeout expired */ + barrier(); + + reg_value = read(display, reg); + if ((reg_value & mask) == value) { + ret = 0; + break; + } + + if (expired) { + ret = -ETIMEDOUT; + break; + } + + if (is_atomic) + udelay(wait); + else + usleep_range(wait, wait << 1); + + if (wait < wait_max) + wait <<= 1; + } + + if (out_val) + *out_val = reg_value; + + return ret; +} + +static int intel_de_wait_for_register(struct intel_display *display, + i915_reg_t reg, u32 mask, u32 value, + unsigned int fast_timeout_us, + unsigned int slow_timeout_us, + u32 (*read)(struct intel_display *display, i915_reg_t reg), + u32 *out_value, bool is_atomic) +{ + int ret = -EINVAL; + + if (fast_timeout_us) + ret = __intel_de_wait_for_register(display, reg, mask, value, + fast_timeout_us, read, + out_value, is_atomic); + + if (ret && slow_timeout_us) + ret = __intel_de_wait_for_register(display, reg, mask, value, + slow_timeout_us, read, + out_value, is_atomic); + + return ret; +} + int intel_de_wait_us(struct intel_display *display, i915_reg_t reg, u32 mask, u32 value, unsigned int timeout_us, u32 *out_value) @@ -15,8 +90,10 @@ int intel_de_wait_us(struct intel_display *display, i915_reg_t reg, intel_dmc_wl_get(display, reg); - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); + ret = intel_de_wait_for_register(display, reg, mask, value, + timeout_us, 0, + intel_de_read, + out_value, false); intel_dmc_wl_put(display, reg); @@ -31,8 +108,10 @@ int intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, intel_dmc_wl_get(display, reg); - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); + ret = intel_de_wait_for_register(display, reg, mask, value, + 2, timeout_ms * 1000, + intel_de_read, + out_value, false); intel_dmc_wl_put(display, reg); @@ -43,16 +122,20 @@ int intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, u32 mask, u32 value, unsigned int timeout_ms, u32 *out_value) { - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); + return intel_de_wait_for_register(display, reg, mask, value, + 2, timeout_ms * 1000, + intel_de_read_fw, + out_value, false); } int intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, u32 mask, u32 value, unsigned int timeout_us, u32 *out_value) { - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); + return intel_de_wait_for_register(display, reg, mask, value, + timeout_us, 0, + intel_de_read_fw, + out_value, true); } int intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h index a8cfd65119e0..08d7ab933672 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h @@ -98,37 +98,6 @@ static inline u32 intel_uncore_rmw(struct intel_uncore *uncore, return xe_mmio_rmw32(__compat_uncore_to_mmio(uncore), reg, clear, set); } -static inline int -__intel_wait_for_register(struct intel_uncore *uncore, i915_reg_t i915_reg, - u32 mask, u32 value, unsigned int fast_timeout_us, - unsigned int slow_timeout_ms, u32 *out_value) -{ - struct xe_reg reg = XE_REG(i915_mmio_reg_offset(i915_reg)); - bool atomic; - - /* - * Replicate the behavior from i915 here, in which sleep is not - * performed if slow_timeout_ms == 0. This is necessary because - * of some paths in display code where waits are done in atomic - * context. - */ - atomic = !slow_timeout_ms && fast_timeout_us > 0; - - return xe_mmio_wait32(__compat_uncore_to_mmio(uncore), reg, mask, value, - fast_timeout_us + 1000 * slow_timeout_ms, - out_value, atomic); -} - -static inline int -__intel_wait_for_register_fw(struct intel_uncore *uncore, i915_reg_t i915_reg, - u32 mask, u32 value, unsigned int fast_timeout_us, - unsigned int slow_timeout_ms, u32 *out_value) -{ - return __intel_wait_for_register(uncore, i915_reg, mask, value, - fast_timeout_us, slow_timeout_ms, - out_value); -} - static inline u32 intel_uncore_read_fw(struct intel_uncore *uncore, i915_reg_t i915_reg) { From d049e56b1739101d1c4d81deedb269c52a8dbba0 Mon Sep 17 00:00:00 2001 From: Yasuaki Torimaru Date: Tue, 24 Mar 2026 19:06:24 +0900 Subject: [PATCH 210/712] wifi: wilc1000: fix u8 overflow in SSID scan buffer size calculation The variable valuesize is declared as u8 but accumulates the total length of all SSIDs to scan. Each SSID contributes up to 33 bytes (IEEE80211_MAX_SSID_LEN + 1), and with WILC_MAX_NUM_PROBED_SSID (10) SSIDs the total can reach 330, which wraps around to 74 when stored in a u8. This causes kmalloc to allocate only 75 bytes while the subsequent memcpy writes up to 331 bytes into the buffer, resulting in a 256-byte heap buffer overflow. Widen valuesize from u8 to u32 to accommodate the full range. Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver") Cc: stable@vger.kernel.org Signed-off-by: Yasuaki Torimaru Link: https://patch.msgid.link/20260324100624.983458-1-yasuakitorimaru@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/microchip/wilc1000/hif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index f354b11cb919..944b2a812b63 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -163,7 +163,7 @@ int wilc_scan(struct wilc_vif *vif, u8 scan_source, u32 index = 0; u32 i, scan_timeout; u8 *buffer; - u8 valuesize = 0; + u32 valuesize = 0; u8 *search_ssid_vals = NULL; const u8 ch_list_len = request->n_channels; struct host_if_drv *hif_drv = vif->hif_drv; From 0fd56fad9c56356e7fa7a7c52e7ecbf807a44eb0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 23 Mar 2026 16:08:45 +0800 Subject: [PATCH 211/712] wifi: wl1251: validate packet IDs before indexing tx_frames wl1251_tx_packet_cb() uses the firmware completion ID directly to index the fixed 16-entry wl->tx_frames[] array. The ID is a raw u8 from the completion block, and the callback does not currently verify that it fits the array before dereferencing it. Reject completion IDs that fall outside wl->tx_frames[] and keep the existing NULL check in the same guard. This keeps the fix local to the trust boundary and avoids touching the rest of the completion flow. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260323080845.40033-1-pengpeng@iscas.ac.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/ti/wl1251/tx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ti/wl1251/tx.c b/drivers/net/wireless/ti/wl1251/tx.c index 2da8c0d5105b..4489aa77bb0f 100644 --- a/drivers/net/wireless/ti/wl1251/tx.c +++ b/drivers/net/wireless/ti/wl1251/tx.c @@ -402,12 +402,14 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, int hdrlen; u8 *frame; - skb = wl->tx_frames[result->id]; - if (skb == NULL) { - wl1251_error("SKB for packet %d is NULL", result->id); + if (unlikely(result->id >= ARRAY_SIZE(wl->tx_frames) || + wl->tx_frames[result->id] == NULL)) { + wl1251_error("invalid packet id %u", result->id); return; } + skb = wl->tx_frames[result->id]; + info = IEEE80211_SKB_CB(skb); if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && From 744fabc338e87b95c4d1ff7c95bc8c0f834c6d99 Mon Sep 17 00:00:00 2001 From: Alexey Velichayshiy Date: Sat, 7 Feb 2026 18:03:22 +0300 Subject: [PATCH 212/712] wifi: iwlwifi: mvm: fix potential out-of-bounds read in iwl_mvm_nd_match_info_handler() The memcpy function assumes the dynamic array notif->matches is at least as large as the number of bytes to copy. Otherwise, results->matches may contain unwanted data. To guarantee safety, extend the validation in one of the checks to ensure sufficient packet length. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: stable@vger.kernel.org Fixes: 5ac54afd4d97 ("wifi: iwlwifi: mvm: Add handling for scan offload match info notification") Signed-off-by: Alexey Velichayshiy Link: https://patch.msgid.link/20260207150335.1013646-1-a.velichayshiy@ispras.ru Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index a19f9d2e9346..9a74f60c9185 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2807,7 +2807,7 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm, if (IS_ERR_OR_NULL(vif)) return; - if (len < sizeof(struct iwl_scan_offload_match_info)) { + if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) { IWL_ERR(mvm, "Invalid scan match info notification\n"); return; } From 57ad0d4a00f5d3e80f33ba2da8d560c73d83dc22 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 6 Mar 2026 13:50:31 +0100 Subject: [PATCH 213/712] s390/cpum_sf: Cap sampling rate to prevent lsctl exception commit fcc43a7e294f ("s390/configs: Set HZ=1000") changed the interrupt frequency of the system. On machines with heavy load and many perf event overflows, this might lead to an exception. Dmesg displays these entries: [112.242542] cpum_sf: Loading sampling controls failed: op 1 err -22 One line per CPU online. The root cause is the CPU Measurement sampling facility overflow adjustment. Whenever an overflow (too much samples per tick) occurs, the sampling rate is adjusted and increased. This was done without observing the maximum sampling rate limit. When the current sampling interval is higher than the maximum sampling rate limit, the lsctl instruction raises an exception. The error messages is the result of such an exception. Observe the upper limit when the new sampling rate is recalculated. Cc: stable@vger.kernel.org Fixes: 39d4a501a9ef ("s390/cpum_sf: Adjust sampling interval to avoid hitting sample limits") Signed-off-by: Thomas Richter Reviewed-by: Sumanth Korikkar Reviewed-by: Hendrik Brueckner Signed-off-by: Vasily Gorbik --- arch/s390/kernel/perf_cpum_sf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index c92c29de725e..7bfeb5208177 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -1168,6 +1168,7 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt, static void hw_perf_event_update(struct perf_event *event, int flush_all) { unsigned long long event_overflow, sampl_overflow, num_sdb; + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); struct hw_perf_event *hwc = &event->hw; union hws_trailer_header prev, new; struct hws_trailer_entry *te; @@ -1247,8 +1248,11 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) * are dropped. * Slightly increase the interval to avoid hitting this limit. */ - if (event_overflow) + if (event_overflow) { SAMPL_RATE(hwc) += DIV_ROUND_UP(SAMPL_RATE(hwc), 10); + if (SAMPL_RATE(hwc) > cpuhw->qsi.max_sampl_rate) + SAMPL_RATE(hwc) = cpuhw->qsi.max_sampl_rate; + } } static inline unsigned long aux_sdb_index(struct aux_buffer *aux, From c8d46f17c2fc7d25c18e60c008928aecab26184d Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 19 Mar 2026 09:06:52 +0100 Subject: [PATCH 214/712] s390/zcrypt: Fix memory leak with CCA cards used as accelerator Tests showed that there is a memory leak if CCA cards are used as accelerator for clear key RSA requests (ME and CRT). With the last rework for the memory allocation the AP messages are allocated by ap_init_apmsg() but for some reason on two places (ME and CRT) the older allocation was still in place. So the first allocation simple was never freed. Fixes: 57db62a130ce ("s390/ap/zcrypt: Rework AP message buffer allocation") Reported-by: Yi Zhang Closes: https://lore.kernel.org/linux-s390/CAHj4cs9H67Uz0iVaRQv447p7JFPRPy3TKAT4=Y6_e=wSHCZM5w@mail.gmail.com/ Reported-by: Nadja Hariz Cc: stable@vger.kernel.org Reviewed-by: Ingo Franzki Reviewed-by: Holger Dengler Acked-by: Heiko Carstens Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_msgtype6.c | 32 ++++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index a0dcab5dc4f2..23a32221e41a 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -953,6 +953,10 @@ static atomic_t zcrypt_step = ATOMIC_INIT(0); /* * The request distributor calls this function if it picked the CEXxC * device to handle a modexpo request. + * This function assumes that ap_msg has been initialized with + * ap_init_apmsg() and thus a valid buffer with the size of + * ap_msg->bufsize is available within ap_msg. Also the caller has + * to make sure ap_release_apmsg() is always called even on failure. * @zq: pointer to zcrypt_queue structure that identifies the * CEXxC device to the request distributor * @mex: pointer to the modexpo request buffer @@ -964,21 +968,17 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq, struct ap_response_type *resp_type = &ap_msg->response; int rc; - ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL); - if (!ap_msg->msg) - return -ENOMEM; - ap_msg->bufsize = PAGE_SIZE; ap_msg->receive = zcrypt_msgtype6_receive; ap_msg->psmid = (((unsigned long)current->pid) << 32) + atomic_inc_return(&zcrypt_step); rc = icamex_msg_to_type6mex_msgx(zq, ap_msg, mex); if (rc) - goto out_free; + goto out; resp_type->type = CEXXC_RESPONSE_TYPE_ICA; init_completion(&resp_type->work); rc = ap_queue_message(zq->queue, ap_msg); if (rc) - goto out_free; + goto out; rc = wait_for_completion_interruptible(&resp_type->work); if (rc == 0) { rc = ap_msg->rc; @@ -991,15 +991,17 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq, ap_cancel_message(zq->queue, ap_msg); } -out_free: - free_page((unsigned long)ap_msg->msg); - ap_msg->msg = NULL; +out: return rc; } /* * The request distributor calls this function if it picked the CEXxC * device to handle a modexpo_crt request. + * This function assumes that ap_msg has been initialized with + * ap_init_apmsg() and thus a valid buffer with the size of + * ap_msg->bufsize is available within ap_msg. Also the caller has + * to make sure ap_release_apmsg() is always called even on failure. * @zq: pointer to zcrypt_queue structure that identifies the * CEXxC device to the request distributor * @crt: pointer to the modexpoc_crt request buffer @@ -1011,21 +1013,17 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq, struct ap_response_type *resp_type = &ap_msg->response; int rc; - ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL); - if (!ap_msg->msg) - return -ENOMEM; - ap_msg->bufsize = PAGE_SIZE; ap_msg->receive = zcrypt_msgtype6_receive; ap_msg->psmid = (((unsigned long)current->pid) << 32) + atomic_inc_return(&zcrypt_step); rc = icacrt_msg_to_type6crt_msgx(zq, ap_msg, crt); if (rc) - goto out_free; + goto out; resp_type->type = CEXXC_RESPONSE_TYPE_ICA; init_completion(&resp_type->work); rc = ap_queue_message(zq->queue, ap_msg); if (rc) - goto out_free; + goto out; rc = wait_for_completion_interruptible(&resp_type->work); if (rc == 0) { rc = ap_msg->rc; @@ -1038,9 +1036,7 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq, ap_cancel_message(zq->queue, ap_msg); } -out_free: - free_page((unsigned long)ap_msg->msg); - ap_msg->msg = NULL; +out: return rc; } From 1b164b876c36c3eb5561dd9b37702b04401b0166 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 24 Mar 2026 10:21:25 -1000 Subject: [PATCH 215/712] cgroup: Wait for dying tasks to leave on rmdir a72f73c4dd9b ("cgroup: Don't expose dead tasks in cgroup") hid PF_EXITING tasks from cgroup.procs so that systemd doesn't see tasks that have already been reaped via waitpid(). However, the populated counter (nr_populated_csets) is only decremented when the task later passes through cgroup_task_dead() in finish_task_switch(). This means cgroup.procs can appear empty while the cgroup is still populated, causing rmdir to fail with -EBUSY. Fix this by making cgroup_rmdir() wait for dying tasks to fully leave. If the cgroup is populated but all remaining tasks have PF_EXITING set (the task iterator returns none due to the existing filter), wait for a kick from cgroup_task_dead() and retry. The wait is brief as tasks are removed from the cgroup's css_set between PF_EXITING assertion in do_exit() and cgroup_task_dead() in finish_task_switch(). v2: cgroup_is_populated() true to false transition happens under css_set_lock not cgroup_mutex, so retest under css_set_lock before sleeping to avoid missed wakeups (Sebastian). Fixes: a72f73c4dd9b ("cgroup: Don't expose dead tasks in cgroup") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202603222104.2c81684e-lkp@intel.com Reported-by: Sebastian Andrzej Siewior Signed-off-by: Tejun Heo Reviewed-by: Sebastian Andrzej Siewior Cc: Bert Karwatzki Cc: Michal Koutny Cc: cgroups@vger.kernel.org --- include/linux/cgroup-defs.h | 3 ++ kernel/cgroup/cgroup.c | 86 +++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index bb92f5c169ca..7f87399938fa 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -609,6 +609,9 @@ struct cgroup { /* used to wait for offlining of csses */ wait_queue_head_t offline_waitq; + /* used by cgroup_rmdir() to wait for dying tasks to leave */ + wait_queue_head_t dying_populated_waitq; + /* used to schedule release agent */ struct work_struct release_agent_work; diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 01fc2a93f3ef..2163054e1aa6 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -2126,6 +2126,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) #endif init_waitqueue_head(&cgrp->offline_waitq); + init_waitqueue_head(&cgrp->dying_populated_waitq); INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent); } @@ -6224,6 +6225,76 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) return 0; }; +/** + * cgroup_drain_dying - wait for dying tasks to leave before rmdir + * @cgrp: the cgroup being removed + * + * The PF_EXITING filter in css_task_iter_advance() hides exiting tasks from + * cgroup.procs so that userspace (e.g. systemd) doesn't see tasks that have + * already been reaped via waitpid(). However, the populated counter + * (nr_populated_csets) is only decremented when the task later passes through + * cgroup_task_dead() in finish_task_switch(). This creates a window where + * cgroup.procs appears empty but cgroup_is_populated() is still true, causing + * rmdir to fail with -EBUSY. + * + * This function bridges that gap. If the cgroup is populated but all remaining + * tasks have PF_EXITING set, we wait for cgroup_task_dead() to process them. + * Tasks are removed from the cgroup's css_set in cgroup_task_dead() called from + * finish_task_switch(). As the window between PF_EXITING and cgroup_task_dead() + * is short, the number of PF_EXITING tasks on the list is small and the wait + * is brief. + * + * Each cgroup_task_dead() kicks the waitqueue via cset->cgrp_links, and we + * retry the full check from scratch. + * + * Must be called with cgroup_mutex held. + */ +static int cgroup_drain_dying(struct cgroup *cgrp) + __releases(&cgroup_mutex) __acquires(&cgroup_mutex) +{ + struct css_task_iter it; + struct task_struct *task; + DEFINE_WAIT(wait); + + lockdep_assert_held(&cgroup_mutex); +retry: + if (!cgroup_is_populated(cgrp)) + return 0; + + /* Same iterator as cgroup.threads - if any task is visible, it's busy */ + css_task_iter_start(&cgrp->self, 0, &it); + task = css_task_iter_next(&it); + css_task_iter_end(&it); + + if (task) + return -EBUSY; + + /* + * All remaining tasks are PF_EXITING and will pass through + * cgroup_task_dead() shortly. Wait for a kick and retry. + * + * cgroup_is_populated() can't transition from false to true while + * we're holding cgroup_mutex, but the true to false transition + * happens under css_set_lock (via cgroup_task_dead()). We must + * retest and prepare_to_wait() under css_set_lock. Otherwise, the + * transition can happen between our first test and + * prepare_to_wait(), and we sleep with no one to wake us. + */ + spin_lock_irq(&css_set_lock); + if (!cgroup_is_populated(cgrp)) { + spin_unlock_irq(&css_set_lock); + return 0; + } + prepare_to_wait(&cgrp->dying_populated_waitq, &wait, + TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&css_set_lock); + mutex_unlock(&cgroup_mutex); + schedule(); + finish_wait(&cgrp->dying_populated_waitq, &wait); + mutex_lock(&cgroup_mutex); + goto retry; +} + int cgroup_rmdir(struct kernfs_node *kn) { struct cgroup *cgrp; @@ -6233,9 +6304,12 @@ int cgroup_rmdir(struct kernfs_node *kn) if (!cgrp) return 0; - ret = cgroup_destroy_locked(cgrp); - if (!ret) - TRACE_CGROUP_PATH(rmdir, cgrp); + ret = cgroup_drain_dying(cgrp); + if (!ret) { + ret = cgroup_destroy_locked(cgrp); + if (!ret) + TRACE_CGROUP_PATH(rmdir, cgrp); + } cgroup_kn_unlock(kn); return ret; @@ -6995,6 +7069,7 @@ void cgroup_task_exit(struct task_struct *tsk) static void do_cgroup_task_dead(struct task_struct *tsk) { + struct cgrp_cset_link *link; struct css_set *cset; unsigned long flags; @@ -7008,6 +7083,11 @@ static void do_cgroup_task_dead(struct task_struct *tsk) if (thread_group_leader(tsk) && atomic_read(&tsk->signal->live)) list_add_tail(&tsk->cg_list, &cset->dying_tasks); + /* kick cgroup_drain_dying() waiters, see cgroup_rmdir() */ + list_for_each_entry(link, &cset->cgrp_links, cgrp_link) + if (waitqueue_active(&link->cgrp->dying_populated_waitq)) + wake_up(&link->cgrp->dying_populated_waitq); + if (dl_task(tsk)) dec_dl_tasks_cs(tsk); From 6680c162b4850976ee52b57372eddc4450c1d074 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 24 Mar 2026 10:21:47 -1000 Subject: [PATCH 216/712] selftests/cgroup: Don't require synchronous populated update on task exit test_cgcore_populated (test_core) and test_cgkill_{simple,tree,forkbomb} (test_kill) check cgroup.events "populated 0" immediately after reaping child tasks with waitpid(). This used to work because cgroup_task_exit() in do_exit() unlinked tasks from css_sets before exit_notify() woke up waitpid(). d245698d727a ("cgroup: Defer task cgroup unlink until after the task is done switching out") moved the unlink to cgroup_task_dead() in finish_task_switch(), which runs after exit_notify(). The populated counter is now decremented after the parent's waitpid() can return, so there is no longer a synchronous ordering guarantee. On PREEMPT_RT, where cgroup_task_dead() is further deferred through lazy irq_work, the race window is even larger. The synchronous populated transition was never part of the cgroup interface contract - it was an implementation artifact. Use cg_read_strcmp_wait() which retries for up to 1 second, matching what these tests actually need to verify: that the cgroup eventually becomes unpopulated after all tasks exit. Fixes: d245698d727a ("cgroup: Defer task cgroup unlink until after the task is done switching out") Reported-by: Sebastian Andrzej Siewior Signed-off-by: Tejun Heo Tested-by: Sebastian Andrzej Siewior Cc: Christian Brauner Cc: cgroups@vger.kernel.org --- tools/testing/selftests/cgroup/lib/cgroup_util.c | 15 +++++++++++++++ .../selftests/cgroup/lib/include/cgroup_util.h | 2 ++ tools/testing/selftests/cgroup/test_core.c | 3 ++- tools/testing/selftests/cgroup/test_kill.c | 7 ++++--- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c index ce6c2642fd9b..6a7295347e90 100644 --- a/tools/testing/selftests/cgroup/lib/cgroup_util.c +++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c @@ -123,6 +123,21 @@ int cg_read_strcmp(const char *cgroup, const char *control, return ret; } +int cg_read_strcmp_wait(const char *cgroup, const char *control, + const char *expected) +{ + int i, ret; + + for (i = 0; i < 100; i++) { + ret = cg_read_strcmp(cgroup, control, expected); + if (!ret) + return ret; + usleep(10000); + } + + return ret; +} + int cg_read_strstr(const char *cgroup, const char *control, const char *needle) { char buf[PAGE_SIZE]; diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h index 77f386dab5e8..567b1082974c 100644 --- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h +++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h @@ -61,6 +61,8 @@ extern int cg_read(const char *cgroup, const char *control, char *buf, size_t len); extern int cg_read_strcmp(const char *cgroup, const char *control, const char *expected); +extern int cg_read_strcmp_wait(const char *cgroup, const char *control, + const char *expected); extern int cg_read_strstr(const char *cgroup, const char *control, const char *needle); extern long cg_read_long(const char *cgroup, const char *control); diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c index 102262555a59..7b83c7e7c9d4 100644 --- a/tools/testing/selftests/cgroup/test_core.c +++ b/tools/testing/selftests/cgroup/test_core.c @@ -233,7 +233,8 @@ static int test_cgcore_populated(const char *root) if (err) goto cleanup; - if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) + if (cg_read_strcmp_wait(cg_test_d, "cgroup.events", + "populated 0\n")) goto cleanup; /* Remove cgroup. */ diff --git a/tools/testing/selftests/cgroup/test_kill.c b/tools/testing/selftests/cgroup/test_kill.c index c8c9d306925b..f6cd23a8ecc7 100644 --- a/tools/testing/selftests/cgroup/test_kill.c +++ b/tools/testing/selftests/cgroup/test_kill.c @@ -86,7 +86,7 @@ static int test_cgkill_simple(const char *root) wait_for_pid(pids[i]); if (ret == KSFT_PASS && - cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n")) + cg_read_strcmp_wait(cgroup, "cgroup.events", "populated 0\n")) ret = KSFT_FAIL; if (cgroup) @@ -190,7 +190,8 @@ static int test_cgkill_tree(const char *root) wait_for_pid(pids[i]); if (ret == KSFT_PASS && - cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n")) + cg_read_strcmp_wait(cgroup[0], "cgroup.events", + "populated 0\n")) ret = KSFT_FAIL; for (i = 9; i >= 0 && cgroup[i]; i--) { @@ -251,7 +252,7 @@ static int test_cgkill_forkbomb(const char *root) wait_for_pid(pid); if (ret == KSFT_PASS && - cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n")) + cg_read_strcmp_wait(cgroup, "cgroup.events", "populated 0\n")) ret = KSFT_FAIL; if (cgroup) From d35ae50c5f48dfcd33cb24bf477ce912fa0af1f7 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Mar 2026 16:26:53 -0500 Subject: [PATCH 217/712] rust: device: add device name method Add a name() method to the `Device` type, which returns a CStr that contains the device name. Signed-off-by: Timur Tabi Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Tested-by: John Hubbard Tested-by: Eliot Courtney Link: https://patch.msgid.link/20260319212658.2541610-2-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- rust/helpers/device.c | 5 +++++ rust/kernel/device.rs | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/rust/helpers/device.c b/rust/helpers/device.c index a8ab931a9bd1..3be4ee590784 100644 --- a/rust/helpers/device.c +++ b/rust/helpers/device.c @@ -25,3 +25,8 @@ __rust_helper void rust_helper_dev_set_drvdata(struct device *dev, void *data) { dev_set_drvdata(dev, data); } + +__rust_helper const char *rust_helper_dev_name(const struct device *dev) +{ + return dev_name(dev); +} diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 379058eca2ed..6d5396a43ebe 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -489,6 +489,17 @@ pub fn fwnode(&self) -> Option<&property::FwNode> { // defined as a `#[repr(transparent)]` wrapper around `fwnode_handle`. Some(unsafe { &*fwnode_handle.cast() }) } + + /// Returns the name of the device. + /// + /// This is the kobject name of the device, or its initial name if the kobject is not yet + /// available. + #[inline] + pub fn name(&self) -> &CStr { + // SAFETY: By its type invariant `self.as_raw()` is a valid pointer to a `struct device`. + // The returned string is valid for the lifetime of the device. + unsafe { CStr::from_char_ptr(bindings::dev_name(self.as_raw())) } + } } // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic From 2c9ee79130bcb1da9b355eb894c80cdfa2dea86f Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 10 Feb 2026 12:41:30 +0530 Subject: [PATCH 218/712] drm/ci: reduce sm8350-hdk parallel jobs from 4 to 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sm8350-hdk jobs are short and each test takes around 2–3 minutes and the full job completes in about 10 minutes. Running 4 parallel jobs uses 4 devices at once, which is not needed. Set parallel to 2 to reduce device usage. Signed-off-by: Vignesh Raman Reviewed-by: Daniel Stone Reviewed-by: Dmitry Baryshkov Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/ci/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index 81147e86bfd0..762598c7db25 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -183,7 +183,7 @@ msm:sm8350-hdk: extends: - .lava-igt:arm64 stage: msm - parallel: 4 + parallel: 2 variables: BOOT_METHOD: fastboot DEVICE_TYPE: sm8350-hdk From f1f2a9a874d1d907cc5a464e6e32143b5517419b Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 10 Feb 2026 12:41:31 +0530 Subject: [PATCH 219/712] drm/ci: i915: cml: update runner tag asus-C436FA-Flip-hatch has fewer devices available in the LAVA lab and drm-ci uses only 2 DUTs, causing tests to time out. Update drm-ci to use puff instead of hatch so the tests can run on 5 DUTs. Also increase parallel count for amly jobs to 3. Signed-off-by: Vignesh Raman Reviewed-by: Daniel Stone Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/ci/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index 762598c7db25..af094153d987 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -277,7 +277,7 @@ i915:glk: i915:amly: extends: - .i915 - parallel: 2 + parallel: 3 variables: DEVICE_TYPE: asus-C433TA-AJ0005-rammus GPU_VERSION: amly @@ -304,11 +304,11 @@ i915:whl: i915:cml: extends: - .i915 - parallel: 2 + parallel: 5 variables: - DEVICE_TYPE: asus-C436FA-Flip-hatch + DEVICE_TYPE: acer-chromebox-cxi4-puff GPU_VERSION: cml - RUNNER_TAG: mesa-ci-x86-64-lava-asus-C436FA-Flip-hatch + RUNNER_TAG: mesa-ci-x86-64-lava-acer-chromebox-cxi4-puff i915:tgl: extends: From 0bebb1773d616ab5b3eab741167ad8791fadfbfc Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 10 Feb 2026 12:41:32 +0530 Subject: [PATCH 220/712] drm/ci: uprev mesa Uprev mesa to adapt to the latest changes in Mesa CI, such as: - LAVA overlay-based firmware handling - Container/job rule separation - Removal of the python-artifacts job - Use lava-job-submitter container to submit jobs - Use of the Alpine container for LAVA jobs - Various other CI improvements - Remove bare-metal jobs and disable apq8016 and apq8096 jobs, as these have been migrated to the Collabora LAVA farm - Fix issues with rebase with external fixes branch - Update expectation files Signed-off-by: Vignesh Raman Reviewed-by: Daniel Stone Reviewed-by: Dmitry Baryshkov Co-developed-by: Dmitry Baryshkov Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/ci/build.sh | 12 +-- drivers/gpu/drm/ci/build.yml | 40 +++---- drivers/gpu/drm/ci/container.yml | 32 ++++-- drivers/gpu/drm/ci/gitlab-ci.yml | 95 ++++++++++++---- drivers/gpu/drm/ci/igt_runner.sh | 4 +- drivers/gpu/drm/ci/image-tags.yml | 22 ++-- drivers/gpu/drm/ci/lava-submit.sh | 101 ++++++++---------- drivers/gpu/drm/ci/static-checks.yml | 1 + drivers/gpu/drm/ci/test.yml | 52 +++------ .../gpu/drm/ci/xfails/amdgpu-stoney-fails.txt | 13 ++- .../drm/ci/xfails/amdgpu-stoney-flakes.txt | 7 ++ drivers/gpu/drm/ci/xfails/i915-amly-fails.txt | 27 +---- drivers/gpu/drm/ci/xfails/i915-apl-fails.txt | 24 +---- drivers/gpu/drm/ci/xfails/i915-cml-fails.txt | 37 ++----- drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt | 7 ++ drivers/gpu/drm/ci/xfails/i915-glk-fails.txt | 22 ++-- drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt | 27 +---- drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt | 5 +- drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt | 5 +- drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt | 6 ++ drivers/gpu/drm/ci/xfails/i915-whl-fails.txt | 13 +-- .../drm/ci/xfails/mediatek-mt8173-fails.txt | 12 +-- .../drm/ci/xfails/mediatek-mt8173-flakes.txt | 35 ++++++ .../msm-sc7180-trogdor-kingoftown-fails.txt | 5 +- ...sm-sc7180-trogdor-lazor-limozeen-fails.txt | 5 +- .../drm/ci/xfails/msm-sm8350-hdk-fails.txt | 1 + .../drm/ci/xfails/panfrost-mt8183-fails.txt | 1 + .../drm/ci/xfails/panfrost-rk3288-fails.txt | 1 + .../drm/ci/xfails/panfrost-rk3399-fails.txt | 1 + .../drm/ci/xfails/rockchip-rk3288-fails.txt | 15 ++- .../drm/ci/xfails/rockchip-rk3288-flakes.txt | 21 ++++ .../drm/ci/xfails/rockchip-rk3399-fails.txt | 12 ++- .../drm/ci/xfails/rockchip-rk3399-flakes.txt | 35 ++++++ .../drm/ci/xfails/virtio_gpu-none-fails.txt | 66 ++---------- drivers/gpu/drm/ci/xfails/vkms-none-fails.txt | 2 + 35 files changed, 398 insertions(+), 366 deletions(-) create mode 100644 drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt diff --git a/drivers/gpu/drm/ci/build.sh b/drivers/gpu/drm/ci/build.sh index ac5e7ed195cf..4353ee0f8889 100644 --- a/drivers/gpu/drm/ci/build.sh +++ b/drivers/gpu/drm/ci/build.sh @@ -3,9 +3,6 @@ set -ex -# Clean up stale rebases that GitLab might not have removed when reusing a checkout dir -rm -rf .git/rebase-apply - . .gitlab-ci/container/container_pre_build.sh # libssl-dev was uninstalled because it was considered an ephemeral package @@ -61,25 +58,24 @@ export PATH=$NEWPATH:$PATH git config --global user.email "fdo@example.com" git config --global user.name "freedesktop.org CI" -git config --global pull.rebase true # cleanup git state on the worker -rm -rf .git/rebase-merge +rm -rf .git/rebase-merge .git/rebase-apply # Try to merge fixes from target repo if [ "$(git ls-remote --exit-code --heads ${UPSTREAM_REPO} ${TARGET_BRANCH}-external-fixes)" ]; then - git pull ${UPSTREAM_REPO} ${TARGET_BRANCH}-external-fixes + git pull --no-rebase ${UPSTREAM_REPO} ${TARGET_BRANCH}-external-fixes fi # Try to merge fixes from local repo if this isn't a merge request # otherwise try merging the fixes from the merge target if [ -z "$CI_MERGE_REQUEST_PROJECT_PATH" ]; then if [ "$(git ls-remote --exit-code --heads origin ${TARGET_BRANCH}-external-fixes)" ]; then - git pull origin ${TARGET_BRANCH}-external-fixes + git pull --no-rebase origin ${TARGET_BRANCH}-external-fixes fi else if [ "$(git ls-remote --exit-code --heads ${CI_MERGE_REQUEST_PROJECT_URL} ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}-external-fixes)" ]; then - git pull ${CI_MERGE_REQUEST_PROJECT_URL} ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}-external-fixes + git pull --no-rebase ${CI_MERGE_REQUEST_PROJECT_URL} ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}-external-fixes fi fi diff --git a/drivers/gpu/drm/ci/build.yml b/drivers/gpu/drm/ci/build.yml index af27ff5de369..efbcaae49807 100644 --- a/drivers/gpu/drm/ci/build.yml +++ b/drivers/gpu/drm/ci/build.yml @@ -1,6 +1,6 @@ .build: extends: - - .container+build-rules + - .build-rules stage: build-only artifacts: paths: @@ -133,6 +133,10 @@ debian-arm32-asan: rules: - when: never +debian-x86_64-msan: + rules: + - when: never + debian-arm64: rules: - when: never @@ -153,7 +157,7 @@ debian-arm64-ubsan: rules: - when: never -debian-build-testing: +debian-build-x86_64: rules: - when: never @@ -177,26 +181,14 @@ debian-release: rules: - when: never +debian-riscv64: + rules: + - when: never + debian-s390x: rules: - when: never -debian-testing: - rules: - - when: never - -debian-testing-asan: - rules: - - when: never - -debian-testing-msan: - rules: - - when: never - -debian-testing-ubsan: - rules: - - when: never - debian-vulkan: rules: - when: never @@ -205,6 +197,18 @@ debian-x86_32: rules: - when: never +debian-x86_64: + rules: + - when: never + +debian-x86_64-asan: + rules: + - when: never + +debian-x86_64-ubsan: + rules: + - when: never + fedora-release: rules: - when: never diff --git a/drivers/gpu/drm/ci/container.yml b/drivers/gpu/drm/ci/container.yml index 5f90508578a3..ce6007a45a07 100644 --- a/drivers/gpu/drm/ci/container.yml +++ b/drivers/gpu/drm/ci/container.yml @@ -5,21 +5,29 @@ debian/x86_64_build-base: variables: - EXTRA_LOCAL_PACKAGES: "libcairo-dev libdw-dev libjson-c-dev libkmod2 libkmod-dev libpciaccess-dev libproc2-dev libudev-dev libunwind-dev python3-docutils bc python3-ply libssl-dev bc" - -debian/x86_64_test-gl: - variables: - EXTRA_LOCAL_PACKAGES: "jq libasound2 libcairo2 libdw1 libglib2.0-0 libjson-c5 libkmod-dev libkmod2 libgles2 libproc2-dev" + EXTRA_LOCAL_PACKAGES: "libcairo-dev libdw-dev libjson-c-dev libkmod-dev libpciaccess-dev libproc2-dev libudev-dev libunwind-dev python3-docutils bc python3-ply libssl-dev bc" debian/arm64_build: variables: - EXTRA_LOCAL_PACKAGES: "libcairo-dev libdw-dev libjson-c-dev libproc2-dev libkmod2 libkmod-dev libpciaccess-dev libudev-dev libunwind-dev python3-docutils libssl-dev crossbuild-essential-armhf libkmod-dev:armhf libproc2-dev:armhf libunwind-dev:armhf libdw-dev:armhf libpixman-1-dev:armhf libcairo-dev:armhf libudev-dev:armhf libjson-c-dev:armhf" + EXTRA_LOCAL_PACKAGES: "libcairo-dev libdw-dev libjson-c-dev libproc2-dev libkmod-dev libpciaccess-dev libudev-dev libunwind-dev python3-docutils libssl-dev crossbuild-essential-armhf libkmod-dev:armhf libproc2-dev:armhf libunwind-dev:armhf libdw-dev:armhf libpixman-1-dev:armhf libcairo-dev:armhf libudev-dev:armhf libjson-c-dev:armhf" -.kernel+rootfs: +debian/x86_64_test-gl: variables: - EXTRA_LOCAL_PACKAGES: "jq libasound2 libcairo2 libdw1 libglib2.0-0 libjson-c5" + EXTRA_LOCAL_PACKAGES: "jq libasound2t64 libcairo2 libdw1t64 libglib2.0-0t64 libjson-c5 libkmod2 libgles2 libdrm-nouveau2 libdrm-amdgpu1" + +debian/arm64_test-gl: + variables: + EXTRA_LOCAL_PACKAGES: "jq libasound2t64 libcairo2 libdw1t64 libglib2.0-0t64 libjson-c5 libkmod2 libgles2 libdrm-nouveau2 libdrm-amdgpu1" + +debian/arm32_test-gl: + variables: + EXTRA_LOCAL_PACKAGES: "jq libasound2t64 libcairo2 libdw1t64 libglib2.0-0t64 libjson-c5 libkmod2 libgles2 libdrm-nouveau2 libdrm-amdgpu1 libunwind8" # Disable container jobs that we won't use +alpine/x86_64_build: + rules: + - when: never + debian/arm64_test-vk: rules: - when: never @@ -28,6 +36,10 @@ debian/baremetal_arm32_test-gl: rules: - when: never +debian/baremetal_arm64_test-gl: + rules: + - when: never + debian/baremetal_arm64_test-vk: rules: - when: never @@ -36,6 +48,10 @@ debian/ppc64el_build: rules: - when: never +debian/riscv64_build: + rules: + - when: never + debian/s390x_build: rules: - when: never diff --git a/drivers/gpu/drm/ci/gitlab-ci.yml b/drivers/gpu/drm/ci/gitlab-ci.yml index 56638814bb28..20e8cbdc39e9 100644 --- a/drivers/gpu/drm/ci/gitlab-ci.yml +++ b/drivers/gpu/drm/ci/gitlab-ci.yml @@ -1,6 +1,6 @@ variables: DRM_CI_PROJECT_PATH: &drm-ci-project-path mesa/mesa - DRM_CI_COMMIT_SHA: &drm-ci-commit-sha 02337aec715c25dae7ff2479d986f831c77fe536 + DRM_CI_COMMIT_SHA: &drm-ci-commit-sha 25881c701a56233dd8fc7f92db6884a73949d63d UPSTREAM_REPO: https://gitlab.freedesktop.org/drm/kernel.git TARGET_BRANCH: drm-next @@ -11,7 +11,7 @@ variables: DEQP_RUNNER_GIT_TAG: v0.20.0 FDO_UPSTREAM_REPO: helen.fornazier/linux # The repo where the git-archive daily runs - MESA_TEMPLATES_COMMIT: &ci-templates-commit c6aeb16f86e32525fa630fb99c66c4f3e62fc3cb + MESA_TEMPLATES_COMMIT: &ci-templates-commit aec7a6ce7bb38902c70641526f6611e27141784a DRM_CI_PROJECT_URL: https://gitlab.freedesktop.org/${DRM_CI_PROJECT_PATH} CI_PRE_CLONE_SCRIPT: |- set -o xtrace @@ -30,6 +30,8 @@ variables: S3_GITCACHE_BUCKET: git-cache # Bucket for the pipeline artifacts pushed to S3 S3_ARTIFACTS_BUCKET: artifacts + # Base path used for various artifacts + S3_BASE_PATH: "${S3_HOST}/${S3_KERNEL_BUCKET}" # per-pipeline artifact storage on MinIO PIPELINE_ARTIFACTS_BASE: ${S3_HOST}/${S3_ARTIFACTS_BUCKET}/${CI_PROJECT_PATH}/${CI_PIPELINE_ID} # per-job artifact storage on MinIO @@ -44,6 +46,8 @@ variables: ARTIFACTS_BASE_URL: https://${CI_PROJECT_ROOT_NAMESPACE}.${CI_PAGES_DOMAIN}/-/${CI_PROJECT_NAME}/-/jobs/${CI_JOB_ID}/artifacts # Python scripts for structured logger PYTHONPATH: "$PYTHONPATH:$CI_PROJECT_DIR/install" + # Mesa-specific variables that shouldn't be forwarded to DUTs and crosvm + CI_EXCLUDE_ENV_VAR_REGEX: 'SCRIPTS_DIR|RESULTS_DIR' default: @@ -84,10 +88,11 @@ include: - project: *drm-ci-project-path ref: *drm-ci-commit-sha file: + - '/.gitlab-ci/bare-metal/gitlab-ci.yml' - '/.gitlab-ci/build/gitlab-ci.yml' - '/.gitlab-ci/container/gitlab-ci.yml' - '/.gitlab-ci/farm-rules.yml' - - '/.gitlab-ci/lava/lava-gitlab-ci.yml' + - '/.gitlab-ci/lava/gitlab-ci.yml' - '/.gitlab-ci/test-source-dep.yml' - '/.gitlab-ci/test/gitlab-ci.yml' - '/src/amd/ci/gitlab-ci-inc.yml' @@ -147,10 +152,10 @@ stages: - if: &is-merge-attempt $GITLAB_USER_LOGIN == "marge-bot" && $CI_PIPELINE_SOURCE == "merge_request_event" # post-merge pipeline - if: &is-post-merge $GITLAB_USER_LOGIN == "marge-bot" && $CI_PIPELINE_SOURCE == "push" - # Pre-merge pipeline - - if: &is-pre-merge $CI_PIPELINE_SOURCE == "merge_request_event" + # Pre-merge pipeline (because merge pipelines are already caught above) + - if: &is-merge-request $CI_PIPELINE_SOURCE == "merge_request_event" # Push to a branch on a fork - - if: &is-fork-push $CI_PIPELINE_SOURCE == "push" + - if: &is-push-to-fork $CI_PIPELINE_SOURCE == "push" # nightly pipeline - if: &is-scheduled-pipeline $CI_PIPELINE_SOURCE == "schedule" # pipeline for direct pushes that bypassed the CI @@ -160,17 +165,59 @@ stages: # Rules applied to every job in the pipeline .common-rules: rules: - - if: *is-fork-push + - if: *is-push-to-fork when: manual - .never-post-merge-rules: rules: - if: *is-post-merge when: never -.container+build-rules: +# Note: make sure the branches in this list are the same as in +# `.build-only-delayed-rules` below. +.container-rules: + rules: + - !reference [.common-rules, rules] + # Run when re-enabling a disabled farm, but not when disabling it + - !reference [.disable-farm-mr-rules, rules] + # Never run immediately after merging, as we just ran everything + - !reference [.never-post-merge-rules, rules] + # Only rebuild containers in merge pipelines if any tags have been + # changed, else we'll just use the already-built containers + - if: *is-merge-attempt + changes: &image_tags_path + - drivers/gpu/drm/ci/image-tags.yml + when: on_success + # Skip everything for pre-merge and merge pipelines which don't change + # anything in the build; we only do this for marge-bot and not user + # pipelines in a MR, because we might still need to run it to copy the + # container into the user's namespace. + - if: *is-merge-attempt + when: never + # Any MR pipeline which changes image-tags.yml needs to be able to + # rebuild the containers + - if: *is-merge-request + changes: *image_tags_path + when: manual + # ... however for MRs running inside the user namespace, we may need to + # run these jobs to copy the container images from upstream + - if: *is-merge-request + when: manual + # Build everything after someone bypassed the CI + - if: *is-direct-push + when: manual + # Scheduled pipelines reuse already-built containers + - if: *is-scheduled-pipeline + when: never + # Allow building everything in fork pipelines, but build nothing unless + # manually triggered + - when: manual + + +# Note: make sure the branches in this list are the same as in +# `.build-only-delayed-rules` below. +.build-rules: rules: - !reference [.common-rules, rules] # Run when re-enabling a disabled farm, but not when disabling it @@ -181,7 +228,7 @@ stages: - if: *is-merge-attempt when: on_success # Same as above, but for pre-merge pipelines - - if: *is-pre-merge + - if: *is-merge-request when: manual # Build everything after someone bypassed the CI - if: *is-direct-push @@ -197,7 +244,7 @@ stages: # Repeat of the above but with `when: on_success` replaced with # `when: delayed` + `start_in:`, for build-only jobs. # Note: make sure the branches in this list are the same as in -# `.container+build-rules` above. +# `.build-rules` above. .build-only-delayed-rules: rules: - !reference [.common-rules, rules] @@ -210,7 +257,7 @@ stages: when: delayed start_in: &build-delay 5 minutes # Same as above, but for pre-merge pipelines - - if: *is-pre-merge + - if: *is-merge-request when: manual # Build everything after someone bypassed the CI - if: *is-direct-push @@ -237,11 +284,6 @@ stages: - _build/meson-logs/strace -python-artifacts: - variables: - GIT_DEPTH: 10 - - # Git archive make-git-archive: extends: @@ -273,7 +315,7 @@ sanity: tags: - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64 rules: - - if: *is-pre-merge + - if: *is-merge-request when: on_success - when: never variables: @@ -284,7 +326,6 @@ sanity: - | set -eu image_tags=( - ALPINE_X86_64_LAVA_SSH_TAG CONTAINER_TAG DEBIAN_BASE_TAG DEBIAN_BUILD_TAG @@ -347,3 +388,19 @@ linkcheck-docs: test-docs: rules: - when: never + +.ci-tron-x86_64-test-vk: + rules: + - when: never + +.ci-tron-x86_64-test-gl-manual: + rules: + - when: never + +.ci-tron-arm64-test-gl: + rules: + - when: never + +.ci-tron-x86_64-test-gl: + rules: + - when: never diff --git a/drivers/gpu/drm/ci/igt_runner.sh b/drivers/gpu/drm/ci/igt_runner.sh index b24d4bc53cda..1c01bda52237 100755 --- a/drivers/gpu/drm/ci/igt_runner.sh +++ b/drivers/gpu/drm/ci/igt_runner.sh @@ -1,6 +1,8 @@ -#!/bin/sh +#!/usr/bin/env bash # SPDX-License-Identifier: MIT +. "${SCRIPTS_DIR}/setup-test-env.sh" + set -ex export IGT_FORCE_DRIVER=${DRIVER_NAME} diff --git a/drivers/gpu/drm/ci/image-tags.yml b/drivers/gpu/drm/ci/image-tags.yml index 7acc2e2a8eaa..7c43ae22bfd3 100644 --- a/drivers/gpu/drm/ci/image-tags.yml +++ b/drivers/gpu/drm/ci/image-tags.yml @@ -1,18 +1,22 @@ variables: - CONTAINER_TAG: "20250502-mesa-uprev" - DEBIAN_BASE_TAG: "${CONTAINER_TAG}" + CONTAINER_TAG: "20260108-mesa-igt" + + DEBIAN_BUILD_BASE_TAG: "${CONTAINER_TAG}" DEBIAN_BUILD_TAG: "${CONTAINER_TAG}" + DEBIAN_TEST_BASE_TAG: "${CONTAINER_TAG}" DEBIAN_TEST_GL_TAG: "${CONTAINER_TAG}" # default kernel for rootfs before injecting the current kernel tree - KERNEL_TAG: "v6.14-mesa-0bdd" + KERNEL_TAG: "v6.16-mesa-9d85" KERNEL_REPO: "gfx-ci/linux" - PKG_REPO_REV: "95bf62c" - - DEBIAN_PYUTILS_TAG: "${CONTAINER_TAG}" + PKG_REPO_REV: "0d2527f6" + FIRMWARE_TAG: "8fc31b97" + FIRMWARE_REPO: "gfx-ci/firmware" ALPINE_X86_64_BUILD_TAG: "${CONTAINER_TAG}" - ALPINE_X86_64_LAVA_SSH_TAG: "${CONTAINER_TAG}" - CONDITIONAL_BUILD_ANGLE_TAG: 384145a4023315dae658259bee07c43a - CONDITIONAL_BUILD_PIGLIT_TAG: a19e424b8a3f020dbf1b9dd29f220a4f + CONDITIONAL_BUILD_ANGLE_TAG: efd57e99d51361944f87b9466356b0ce + CONDITIONAL_BUILD_CROSVM_TAG: 4079babd375b09761d59eacb25a0598a + CONDITIONAL_BUILD_PIGLIT_TAG: 21ab2c66f54777163dd038dc4cfcfde6 + + CROSVM_TAG: ${CONDITIONAL_BUILD_CROSVM_TAG} diff --git a/drivers/gpu/drm/ci/lava-submit.sh b/drivers/gpu/drm/ci/lava-submit.sh index a295102c3468..405055aa9cc4 100755 --- a/drivers/gpu/drm/ci/lava-submit.sh +++ b/drivers/gpu/drm/ci/lava-submit.sh @@ -3,27 +3,18 @@ # shellcheck disable=SC2086 # we want word splitting # shellcheck disable=SC1091 # paths only become valid at runtime -# If we run in the fork (not from mesa or Marge-bot), reuse mainline kernel and rootfs, if exist. -_check_artifact_path() { - _url="https://${1}/${2}" - if curl -s -o /dev/null -I -L -f --retry 4 --retry-delay 15 "${_url}"; then - echo -n "${_url}" - fi -} +# shellcheck disable=SC1090 +source "${FDO_CI_BASH_HELPERS}" -get_path_to_artifact() { - _mainline_artifact="$(_check_artifact_path ${BASE_SYSTEM_MAINLINE_HOST_PATH} ${1})" - if [ -n "${_mainline_artifact}" ]; then - echo -n "${_mainline_artifact}" - return - fi - _fork_artifact="$(_check_artifact_path ${BASE_SYSTEM_FORK_HOST_PATH} ${1})" - if [ -n "${_fork_artifact}" ]; then - echo -n "${_fork_artifact}" - return - fi +fdo_log_section_start_collapsed prepare_rootfs "Preparing root filesystem" + +set -ex + +# If we run in the fork (not from mesa or Marge-bot), reuse mainline kernel and rootfs, if exist. +ROOTFS_URL="$(fdo_find_s3_path "$LAVA_ROOTFS_PATH")" || +{ set +x - error "Sorry, I couldn't find a viable built path for ${1} in either mainline or a fork." >&2 + fdo_log_section_error "Sorry, I couldn't find a viable built path for ${LAVA_ROOTFS_PATH} in either mainline or a fork." >&2 echo "" >&2 echo "If you're working on CI, this probably means that you're missing a dependency:" >&2 echo "this job ran ahead of the job which was supposed to upload that artifact." >&2 @@ -35,38 +26,51 @@ get_path_to_artifact() { exit 1 } -. "${SCRIPTS_DIR}/setup-test-env.sh" - -section_start prepare_rootfs "Preparing root filesystem" - -set -ex - -ROOTFS_URL="$(get_path_to_artifact lava-rootfs.tar.zst)" -[ $? != 1 ] || exit 1 - rm -rf results -mkdir -p results/job-rootfs-overlay/ +mkdir results -artifacts/ci-common/export-gitlab-job-env-for-dut.sh \ - > results/job-rootfs-overlay/set-job-env-vars.sh -cp artifacts/ci-common/init-*.sh results/job-rootfs-overlay/ -cp "$SCRIPTS_DIR"/setup-test-env.sh results/job-rootfs-overlay/ +fdo_filter_env_vars > dut-env-vars.sh +# Set SCRIPTS_DIR to point to the Mesa install we download for the DUT +echo "export SCRIPTS_DIR='$CI_PROJECT_DIR/install'" >> dut-env-vars.sh -tar zcf job-rootfs-overlay.tar.gz -C results/job-rootfs-overlay/ . -ci-fairy s3cp --token-file "${S3_JWT_FILE}" job-rootfs-overlay.tar.gz "https://${JOB_ROOTFS_OVERLAY_PATH}" +fdo_log_section_end prepare_rootfs # Prepare env vars for upload. -section_switch variables "Environment variables passed through to device:" -cat results/job-rootfs-overlay/set-job-env-vars.sh +fdo_log_section_start_collapsed variables "Environment variables passed through to device:" +cat dut-env-vars.sh +fdo_log_section_end variables -section_switch lava_submit "Submitting job for scheduling" +fdo_log_section_start_collapsed lava_submit "Submitting job for scheduling" touch results/lava.log tail -f results/lava.log & # Ensure that we are printing the commands that are being executed, # making it easier to debug the job in case it fails. set -x -PYTHONPATH=artifacts/ artifacts/lava/lava_job_submitter.py \ + +# List of optional overlays +LAVA_EXTRA_OVERLAYS=() +if [ -n "${LAVA_FIRMWARE:-}" ]; then + for fw in $LAVA_FIRMWARE; do + LAVA_EXTRA_OVERLAYS+=( + - append-overlay + --name=linux-firmware + --url="https://${S3_BASE_PATH}/${FIRMWARE_REPO}/${fw}-${FIRMWARE_TAG}.tar" + --path="/" + --format=tar + ) + done +fi +LAVA_EXTRA_OVERLAYS+=( + - append-overlay \ + --name=kernel-build \ + --url="${FDO_HTTP_CACHE_URI:-}https://${PIPELINE_ARTIFACTS_BASE}/${DEBIAN_ARCH}/kernel-files.tar.zst" \ + --compression=zstd \ + --path="${CI_PROJECT_DIR}" \ + --format=tar \ +) + +lava-job-submitter \ --farm "${FARM}" \ --device-type "${DEVICE_TYPE}" \ --boot-method "${BOOT_METHOD}" \ @@ -75,9 +79,8 @@ PYTHONPATH=artifacts/ artifacts/lava/lava_job_submitter.py \ --pipeline-info "$CI_JOB_NAME: $CI_PIPELINE_URL on $CI_COMMIT_REF_NAME ${CI_NODE_INDEX}/${CI_NODE_TOTAL}" \ --rootfs-url "${ROOTFS_URL}" \ --kernel-url-prefix "https://${PIPELINE_ARTIFACTS_BASE}/${DEBIAN_ARCH}" \ - --kernel-external "${EXTERNAL_KERNEL_TAG}" \ - --first-stage-init artifacts/ci-common/init-stage1.sh \ --dtb-filename "${DTB}" \ + --env-file dut-env-vars.sh \ --jwt-file "${S3_JWT_FILE}" \ --kernel-image-name "${KERNEL_IMAGE_NAME}" \ --kernel-image-type "${KERNEL_IMAGE_TYPE}" \ @@ -86,20 +89,10 @@ PYTHONPATH=artifacts/ artifacts/lava/lava_job_submitter.py \ --mesa-job-name "$CI_JOB_NAME" \ --structured-log-file "results/lava_job_detail.json" \ --ssh-client-image "${LAVA_SSH_CLIENT_IMAGE}" \ + --project-dir "${CI_PROJECT_DIR}" \ --project-name "${CI_PROJECT_NAME}" \ - --starting-section "${CURRENT_SECTION}" \ + --starting-section lava_submit \ --job-submitted-at "${CI_JOB_STARTED_AT}" \ - - append-overlay \ - --name=kernel-build \ - --url="${FDO_HTTP_CACHE_URI:-}https://${PIPELINE_ARTIFACTS_BASE}/${DEBIAN_ARCH}/kernel-files.tar.zst" \ - --compression=zstd \ - --path="${CI_PROJECT_DIR}" \ - --format=tar \ - - append-overlay \ - --name=job-overlay \ - --url="https://${JOB_ROOTFS_OVERLAY_PATH}" \ - --compression=gz \ - --path="/" \ - --format=tar \ + "${LAVA_EXTRA_OVERLAYS[@]}" \ - submit \ >> results/lava.log diff --git a/drivers/gpu/drm/ci/static-checks.yml b/drivers/gpu/drm/ci/static-checks.yml index 13ffa827b7fa..712d3efe99c6 100644 --- a/drivers/gpu/drm/ci/static-checks.yml +++ b/drivers/gpu/drm/ci/static-checks.yml @@ -1,4 +1,5 @@ check-patch: + stage: static-checks extends: - .build - .use-debian/x86_64_build diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index af094153d987..c314926e3693 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -7,13 +7,16 @@ .lava-test: extends: - - .container+build-rules + - .build-rules - .allow_failure_lockdep timeout: "1h30m" rules: - !reference [.scheduled_pipeline-rules, rules] - !reference [.collabora-farm-rules, rules] - when: on_success + before_script: + # lava-submit.sh is a part of the archive, unlike Mesa CI + - eval "$S3_JWT_FILE_SCRIPT" script: # Note: Build dir (and thus install) may be dirty due to GIT_STRATEGY - rm -rf install @@ -32,9 +35,7 @@ dependencies: - testing:arm32 needs: - - alpine/x86_64_lava_ssh_client - debian/arm32_test-gl - - python-artifacts - testing:arm32 - igt:arm32 @@ -48,9 +49,7 @@ dependencies: - testing:arm64 needs: - - alpine/x86_64_lava_ssh_client - debian/arm64_test-gl - - python-artifacts - testing:arm64 - igt:arm64 @@ -64,37 +63,10 @@ dependencies: - testing:x86_64 needs: - - alpine/x86_64_lava_ssh_client - debian/x86_64_test-gl - - python-artifacts - testing:x86_64 - igt:x86_64 -.baremetal-igt-arm64: - extends: - - .baremetal-test-arm64-gl - - .use-debian/baremetal_arm64_test-gl - - .allow_failure_lockdep - timeout: "1h30m" - rules: - - !reference [.scheduled_pipeline-rules, rules] - - !reference [.google-freedreno-farm-rules, rules] - - when: on_success - variables: - FDO_CI_CONCURRENT: 10 - HWCI_TEST_SCRIPT: "/install/igt_runner.sh" - S3_ARTIFACT_NAME: "arm64/kernel-files" - BM_KERNEL: https://${PIPELINE_ARTIFACTS_BASE}/arm64/Image.gz - BM_CMDLINE: "ip=dhcp console=ttyMSM0,115200n8 $BM_KERNEL_EXTRA_ARGS root=/dev/nfs rw nfsrootdebug nfsroot=,tcp,nfsvers=4.2 init=/init $BM_KERNELARGS" - FARM: google - needs: - - debian/baremetal_arm64_test-gl - - job: testing:arm64 - artifacts: false - - igt:arm64 - tags: - - $RUNNER_TAG - .software-driver: stage: software-driver extends: @@ -110,6 +82,7 @@ - !reference [default, before_script] - rm -rf install - tar -xf artifacts/install.tar + - mkdir -p /kernel script: - ln -sf $CI_PROJECT_DIR/install /install - mv install/bzImage /kernel/bzImage @@ -127,6 +100,7 @@ DRIVER_NAME: msm BOOT_METHOD: depthcharge KERNEL_IMAGE_TYPE: "" + LAVA_FIRMWARE: qcom-lava msm:sc7180-trogdor-lazor-limozeen: extends: @@ -148,9 +122,7 @@ msm:sc7180-trogdor-kingoftown: GPU_VERSION: ${DEVICE_TYPE} RUNNER_TAG: mesa-ci-x86-64-lava-sc7180-trogdor-kingoftown -msm:apq8016: - extends: - - .baremetal-igt-arm64 +.msm:apq8016: stage: msm variables: DEVICE_TYPE: apq8016-sbc-usb-host @@ -165,9 +137,7 @@ msm:apq8016: script: - ./install/bare-metal/fastboot.sh || exit $? -msm:apq8096: - extends: - - .baremetal-igt-arm64 +.msm:apq8096: stage: msm variables: DEVICE_TYPE: apq8096-db820c @@ -194,11 +164,14 @@ msm:sm8350-hdk: KERNEL_IMAGE_NAME: "Image.gz" KERNEL_IMAGE_TYPE: "" RUNNER_TAG: mesa-ci-x86-64-lava-sm8350-hdk + LAVA_FIRMWARE: qcom-lava + LAVA_FASTBOOT_CMD: "set_active a" .rockchip-device: variables: DTB: ${DEVICE_TYPE} BOOT_METHOD: depthcharge + LAVA_FIRMWARE: arm .rockchip-display: stage: rockchip @@ -255,6 +228,7 @@ panfrost:rk3399: DTB: "" BOOT_METHOD: depthcharge KERNEL_IMAGE_TYPE: "" + LAVA_FIRMWARE: i915 i915:apl: extends: @@ -337,6 +311,7 @@ i915:jsl: DTB: "" BOOT_METHOD: depthcharge KERNEL_IMAGE_TYPE: "" + LAVA_FIRMWARE: amdgpu-lava amdgpu:stoney: extends: @@ -355,6 +330,7 @@ amdgpu:stoney: DTB: ${DEVICE_TYPE} BOOT_METHOD: depthcharge KERNEL_IMAGE_TYPE: "" + LAVA_FIRMWARE: arm .mediatek-display: stage: mediatek diff --git a/drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt index f44dbce3151a..442d3bc3d90b 100644 --- a/drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt +++ b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt @@ -3,9 +3,10 @@ amdgpu/amd_abm@abm_gradual,Fail amdgpu/amd_abm@backlight_monotonic_abm,Fail amdgpu/amd_abm@backlight_monotonic_basic,Fail amdgpu/amd_abm@dpms_cycle,Fail -amdgpu/amd_assr@assr-links,Fail amdgpu/amd_assr@assr-links-dpms,Fail -amdgpu/amd_mall@static-screen,Crash +amdgpu/amd_assr@assr-links,Fail +amdgpu/amd_basic@cs-gfx-with-IP-GFX,Fail +amdgpu/amd_basic@cs-multi-fence-with-IP-GFX,Fail amdgpu/amd_mode_switch@mode-switch-first-last-pipe-2,Crash amdgpu/amd_plane@mpo-pan-nv12,Fail amdgpu/amd_plane@mpo-pan-p010,Fail @@ -13,11 +14,13 @@ amdgpu/amd_plane@mpo-pan-rgb,Crash amdgpu/amd_plane@mpo-scale-nv12,Fail amdgpu/amd_plane@mpo-scale-p010,Fail amdgpu/amd_plane@mpo-scale-rgb,Crash -amdgpu/amd_plane@mpo-swizzle-toggle,Fail +amdgpu/amd_plane@mpo-swizzle-toggle,Crash amdgpu/amd_uvd_dec@amdgpu_uvd_decode,Fail +core_setmaster@master-drop-set-user,Fail kms_addfb_basic@bad-pitch-65536,Fail kms_addfb_basic@bo-too-small,Fail kms_addfb_basic@too-high,Fail +kms_async_flips@basic-modeset-with-all-modifiers-formats,Crash kms_atomic_transition@plane-all-modeset-transition-internal-panels,Fail kms_atomic_transition@plane-all-transition,Fail kms_atomic_transition@plane-all-transition-nonblocking,Fail @@ -33,8 +36,10 @@ kms_cursor_crc@cursor-sliding-64x64,Fail kms_cursor_edge_walk@64x64-left-edge,Fail kms_flip@flip-vs-modeset-vs-hang,Fail kms_flip@flip-vs-panning-vs-hang,Fail +kms_invalid_mode@int-max-clock,Fail +kms_invalid_mode@overflow-vrefresh,Fail kms_lease@lease-uevent,Fail -kms_plane@pixel-format,Fail kms_plane_cursor@primary,Fail +kms_plane@pixel-format,Fail kms_rotation_crc@primary-rotation-180,Fail perf@i915-ref-count,Fail diff --git a/drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt index adffb011298a..8b81af104b30 100644 --- a/drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt +++ b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt @@ -32,3 +32,10 @@ kms_async_flips@async-flip-with-page-flip-events-atomic # IGT Version: 1.29-g33adea9eb # Linux Version: 6.13.0-rc2 kms_async_flips@crc-atomic + +# Board Name: hp-11A-G6-EE-grunt +# Bug Report: https://gitlab.freedesktop.org/drm/amd/-/issues/4406 +# Failure Rate: 20 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_async_flips@alternate-sync-async-flip diff --git a/drivers/gpu/drm/ci/xfails/i915-amly-fails.txt b/drivers/gpu/drm/ci/xfails/i915-amly-fails.txt index 8e2b5504004e..ff0c10626004 100644 --- a/drivers/gpu/drm/ci/xfails/i915-amly-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-amly-fails.txt @@ -1,43 +1,18 @@ -core_setmaster_vs_auth,Fail i915_module_load@load,Fail i915_module_load@reload,Fail i915_module_load@reload-no-display,Fail i915_module_load@resize-bar,Fail i915_pm_rpm@gem-execbuf-stress,Timeout i915_pm_rpm@module-reload,Fail -kms_ccs@ccs-on-another-bo-y-tiled-gen12-rc-ccs-cc,Timeout -kms_fb_coherency@memset-crc,Crash -kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail -kms_frontbuffer_tracking@fbc-rgb101010-draw-mmap-cpu,Timeout kms_lease@lease-uevent,Fail kms_plane_alpha_blend@alpha-basic,Fail kms_plane_alpha_blend@alpha-opaque-fb,Fail kms_plane_alpha_blend@alpha-transparent-fb,Fail kms_plane_alpha_blend@constant-alpha-max,Fail -kms_plane_scaling@planes-upscale-factor-0-25,Timeout -kms_pm_backlight@brightness-with-dpms,Crash -kms_pm_backlight@fade,Crash -kms_prop_blob@invalid-set-prop-any,Fail -kms_properties@connector-properties-legacy,Timeout +kms_pm_rpm@modeset-stress-extra-wait,Timeout kms_universal_plane@disable-primary-vs-flip,Timeout perf@i915-ref-count,Fail perf_pmu@module-unload,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-apl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-apl-fails.txt index 7353ab11e940..032f7adeeff2 100644 --- a/drivers/gpu/drm/ci/xfails/i915-apl-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-apl-fails.txt @@ -1,29 +1,9 @@ +core_setmaster@master-drop-set-user,Fail i915_module_load@load,Fail i915_module_load@reload,Fail i915_module_load@reload-no-display,Fail i915_module_load@resize-bar,Fail -kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail +kms_flip@flip-vs-wf_vblank-interruptible,Fail kms_lease@lease-uevent,Fail kms_plane_alpha_blend@alpha-basic,Fail kms_plane_alpha_blend@alpha-opaque-fb,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-cml-fails.txt b/drivers/gpu/drm/ci/xfails/i915-cml-fails.txt index 6fef7c1e56ea..351cb06228ff 100644 --- a/drivers/gpu/drm/ci/xfails/i915-cml-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-cml-fails.txt @@ -1,4 +1,5 @@ -core_setmaster_vs_auth,Fail +api_intel_bb@intel-bb-blit-none,Timeout +core_setmaster@master-drop-set-user,Fail i915_module_load@load,Fail i915_module_load@reload,Fail i915_module_load@reload-no-display,Fail @@ -8,18 +9,15 @@ i915_pipe_stress@stress-xrgb8888-ytiled,Fail i915_pm_rpm@gem-execbuf-stress,Timeout i915_pm_rpm@module-reload,Fail i915_pm_rpm@system-suspend-execbuf,Timeout -kms_ccs@ccs-on-another-bo-y-tiled-gen12-rc-ccs-cc,Timeout -kms_cursor_crc@cursor-suspend,Timeout -kms_fb_coherency@memset-crc,Crash kms_flip@busy-flip,Timeout kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail @@ -31,39 +29,18 @@ kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail kms_lease@lease-uevent,Fail -kms_pipe_stress@stress-xrgb8888-untiled,Fail -kms_pipe_stress@stress-xrgb8888-ytiled,Fail -kms_plane_alpha_blend@alpha-basic,Fail -kms_plane_alpha_blend@alpha-opaque-fb,Fail -kms_plane_alpha_blend@alpha-transparent-fb,Fail -kms_plane_alpha_blend@constant-alpha-max,Fail -kms_plane_scaling@planes-upscale-factor-0-25,Timeout -kms_pm_backlight@brightness-with-dpms,Crash -kms_pm_backlight@fade,Crash -kms_prop_blob@invalid-set-prop-any,Fail -kms_properties@connector-properties-legacy,Timeout +kms_pm_rpm@basic-rte,Fail kms_psr2_sf@cursor-plane-update-sf,Fail kms_psr2_sf@overlay-plane-update-continuous-sf,Fail kms_psr2_sf@overlay-plane-update-sf-dmg-area,Fail kms_psr2_sf@overlay-primary-update-sf-dmg-area,Fail kms_psr2_sf@plane-move-sf-dmg-area,Fail -kms_psr2_sf@primary-plane-update-sf-dmg-area,Fail kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb,Fail -kms_psr2_sf@psr2-cursor-plane-update-sf,Fail -kms_psr2_sf@psr2-overlay-plane-update-continuous-sf,Fail -kms_psr2_sf@psr2-overlay-plane-update-sf-dmg-area,Fail -kms_psr2_sf@psr2-overlay-primary-update-sf-dmg-area,Fail -kms_psr2_sf@psr2-plane-move-sf-dmg-area,Fail -kms_psr2_sf@psr2-primary-plane-update-sf-dmg-area,Fail -kms_psr2_sf@psr2-primary-plane-update-sf-dmg-area-big-fb,Fail -kms_psr2_su@page_flip-NV12,Fail -kms_psr2_su@page_flip-P010,Fail -kms_setmode@basic,Fail -kms_universal_plane@disable-primary-vs-flip,Timeout +kms_psr2_sf@primary-plane-update-sf-dmg-area,Fail perf@i915-ref-count,Fail perf_pmu@module-unload,Fail perf_pmu@rc6,Crash diff --git a/drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt index 5343cc1c8696..5e5b9901a08e 100644 --- a/drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt +++ b/drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt @@ -32,3 +32,10 @@ kms_async_flips@crc # IGT Version: 1.29-g33adea9eb # Linux Version: 6.13.0-rc2 gen9_exec_parse@unaligned-access + +# Board Name: asus-C436FA-Flip-hatch +# Bug Report: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/14625 +# Failure Rate: 100 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +perf_pmu@most-busy-check-all diff --git a/drivers/gpu/drm/ci/xfails/i915-glk-fails.txt b/drivers/gpu/drm/ci/xfails/i915-glk-fails.txt index 8adf5f0a6e80..5d79b65cd354 100644 --- a/drivers/gpu/drm/ci/xfails/i915-glk-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-glk-fails.txt @@ -4,41 +4,31 @@ i915_module_load@load,Fail i915_module_load@reload,Fail i915_module_load@reload-no-display,Fail i915_module_load@resize-bar,Fail +kms_dirtyfb@default-dirtyfb-ioctl,Fail kms_dirtyfb@drrs-dirtyfb-ioctl,Fail +kms_dirtyfb@fbc-dirtyfb-ioctl,Fail kms_flip@blocking-wf_vblank,Fail -kms_flip@wf_vblank-ts-check,Fail -kms_flip@wf_vblank-ts-check-interruptible,Fail -kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail -kms_frontbuffer_tracking@fbc-rgb101010-draw-mmap-cpu,Timeout +kms_flip@wf_vblank-ts-check,Fail +kms_flip@wf_vblank-ts-check-interruptible,Fail +kms_frontbuffer_tracking@fbcdrrs-tiling-linear,Fail kms_frontbuffer_tracking@fbc-tiling-linear,Fail kms_lease@lease-uevent,Fail kms_plane_alpha_blend@alpha-opaque-fb,Fail kms_plane_scaling@planes-upscale-factor-0-25,Timeout -kms_pm_backlight@brightness-with-dpms,Crash -kms_pm_backlight@fade,Crash kms_prop_blob@invalid-set-prop-any,Fail kms_properties@connector-properties-legacy,Timeout kms_rotation_crc@multiplane-rotation,Fail -kms_rotation_crc@multiplane-rotation-cropping-top,Fail kms_universal_plane@disable-primary-vs-flip,Timeout perf@non-zero-reason,Timeout sysfs_heartbeat_interval@long,Timeout diff --git a/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt index 57453e340040..9ad246917598 100644 --- a/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt @@ -1,45 +1,24 @@ +core_setmaster@master-drop-set-root,Fail drm_fdinfo@busy-check-all,Fail i915_module_load@load,Fail i915_module_load@reload,Fail i915_module_load@reload-no-display,Fail i915_module_load@resize-bar,Fail i915_pm_rpm@gem-execbuf-stress,Timeout +i915_pm_rpm@module-reload,Fail kms_flip@dpms-off-confusion,Fail -kms_flip@nonexisting-fb,Fail -kms_flip@single-buffer-flip-vs-dpms-off-vs-modeset,Fail -kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,UnexpectedImprovement(Skip) -kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail kms_lease@lease-uevent,Fail kms_pm_rpm@modeset-stress-extra-wait,Timeout -kms_rotation_crc@bad-pixel-format,Fail kms_rotation_crc@multiplane-rotation,Fail -kms_rotation_crc@multiplane-rotation-cropping-bottom,Fail -kms_rotation_crc@multiplane-rotation-cropping-top,Fail perf@i915-ref-count,Fail perf_pmu@module-unload,Fail -perf_pmu@most-busy-idle-check-all,Fail perf_pmu@rc6,Crash -prime_busy@before-wait,Fail sysfs_heartbeat_interval@long,Timeout sysfs_heartbeat_interval@off,Timeout sysfs_preempt_timeout@off,Timeout diff --git a/drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt index 117098bc95d9..f37302331516 100644 --- a/drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt @@ -1,11 +1,12 @@ +core_setmaster@master-drop-set-user,Fail i915_module_load@load,Fail i915_module_load@reload,Fail i915_module_load@reload-no-display,Fail i915_module_load@resize-bar,Fail i915_pm_rpm@gem-execbuf-stress,Timeout kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail @@ -17,6 +18,8 @@ perf@i915-ref-count,Fail perf_pmu@busy-accuracy-50,Fail perf_pmu@module-unload,Fail perf_pmu@rc6,Crash +prime_busy@after-wait,Fail +prime_busy@before,Fail sysfs_heartbeat_interval@long,Timeout sysfs_heartbeat_interval@off,Timeout sysfs_preempt_timeout@off,Timeout diff --git a/drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt index 462c050a8b2d..102c4b3eef4d 100644 --- a/drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt @@ -1,14 +1,14 @@ api_intel_allocator@reopen,Timeout api_intel_bb@destroy-bb,Timeout core_hotunplug@hotrebind-lateclose,Timeout +core_setmaster@master-drop-set-user,Fail +drm_read@short-buffer-block,Timeout dumb_buffer@map-valid,Timeout i915_module_load@load,Fail i915_module_load@reload,Fail i915_module_load@reload-no-display,Fail i915_module_load@resize-bar,Fail -i915_pm_rpm@gem-execbuf-stress,Timeout i915_pm_rps@engine-order,Timeout -i915_pm_rps@waitboost,Fail kms_lease@lease-uevent,Fail kms_rotation_crc@multiplane-rotation,Fail perf@i915-ref-count,Fail @@ -17,7 +17,6 @@ perf_pmu@enable-race,Timeout perf_pmu@module-unload,Fail perf_pmu@rc6,Crash perf_pmu@semaphore-wait-idle,Timeout -prime_busy@before,Fail prime_mmap@test_refcounting,Timeout sriov_basic@enable-vfs-bind-unbind-each-numvfs-all,Timeout syncobj_basic@illegal-fd-to-handle,Timeout diff --git a/drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt new file mode 100644 index 000000000000..9738006e3759 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt @@ -0,0 +1,6 @@ +# Board Name: acer-cp514-2h-1130g7-volteer +# Bug Report: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/14624 +# Failure Rate: 100 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +perf@gen12-unprivileged-single-ctx-counters diff --git a/drivers/gpu/drm/ci/xfails/i915-whl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-whl-fails.txt index 0f167cfd503c..3bb5496036d8 100644 --- a/drivers/gpu/drm/ci/xfails/i915-whl-fails.txt +++ b/drivers/gpu/drm/ci/xfails/i915-whl-fails.txt @@ -6,17 +6,17 @@ i915_module_load@resize-bar,Fail i915_pm_rpm@gem-execbuf-stress,Timeout i915_pm_rpm@module-reload,Fail i915_pm_rpm@system-suspend-execbuf,Timeout -kms_ccs@ccs-on-another-bo-y-tiled-gen12-rc-ccs-cc,Timeout -kms_cursor_crc@cursor-suspend,Timeout +kms_dirtyfb@default-dirtyfb-ioctl,Fail +kms_dirtyfb@fbc-dirtyfb-ioctl,Fail kms_fb_coherency@memset-crc,Crash kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail @@ -26,10 +26,9 @@ kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail -kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail -kms_frontbuffer_tracking@fbc-rgb101010-draw-mmap-cpu,Timeout +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail kms_frontbuffer_tracking@fbc-tiling-linear,Fail kms_lease@lease-uevent,Fail kms_plane_alpha_blend@alpha-basic,Fail @@ -37,8 +36,6 @@ kms_plane_alpha_blend@alpha-opaque-fb,Fail kms_plane_alpha_blend@alpha-transparent-fb,Fail kms_plane_alpha_blend@constant-alpha-max,Fail kms_plane_scaling@planes-upscale-factor-0-25,Timeout -kms_pm_backlight@brightness-with-dpms,Crash -kms_pm_backlight@fade,Crash kms_prop_blob@invalid-set-prop-any,Fail kms_properties@connector-properties-legacy,Timeout kms_universal_plane@disable-primary-vs-flip,Timeout diff --git a/drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt b/drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt index 592d7d69e6fc..31b8a0b27e13 100644 --- a/drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt +++ b/drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt @@ -1,7 +1,8 @@ +core_setmaster@master-drop-set-root,Fail +core_setmaster@master-drop-set-shared-fd,Fail +core_setmaster@master-drop-set-user,Fail kms_3d,Fail -kms_bw@connected-linear-tiling-1-displays-1920x1080p,Fail kms_bw@connected-linear-tiling-1-displays-2560x1440p,Fail -kms_bw@connected-linear-tiling-1-displays-3840x2160p,Fail kms_bw@connected-linear-tiling-2-displays-1920x1080p,Fail kms_bw@connected-linear-tiling-2-displays-2160x1440p,Fail kms_bw@connected-linear-tiling-2-displays-2560x1440p,Fail @@ -14,9 +15,7 @@ kms_bw@linear-tiling-2-displays-1920x1080p,Fail kms_bw@linear-tiling-2-displays-2160x1440p,Fail kms_bw@linear-tiling-2-displays-2560x1440p,Fail kms_bw@linear-tiling-2-displays-3840x2160p,Fail -kms_color@invalid-gamma-lut-sizes,Fail kms_cursor_legacy@cursor-vs-flip-atomic,Fail -kms_cursor_legacy@cursor-vs-flip-legacy,Fail kms_cursor_legacy@flip-vs-cursor-atomic,Fail kms_cursor_legacy@flip-vs-cursor-legacy,Fail kms_cursor_legacy@flip-vs-cursor-toggle,Fail @@ -24,9 +23,9 @@ kms_cursor_legacy@flip-vs-cursor-varying-size,Fail kms_flip@basic-plain-flip,Fail kms_flip@dpms-off-confusion,Fail kms_flip@dpms-off-confusion-interruptible,Fail -kms_flip@flip-vs-absolute-wf_vblank,Fail -kms_flip@flip-vs-absolute-wf_vblank-interruptible,Fail kms_flip@flip-vs-blocking-wf-vblank,Fail +kms_flip@flip-vs-dpms-on-nop,Fail +kms_flip@flip-vs-dpms-on-nop-interruptible,Fail kms_flip@flip-vs-expired-vblank,Fail kms_flip@flip-vs-expired-vblank-interruptible,Fail kms_flip@flip-vs-modeset-vs-hang,Fail @@ -40,5 +39,4 @@ kms_flip@plain-flip-fb-recreate-interruptible,Fail kms_flip@plain-flip-interruptible,Fail kms_flip@plain-flip-ts-check,Fail kms_flip@plain-flip-ts-check-interruptible,Fail -kms_invalid_mode@overflow-vrefresh,Fail kms_lease@lease-uevent,Fail diff --git a/drivers/gpu/drm/ci/xfails/mediatek-mt8173-flakes.txt b/drivers/gpu/drm/ci/xfails/mediatek-mt8173-flakes.txt index 443596d9e662..6f4d41e16e19 100644 --- a/drivers/gpu/drm/ci/xfails/mediatek-mt8173-flakes.txt +++ b/drivers/gpu/drm/ci/xfails/mediatek-mt8173-flakes.txt @@ -53,3 +53,38 @@ kms_bw@connected-linear-tiling-1-displays-2160x1440p # IGT Version: 1.30-g04bedb923 # Linux Version: 6.14.0-rc4 kms_flip@flip-vs-wf_vblank-interruptible + +# Board Name: mt8173-elm-hana +# Bug Report: https://lore.kernel.org/dri-devel/7559dd68-c9dd-410f-880f-201679e2dd54@collabora.com/T/#u +# Failure Rate: 20 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@blocking-wf_vblank + +# Board Name: mt8173-elm-hana +# Bug Report: https://lore.kernel.org/dri-devel/953ab66e-9dda-4003-9b98-9e0d81e18a1f@collabora.com/T/#u +# Failure Rate: 40 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@busy-flip + +# Board Name: mt8173-elm-hana +# Bug Report: https://lore.kernel.org/dri-devel/6ab7f59c-042e-4c7a-baaa-86c7d47ab308@collabora.com/ +# Failure Rate: 40 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@flip-vs-rmfb + +# Board Name: mt8173-elm-hana +# Bug Report: https://lore.kernel.org/dri-devel/30b3f8b0-3409-4329-bb60-b6287e1a439d@collabora.com/ +# Failure Rate: 60 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_atomic_transition@plane-all-modeset-transition-internal-panels + +# Board Name: mt8173-elm-hana +# Bug Report: https://lore.kernel.org/dri-devel/4c9e1501-52cd-4659-a894-8a2ac58c3996@collabora.com/ +# Failure Rate: 40 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@absolute-wf_vblank diff --git a/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-kingoftown-fails.txt b/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-kingoftown-fails.txt index ae36a39619c6..2daf4ab879a9 100644 --- a/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-kingoftown-fails.txt +++ b/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-kingoftown-fails.txt @@ -1,3 +1,4 @@ +core_setmaster@master-drop-set-user,Fail kms_color@ctm-0-25,Fail kms_color@ctm-0-50,Fail kms_color@ctm-0-75,Fail @@ -14,8 +15,8 @@ kms_flip@flip-vs-modeset-vs-hang,Fail kms_flip@flip-vs-panning-vs-hang,Fail kms_lease@lease-uevent,Fail kms_pipe_crc_basic@compare-crc-sanitycheck-nv12,Fail -kms_plane@pixel-format,Fail -kms_plane@pixel-format-source-clamping,Fail kms_plane_alpha_blend@alpha-7efc,Fail kms_plane_alpha_blend@coverage-7efc,Fail kms_plane_alpha_blend@coverage-vs-premult-vs-constant,Fail +kms_plane@pixel-format,Fail +kms_plane@pixel-format-source-clamping,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-lazor-limozeen-fails.txt b/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-lazor-limozeen-fails.txt index ae36a39619c6..2daf4ab879a9 100644 --- a/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-lazor-limozeen-fails.txt +++ b/drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-lazor-limozeen-fails.txt @@ -1,3 +1,4 @@ +core_setmaster@master-drop-set-user,Fail kms_color@ctm-0-25,Fail kms_color@ctm-0-50,Fail kms_color@ctm-0-75,Fail @@ -14,8 +15,8 @@ kms_flip@flip-vs-modeset-vs-hang,Fail kms_flip@flip-vs-panning-vs-hang,Fail kms_lease@lease-uevent,Fail kms_pipe_crc_basic@compare-crc-sanitycheck-nv12,Fail -kms_plane@pixel-format,Fail -kms_plane@pixel-format-source-clamping,Fail kms_plane_alpha_blend@alpha-7efc,Fail kms_plane_alpha_blend@coverage-7efc,Fail kms_plane_alpha_blend@coverage-vs-premult-vs-constant,Fail +kms_plane@pixel-format,Fail +kms_plane@pixel-format-source-clamping,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt index 8d26b23133aa..f387c73193c6 100644 --- a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt +++ b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt @@ -1,3 +1,4 @@ +core_setmaster@master-drop-set-user,Fail kms_3d,Fail kms_cursor_legacy@forked-bo,Fail kms_cursor_legacy@forked-move,Fail diff --git a/drivers/gpu/drm/ci/xfails/panfrost-mt8183-fails.txt b/drivers/gpu/drm/ci/xfails/panfrost-mt8183-fails.txt index abd1ccb71561..cc5f7fe98dd7 100644 --- a/drivers/gpu/drm/ci/xfails/panfrost-mt8183-fails.txt +++ b/drivers/gpu/drm/ci/xfails/panfrost-mt8183-fails.txt @@ -1,2 +1,3 @@ +core_setmaster@master-drop-set-user,Fail panfrost/panfrost_prime@gem-prime-import,Fail panfrost/panfrost_submit@pan-submit-error-bad-requirements,Fail diff --git a/drivers/gpu/drm/ci/xfails/panfrost-rk3288-fails.txt b/drivers/gpu/drm/ci/xfails/panfrost-rk3288-fails.txt index 8330b934602a..8d0b7682e640 100644 --- a/drivers/gpu/drm/ci/xfails/panfrost-rk3288-fails.txt +++ b/drivers/gpu/drm/ci/xfails/panfrost-rk3288-fails.txt @@ -1,2 +1,3 @@ +core_setmaster@master-drop-set-user,Crash panfrost/panfrost_prime@gem-prime-import,Crash panfrost/panfrost_submit@pan-submit-error-bad-requirements,Crash diff --git a/drivers/gpu/drm/ci/xfails/panfrost-rk3399-fails.txt b/drivers/gpu/drm/ci/xfails/panfrost-rk3399-fails.txt index abd1ccb71561..cc5f7fe98dd7 100644 --- a/drivers/gpu/drm/ci/xfails/panfrost-rk3399-fails.txt +++ b/drivers/gpu/drm/ci/xfails/panfrost-rk3399-fails.txt @@ -1,2 +1,3 @@ +core_setmaster@master-drop-set-user,Fail panfrost/panfrost_prime@gem-prime-import,Fail panfrost/panfrost_submit@pan-submit-error-bad-requirements,Fail diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt index 61122ea7f008..526f582038d3 100644 --- a/drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt @@ -2,8 +2,17 @@ core_setmaster@master-drop-set-root,Crash core_setmaster@master-drop-set-shared-fd,Crash core_setmaster@master-drop-set-user,Crash core_setmaster_vs_auth,Crash -dumb_buffer@create-clear,Crash fbdev@pan,Crash -kms_cursor_legacy@basic-flip-before-cursor-legacy,Fail -kms_prop_blob@invalid-set-prop,Crash +kms_cursor_crc@cursor-dpms,Crash +kms_cursor_crc@cursor-sliding-32x32,Crash +kms_cursor_legacy@basic-flip-before-cursor-atomic,Crash +kms_cursor_legacy@cursor-vs-flip-atomic,Crash +kms_flip@basic-flip-vs-wf_vblank,Crash +kms_flip@flip-vs-panning-vs-hang,Crash +kms_flip@plain-flip-fb-recreate-interruptible,Crash +kms_pipe_crc_basic@read-crc-frame-sequence,Crash +kms_plane_cursor@overlay,Crash +kms_plane_cursor@viewport,Crash kms_prop_blob@invalid-set-prop-any,Crash +kms_prop_blob@invalid-set-prop,Crash +kms_properties@get_properties-sanity-non-atomic,Fail diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt index cd0b27d8b636..34f43e95a51b 100644 --- a/drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt @@ -32,3 +32,24 @@ kms_flip@plain-flip-ts-check # IGT Version: 1.28-ga73311079 # Linux Version: 6.11.0-rc2 kms_cursor_crc@cursor-alpha-opaque + +# Board Name: rk3288-veyron-jaq +# Bug Report: https://lore.kernel.org/dri-devel/acfd5838-d861-4dd9-97c3-99fffc9bfa04@collabora.com/T/#u +# Failure Rate: 40 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@flip-vs-absolute-wf_vblank + +# Board Name: rk3288-veyron-jaq +# Bug Report: https://lore.kernel.org/dri-devel/81e13fcc-d916-4eb8-91cd-f74f64f53f72@collabora.com/T/#u +# Failure Rate: 40 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@flip-vs-dpms-on-nop-interruptible + +# Board Name: rk3288-veyron-jaq +# Bug Report: https://lore.kernel.org/dri-devel/10c5abab-c8fe-4eff-8eed-009038436b49@collabora.com/T/#u +# Failure Rate: 20 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@plain-flip-fb-recreate diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt index 45dd8d493f6e..5110ce2dc56e 100644 --- a/drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt @@ -1,9 +1,9 @@ -dumb_buffer@create-clear,Crash +core_setmaster@master-drop-set-user,Fail kms_atomic_transition@modeset-transition,Fail kms_atomic_transition@modeset-transition-fencing,Fail kms_atomic_transition@plane-toggle-modeset-transition,Fail -kms_color@gamma,Fail -kms_color@legacy-gamma,Fail +kms_cursor_crc@async-cursor-crc-framebuffer-change,Fail +kms_cursor_crc@async-cursor-crc-position-change,Fail kms_cursor_crc@cursor-alpha-opaque,Fail kms_cursor_crc@cursor-alpha-transparent,Fail kms_cursor_crc@cursor-dpms,Fail @@ -41,8 +41,11 @@ kms_cursor_legacy@flip-vs-cursor-crc-atomic,Fail kms_cursor_legacy@flip-vs-cursor-crc-legacy,Fail kms_cursor_legacy@flip-vs-cursor-legacy,Fail kms_cursor_legacy@long-nonblocking-modeset-vs-cursor-atomic,Fail +kms_flip@basic-flip-vs-dpms,Fail kms_flip@basic-flip-vs-wf_vblank,Fail kms_flip@blocking-wf_vblank,Fail +kms_flip@flip-vs-dpms-on-nop,Fail +kms_flip@flip-vs-dpms-on-nop-interruptible,Fail kms_flip@flip-vs-modeset-vs-hang,Fail kms_flip@flip-vs-panning,Fail kms_flip@flip-vs-panning-interruptible,Fail @@ -51,7 +54,6 @@ kms_flip@modeset-vs-vblank-race,Fail kms_flip@plain-flip-fb-recreate,Fail kms_flip@plain-flip-fb-recreate-interruptible,Fail kms_flip@plain-flip-ts-check,Fail -kms_flip@plain-flip-ts-check-interruptible,Fail kms_flip@wf_vblank-ts-check-interruptible,Fail kms_invalid_mode@int-max-clock,Fail kms_invalid_mode@overflow-vrefresh,Fail @@ -64,11 +66,11 @@ kms_pipe_crc_basic@nonblocking-crc,Fail kms_pipe_crc_basic@nonblocking-crc-frame-sequence,Fail kms_pipe_crc_basic@read-crc,Fail kms_pipe_crc_basic@read-crc-frame-sequence,Fail +kms_plane_cursor@primary,Fail kms_plane@pixel-format,Fail kms_plane@pixel-format-source-clamping,Fail kms_plane@plane-panning-bottom-right,Fail kms_plane@plane-panning-top-left,Fail kms_plane@plane-position-covered,Fail kms_plane@plane-position-hole,Fail -kms_plane_cursor@primary,Fail kms_universal_plane@universal-plane-functional,Fail diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt index b467991d4094..918dd33c2092 100644 --- a/drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt @@ -144,3 +144,38 @@ kms_bw@connected-linear-tiling-1-displays-2160x1440p # IGT Version: 1.30-g04bedb923 # Linux Version: 6.14.0-rc4 kms_bw@linear-tiling-1-displays-3840x2160p + +# Board Name: rk3399-gru-kevin +# Bug Report: https://lore.kernel.org/dri-devel/7b6e2e3b-2ea2-4cd7-92a5-68d23a63e426@collabora.com/T/#u +# Failure Rate: 60 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_color@gamma + +# Board Name: rk3399-gru-kevin +# Bug Report: https://lore.kernel.org/dri-devel/e29c2892-08f2-423f-af72-e4d8b207fd1c@collabora.com/T/#u +# Failure Rate: 60 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_bw@connected-linear-tiling-1-displays-3840x2160p + +# Board Name: rk3399-gru-kevin +# Bug Report: https://lore.kernel.org/dri-devel/ad9ce463-c803-4502-ae89-381a6b6eb19f@collabora.com/T/#u +# Failure Rate: 40 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_color@legacy-gamma + +# Board Name: rk3399-gru-kevin +# Bug Report: https://lore.kernel.org/dri-devel/59724e10-12ca-4481-b0e4-72d7b6e4dae0@collabora.com/T/#u +# Failure Rate: 40 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_flip@plain-flip-ts-check-interruptible + +# Board Name: rk3399-gru-kevin +# Bug Report: https://lore.kernel.org/dri-devel/d790db5f-a1ba-47f9-9af0-d3287ef3274c@collabora.com/T/#u +# Failure Rate: 20 +# IGT Version: 2.1-g26ddb59c1 +# Linux Version: 6.16.0-rc2 +kms_bw@linear-tiling-2-displays-3840x2160p diff --git a/drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt b/drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt index 9749ddb75121..62cb7b94f3fd 100644 --- a/drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt +++ b/drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt @@ -2,11 +2,6 @@ kms_addfb_basic@bad-pitch-65536,Fail kms_addfb_basic@bo-too-small,Fail kms_addfb_basic@size-max,Fail kms_addfb_basic@too-high,Fail -kms_atomic_transition@plane-primary-toggle-with-vblank-wait,Fail -kms_bw@connected-linear-tiling-1-displays-1920x1080p,Fail -kms_bw@connected-linear-tiling-1-displays-2160x1440p,Fail -kms_bw@connected-linear-tiling-1-displays-2560x1440p,Fail -kms_bw@connected-linear-tiling-1-displays-3840x2160p,Fail kms_bw@connected-linear-tiling-10-displays-1920x1080p,Fail kms_bw@connected-linear-tiling-10-displays-2160x1440p,Fail kms_bw@connected-linear-tiling-10-displays-2560x1440p,Fail @@ -35,6 +30,10 @@ kms_bw@connected-linear-tiling-16-displays-1920x1080p,Fail kms_bw@connected-linear-tiling-16-displays-2160x1440p,Fail kms_bw@connected-linear-tiling-16-displays-2560x1440p,Fail kms_bw@connected-linear-tiling-16-displays-3840x2160p,Fail +kms_bw@connected-linear-tiling-1-displays-1920x1080p,Fail +kms_bw@connected-linear-tiling-1-displays-2160x1440p,Fail +kms_bw@connected-linear-tiling-1-displays-2560x1440p,Fail +kms_bw@connected-linear-tiling-1-displays-3840x2160p,Fail kms_bw@connected-linear-tiling-2-displays-1920x1080p,Fail kms_bw@connected-linear-tiling-2-displays-2160x1440p,Fail kms_bw@connected-linear-tiling-2-displays-2560x1440p,Fail @@ -67,10 +66,6 @@ kms_bw@connected-linear-tiling-9-displays-1920x1080p,Fail kms_bw@connected-linear-tiling-9-displays-2160x1440p,Fail kms_bw@connected-linear-tiling-9-displays-2560x1440p,Fail kms_bw@connected-linear-tiling-9-displays-3840x2160p,Fail -kms_bw@linear-tiling-1-displays-1920x1080p,Fail -kms_bw@linear-tiling-1-displays-2160x1440p,Fail -kms_bw@linear-tiling-1-displays-2560x1440p,Fail -kms_bw@linear-tiling-1-displays-3840x2160p,Fail kms_bw@linear-tiling-10-displays-1920x1080p,Fail kms_bw@linear-tiling-10-displays-2160x1440p,Fail kms_bw@linear-tiling-10-displays-2560x1440p,Fail @@ -99,6 +94,10 @@ kms_bw@linear-tiling-16-displays-1920x1080p,Fail kms_bw@linear-tiling-16-displays-2160x1440p,Fail kms_bw@linear-tiling-16-displays-2560x1440p,Fail kms_bw@linear-tiling-16-displays-3840x2160p,Fail +kms_bw@linear-tiling-1-displays-1920x1080p,Fail +kms_bw@linear-tiling-1-displays-2160x1440p,Fail +kms_bw@linear-tiling-1-displays-2560x1440p,Fail +kms_bw@linear-tiling-1-displays-3840x2160p,Fail kms_bw@linear-tiling-2-displays-1920x1080p,Fail kms_bw@linear-tiling-2-displays-2160x1440p,Fail kms_bw@linear-tiling-2-displays-2560x1440p,Fail @@ -131,60 +130,11 @@ kms_bw@linear-tiling-9-displays-1920x1080p,Fail kms_bw@linear-tiling-9-displays-2160x1440p,Fail kms_bw@linear-tiling-9-displays-2560x1440p,Fail kms_bw@linear-tiling-9-displays-3840x2160p,Fail -kms_flip@absolute-wf_vblank,Fail -kms_flip@absolute-wf_vblank-interruptible,Fail -kms_flip@basic-flip-vs-wf_vblank,Fail -kms_flip@blocking-absolute-wf_vblank,Fail -kms_flip@blocking-absolute-wf_vblank-interruptible,Fail -kms_flip@blocking-wf_vblank,Fail -kms_flip@busy-flip,Fail -kms_flip@dpms-vs-vblank-race,Fail -kms_flip@dpms-vs-vblank-race-interruptible,Fail -kms_flip@flip-vs-absolute-wf_vblank,Fail -kms_flip@flip-vs-absolute-wf_vblank-interruptible,Fail -kms_flip@flip-vs-blocking-wf-vblank,Fail -kms_flip@flip-vs-expired-vblank,Fail -kms_flip@flip-vs-expired-vblank-interruptible,Fail kms_flip@flip-vs-modeset-vs-hang,Fail kms_flip@flip-vs-panning-vs-hang,Fail -kms_flip@flip-vs-wf_vblank-interruptible,Fail -kms_flip@modeset-vs-vblank-race,Fail -kms_flip@modeset-vs-vblank-race-interruptible,Fail -kms_flip@plain-flip-fb-recreate,Fail -kms_flip@plain-flip-fb-recreate-interruptible,Fail -kms_flip@plain-flip-ts-check,Fail -kms_flip@plain-flip-ts-check-interruptible,Fail -kms_flip@wf_vblank-ts-check,Fail -kms_flip@wf_vblank-ts-check-interruptible,Fail kms_invalid_mode@int-max-clock,Fail kms_invalid_mode@overflow-vrefresh,Fail -kms_lease@cursor-implicit-plane,Fail kms_lease@lease-uevent,Fail -kms_lease@page-flip-implicit-plane,Fail -kms_lease@setcrtc-implicit-plane,Fail -kms_lease@simple-lease,Fail -kms_sequence@get-busy,Fail -kms_sequence@get-forked,Fail -kms_sequence@get-forked-busy,Fail -kms_sequence@get-idle,Fail -kms_sequence@queue-busy,Fail -kms_sequence@queue-idle,Fail -kms_setmode@basic,Fail -kms_vblank@accuracy-idle,Fail -kms_vblank@crtc-id,Fail -kms_vblank@invalid,Fail -kms_vblank@query-busy,Fail -kms_vblank@query-forked,Fail -kms_vblank@query-forked-busy,Fail -kms_vblank@query-idle,Fail -kms_vblank@ts-continuation-dpms-rpm,Fail kms_vblank@ts-continuation-dpms-suspend,Fail -kms_vblank@ts-continuation-idle,Fail -kms_vblank@ts-continuation-modeset,Fail -kms_vblank@ts-continuation-modeset-rpm,Fail kms_vblank@ts-continuation-suspend,Fail -kms_vblank@wait-busy,Fail -kms_vblank@wait-forked,Fail -kms_vblank@wait-forked-busy,Fail -kms_vblank@wait-idle,Fail perf@i915-ref-count,Fail diff --git a/drivers/gpu/drm/ci/xfails/vkms-none-fails.txt b/drivers/gpu/drm/ci/xfails/vkms-none-fails.txt index 6ebcc7d89fbd..029bd4956e85 100644 --- a/drivers/gpu/drm/ci/xfails/vkms-none-fails.txt +++ b/drivers/gpu/drm/ci/xfails/vkms-none-fails.txt @@ -16,6 +16,8 @@ kms_flip@flip-vs-panning-vs-hang,Fail kms_flip@flip-vs-suspend,Fail kms_flip@flip-vs-suspend-interruptible,Fail kms_lease@lease-uevent,Fail +kms_plane@pixel-format-source-clamping,Timeout +kms_plane@pixel-format,Timeout kms_writeback@writeback-check-output,Fail kms_writeback@writeback-check-output-XRGB2101010,Fail kms_writeback@writeback-fb-id,Fail From dd3a39aa20b9f06abff31ab0a17eeb6b18d9b3ce Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 10 Feb 2026 12:41:33 +0530 Subject: [PATCH 221/712] drm/ci: move qualcomm baremetal jobs to lava Qualcomm apq8016 and apq8096 DUTS are moved to Collabora lava farm. So enable these jobs to use lava and update expectation files. Signed-off-by: Vignesh Raman Reviewed-by: Daniel Stone Reviewed-by: Dmitry Baryshkov Co-developed-by: Dmitry Baryshkov Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/ci/arm64.config | 1 + drivers/gpu/drm/ci/test.yml | 41 +++++++++++-------- .../gpu/drm/ci/xfails/msm-apq8016-fails.txt | 4 ++ .../gpu/drm/ci/xfails/msm-apq8096-fails.txt | 2 + 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/ci/arm64.config b/drivers/gpu/drm/ci/arm64.config index fddfbd4d2493..b850b88787ad 100644 --- a/drivers/gpu/drm/ci/arm64.config +++ b/drivers/gpu/drm/ci/arm64.config @@ -83,6 +83,7 @@ CONFIG_SC_DISPCC_7180=y CONFIG_SC_GPUCC_7180=y CONFIG_SM_GPUCC_8350=y CONFIG_QCOM_SPMI_ADC5=y +CONFIG_QCOM_SPMI_VADC=y CONFIG_DRM_PARADE_PS8640=y CONFIG_DRM_LONTIUM_LT9611UXC=y CONFIG_PHY_QCOM_USB_HS=y diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index c314926e3693..b7409f8a13a5 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -122,32 +122,39 @@ msm:sc7180-trogdor-kingoftown: GPU_VERSION: ${DEVICE_TYPE} RUNNER_TAG: mesa-ci-x86-64-lava-sc7180-trogdor-kingoftown -.msm:apq8016: +msm:apq8016: + extends: + - .lava-igt:arm64 stage: msm + parallel: 3 variables: - DEVICE_TYPE: apq8016-sbc-usb-host + BOOT_METHOD: fastboot + DEVICE_TYPE: dragonboard-410c DRIVER_NAME: msm - BM_DTB: https://${PIPELINE_ARTIFACTS_BASE}/arm64/${DEVICE_TYPE}.dtb + DTB: apq8016-sbc-usb-host + FARM: collabora GPU_VERSION: apq8016 - # disabling unused clocks congests with the MDSS runtime PM trying to - # disable those clocks and causes boot to fail. - # Reproducer: DRM_MSM=y, DRM_I2C_ADV7511=m - BM_KERNEL_EXTRA_ARGS: clk_ignore_unused - RUNNER_TAG: google-freedreno-db410c - script: - - ./install/bare-metal/fastboot.sh || exit $? + KERNEL_IMAGE_NAME: "Image.gz" + KERNEL_IMAGE_TYPE: "" + RUNNER_TAG: mesa-ci-x86-64-lava-dragonboard-410c + LAVA_FIRMWARE: qcom-lava -.msm:apq8096: +msm:apq8096: + extends: + - .lava-igt:arm64 stage: msm + parallel: 3 variables: - DEVICE_TYPE: apq8096-db820c + BOOT_METHOD: fastboot + DEVICE_TYPE: dragonboard-820c DRIVER_NAME: msm - BM_KERNEL_EXTRA_ARGS: maxcpus=2 - BM_DTB: https://${PIPELINE_ARTIFACTS_BASE}/arm64/${DEVICE_TYPE}.dtb + DTB: apq8096-db820c + FARM: collabora GPU_VERSION: apq8096 - RUNNER_TAG: google-freedreno-db820c - script: - - ./install/bare-metal/fastboot.sh || exit $? + KERNEL_IMAGE_NAME: "Image.gz" + KERNEL_IMAGE_TYPE: "" + RUNNER_TAG: mesa-ci-x86-64-lava-dragonboard-820c + LAVA_FIRMWARE: qcom-lava msm:sm8350-hdk: extends: diff --git a/drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt b/drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt index 72c469021b66..4546363447ff 100644 --- a/drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt +++ b/drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt @@ -1,5 +1,9 @@ +core_setmaster@master-drop-set-user,Fail kms_3d,Fail +kms_cursor_legacy@forked-move,Fail +kms_cursor_legacy@single-bo,Fail kms_force_connector_basic@force-edid,Fail kms_hdmi_inject@inject-4k,Fail kms_lease@lease-uevent,Fail +msm/msm_mapping@memptrs,Fail msm/msm_mapping@ring,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt b/drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt index 2893f98a6b97..0d5cb2a87e67 100644 --- a/drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt +++ b/drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt @@ -1,2 +1,4 @@ +core_setmaster@master-drop-set-user,Fail kms_3d,Fail kms_lease@lease-uevent,Fail +msm/msm_mapping@memptrs,Fail From 6efced27f5df9d7a57e4847fe2898cdd19f72311 Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 10 Feb 2026 12:41:34 +0530 Subject: [PATCH 222/712] drm/ci: add rk3588-rock-5b Add job that executes the IGT test suite for rk3588-rock-5b. Signed-off-by: Vignesh Raman Reviewed-by: Daniel Stone Signed-off-by: Dmitry Baryshkov --- MAINTAINERS | 1 + drivers/gpu/drm/ci/arm64.config | 5 ++++ drivers/gpu/drm/ci/build.sh | 1 + drivers/gpu/drm/ci/gitlab-ci.yml | 1 + drivers/gpu/drm/ci/igt_runner.sh | 2 +- drivers/gpu/drm/ci/test.yml | 28 +++++++++++++++++++ .../drm/ci/xfails/panthor-rk3588-fails.txt | 5 ++++ .../drm/ci/xfails/panthor-rk3588-skips.txt | 20 +++++++++++++ .../drm/ci/xfails/rockchip-rk3588-fails.txt | 9 ++++++ .../drm/ci/xfails/rockchip-rk3588-skips.txt | 14 ++++++++++ 10 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/ci/xfails/panthor-rk3588-fails.txt create mode 100644 drivers/gpu/drm/ci/xfails/panthor-rk3588-skips.txt create mode 100644 drivers/gpu/drm/ci/xfails/rockchip-rk3588-fails.txt create mode 100644 drivers/gpu/drm/ci/xfails/rockchip-rk3588-skips.txt diff --git a/MAINTAINERS b/MAINTAINERS index ff92b9cc9435..b0eed114ed29 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2149,6 +2149,7 @@ L: dri-devel@lists.freedesktop.org S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml +F: drivers/gpu/drm/ci/xfails/panthor* F: drivers/gpu/drm/panthor/ F: include/uapi/drm/panthor_drm.h diff --git a/drivers/gpu/drm/ci/arm64.config b/drivers/gpu/drm/ci/arm64.config index b850b88787ad..5da3913ec8a4 100644 --- a/drivers/gpu/drm/ci/arm64.config +++ b/drivers/gpu/drm/ci/arm64.config @@ -209,3 +209,8 @@ CONFIG_ARM_TEGRA_DEVFREQ=y CONFIG_TEGRA_SOCTHERM=y CONFIG_DRM_TEGRA_DEBUG=y CONFIG_PWM_TEGRA=y + +# For Rockchip rk3588 +CONFIG_DRM_PANTHOR=m +CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX=y diff --git a/drivers/gpu/drm/ci/build.sh b/drivers/gpu/drm/ci/build.sh index 4353ee0f8889..d00d549cbd9c 100644 --- a/drivers/gpu/drm/ci/build.sh +++ b/drivers/gpu/drm/ci/build.sh @@ -16,6 +16,7 @@ if [[ "$KERNEL_ARCH" = "arm64" ]]; then GCC_ARCH="aarch64-linux-gnu" DEBIAN_ARCH="arm64" DEVICE_TREES="arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dtb" DEVICE_TREES+=" arch/arm64/boot/dts/amlogic/meson-gxl-s805x-libretech-ac.dtb" DEVICE_TREES+=" arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dtb" DEVICE_TREES+=" arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dtb" diff --git a/drivers/gpu/drm/ci/gitlab-ci.yml b/drivers/gpu/drm/ci/gitlab-ci.yml index 20e8cbdc39e9..56088c5393cd 100644 --- a/drivers/gpu/drm/ci/gitlab-ci.yml +++ b/drivers/gpu/drm/ci/gitlab-ci.yml @@ -136,6 +136,7 @@ stages: - meson - msm - panfrost + - panthor - powervr - rockchip - software-driver diff --git a/drivers/gpu/drm/ci/igt_runner.sh b/drivers/gpu/drm/ci/igt_runner.sh index 1c01bda52237..741d30655ab5 100755 --- a/drivers/gpu/drm/ci/igt_runner.sh +++ b/drivers/gpu/drm/ci/igt_runner.sh @@ -23,7 +23,7 @@ set -e mkdir -p /lib/modules case "$DRIVER_NAME" in - amdgpu|vkms) + amdgpu|vkms|panthor) # Cannot use HWCI_KERNEL_MODULES as at that point we don't have the module in /lib mv /install/modules/lib/modules/* /lib/modules/. || true modprobe --first-time $DRIVER_NAME diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index b7409f8a13a5..f6bee5b67931 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -206,6 +206,19 @@ msm:sm8350-hdk: KERNEL_IMAGE_TYPE: "" RUNNER_TAG: mesa-ci-x86-64-lava-rk3399-gru-kevin +.rk3588: + extends: + - .lava-igt:arm64 + - .rockchip-device + parallel: 2 + variables: + DEVICE_TYPE: rk3588-rock-5b + GPU_VERSION: rk3588 + BOOT_METHOD: u-boot + KERNEL_IMAGE_NAME: Image + KERNEL_IMAGE_TYPE: "image" + RUNNER_TAG: mesa-ci-x86-64-lava-rk3588-rock-5b + rockchip:rk3288: extends: - .rk3288 @@ -226,6 +239,16 @@ panfrost:rk3399: - .rk3399 - .panfrost-gpu +rockchip:rk3588: + extends: + - .rk3588 + - .rockchip-display + +panthor:rk3588: + extends: + - .rk3588 + - .panthor-gpu + .i915: extends: - .lava-igt:x86_64 @@ -354,6 +377,11 @@ amdgpu:stoney: variables: DRIVER_NAME: panfrost +.panthor-gpu: + stage: panthor + variables: + DRIVER_NAME: panthor + .mt8173: extends: - .mediatek-device diff --git a/drivers/gpu/drm/ci/xfails/panthor-rk3588-fails.txt b/drivers/gpu/drm/ci/xfails/panthor-rk3588-fails.txt new file mode 100644 index 000000000000..7407bd0128d4 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/panthor-rk3588-fails.txt @@ -0,0 +1,5 @@ +core_hotunplug@hotreplug,Fail +core_hotunplug@hotreplug-lateclose,Fail +core_hotunplug@hotunplug-rescan,Fail +core_hotunplug@unplug-rescan,Fail +core_setmaster@master-drop-set-user,Fail diff --git a/drivers/gpu/drm/ci/xfails/panthor-rk3588-skips.txt b/drivers/gpu/drm/ci/xfails/panthor-rk3588-skips.txt new file mode 100644 index 000000000000..b724cf04e3b3 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/panthor-rk3588-skips.txt @@ -0,0 +1,20 @@ +# Skip driver specific tests +^amdgpu.* +^msm.* +nouveau_.* +^v3d.* +^vc4.* +^vmwgfx* + +# Skip intel specific tests +gem_.* +i915_.* +tools_test.* +kms_dp_link_training.* + +# Panfrost is not a KMS driver, so skip the KMS tests +kms_.* + +# Skip display functionality tests for GPU-only drivers +dumb_buffer.* +fbdev.* diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3588-fails.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3588-fails.txt new file mode 100644 index 000000000000..799c3b04c3f2 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3588-fails.txt @@ -0,0 +1,9 @@ +core_setmaster@master-drop-set-user,Fail +kms_3d,Fail +kms_cursor_legacy@forked-bo,Fail +kms_cursor_legacy@forked-move,Fail +kms_cursor_legacy@single-bo,Fail +kms_cursor_legacy@single-move,Fail +kms_cursor_legacy@torture-bo,Fail +kms_cursor_legacy@torture-move,Fail +kms_lease@lease-uevent,Fail diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3588-skips.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3588-skips.txt new file mode 100644 index 000000000000..a165fccd8a93 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3588-skips.txt @@ -0,0 +1,14 @@ +# Skip driver specific tests +^amdgpu.* +^msm.* +nouveau_.* +^panfrost.* +^v3d.* +^vc4.* +^vmwgfx* + +# Skip intel specific tests +gem_.* +i915_.* +tools_test.* +kms_dp_link_training.* From 69bfce0f25c8f949f7a02c3ed93e36aadad3fa57 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Mar 2026 16:26:54 -0500 Subject: [PATCH 223/712] rust: uaccess: add write_dma() for copying from DMA buffers to userspace Add UserSliceWriter::write_dma() to copy data from a Coherent<[u8]> to userspace. This provides a safe interface for copying DMA buffer contents to userspace without requiring callers to work with raw pointers. Because write_dma() and write_slice() have common code, factor that code out into a helper function, write_raw(). The method handles bounds checking and offset calculation internally, wrapping the unsafe copy_to_user() call. Signed-off-by: Timur Tabi Reviewed-by: Alexandre Courbot Acked-by: Miguel Ojeda Tested-by: John Hubbard Tested-by: Eliot Courtney Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260319212658.2541610-3-ttabi@nvidia.com [ Rebase onto Coherent changes; remove unnecessary turbofish from cast(). - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/uaccess.rs | 85 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index f989539a31b4..e26ef90ba8ad 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -7,6 +7,7 @@ use crate::{ alloc::{Allocator, Flags}, bindings, + dma::Coherent, error::Result, ffi::{c_char, c_void}, fs::file, @@ -459,20 +460,19 @@ pub fn is_empty(&self) -> bool { self.length == 0 } - /// Writes raw data to this user pointer from a kernel buffer. + /// Low-level write from a raw pointer. /// - /// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of - /// bounds of this [`UserSliceWriter`]. This call may modify the associated userspace slice even - /// if it returns an error. - pub fn write_slice(&mut self, data: &[u8]) -> Result { - let len = data.len(); - let data_ptr = data.as_ptr().cast::(); + /// # Safety + /// + /// The caller must ensure that `from` is valid for reads of `len` bytes. + unsafe fn write_raw(&mut self, from: *const u8, len: usize) -> Result { if len > self.length { return Err(EFAULT); } - // SAFETY: `data_ptr` points into an immutable slice of length `len`, so we may read - // that many bytes from it. - let res = unsafe { bindings::copy_to_user(self.ptr.as_mut_ptr(), data_ptr, len) }; + + // SAFETY: Caller guarantees `from` is valid for `len` bytes (see this function's + // safety contract). + let res = unsafe { bindings::copy_to_user(self.ptr.as_mut_ptr(), from.cast(), len) }; if res != 0 { return Err(EFAULT); } @@ -481,6 +481,71 @@ pub fn write_slice(&mut self, data: &[u8]) -> Result { Ok(()) } + /// Writes raw data to this user pointer from a kernel buffer. + /// + /// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of + /// bounds of this [`UserSliceWriter`]. This call may modify the associated userspace slice even + /// if it returns an error. + pub fn write_slice(&mut self, data: &[u8]) -> Result { + // SAFETY: `data` is a valid slice, so `data.as_ptr()` is valid for + // reading `data.len()` bytes. + unsafe { self.write_raw(data.as_ptr(), data.len()) } + } + + /// Writes raw data to this user pointer from a DMA coherent allocation. + /// + /// Copies `count` bytes from `alloc` starting from `offset` into this userspace slice. + /// + /// # Errors + /// + /// - [`EOVERFLOW`]: `offset + count` overflows. + /// - [`ERANGE`]: `offset + count` exceeds the size of `alloc`, or `count` exceeds the + /// size of the user-space buffer. + /// - [`EFAULT`]: the write hits a bad address or goes out of bounds of this + /// [`UserSliceWriter`]. + /// + /// This call may modify the associated userspace slice even if it returns an error. + /// + /// Note: The memory may be concurrently modified by hardware (e.g., DMA). In such cases, + /// the copied data may be inconsistent, but this does not cause undefined behavior. + /// + /// # Example + /// + /// Copy the first 256 bytes of a DMA coherent allocation into a userspace buffer: + /// + /// ```no_run + /// use kernel::uaccess::UserSliceWriter; + /// use kernel::dma::Coherent; + /// + /// fn copy_dma_to_user( + /// mut writer: UserSliceWriter, + /// alloc: &Coherent<[u8]>, + /// ) -> Result { + /// writer.write_dma(alloc, 0, 256) + /// } + /// ``` + pub fn write_dma(&mut self, alloc: &Coherent<[u8]>, offset: usize, count: usize) -> Result { + let len = alloc.size(); + if offset.checked_add(count).ok_or(EOVERFLOW)? > len { + return Err(ERANGE); + } + + if count > self.len() { + return Err(ERANGE); + } + + // SAFETY: `as_ptr()` returns a valid pointer to a memory region of `count()` bytes, as + // guaranteed by the `Coherent` invariants. The check above ensures `offset + count <= len`. + let src_ptr = unsafe { alloc.as_ptr().cast::().add(offset) }; + + // Note: Use `write_raw` instead of `write_slice` because the allocation is coherent + // memory that hardware may modify (e.g., DMA); we cannot form a `&[u8]` slice over + // such volatile memory. + // + // SAFETY: `src_ptr` points into the allocation and is valid for `count` bytes (see above). + unsafe { self.write_raw(src_ptr, count) } + } + /// Writes raw data to this user pointer from a kernel buffer partially. /// /// This is the same as [`Self::write_slice`] but considers the given `offset` into `data` and From 01681851393642e10b5cc3f35eb6a1916ee4aff1 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Mar 2026 16:26:55 -0500 Subject: [PATCH 224/712] rust: dma: implement BinaryWriter for Coherent<[u8]> Implement the BinaryWriter trait for Coherent<[u8]>, enabling DMA coherent allocations to be exposed as readable binary files. The implementation handles offset tracking and bounds checking, copying data from the coherent allocation to userspace via write_dma(). Signed-off-by: Timur Tabi Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Tested-by: John Hubbard Tested-by: Eliot Courtney Link: https://patch.msgid.link/20260319212658.2541610-4-ttabi@nvidia.com [ Rebase onto Coherent changes. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index bf823818a67d..3eef7c2396bb 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -6,12 +6,14 @@ use crate::{ bindings, + debugfs, device::{ self, Bound, Core, // }, error::to_result, + fs::file, prelude::*, ptr::KnownSize, sync::aref::ARef, @@ -19,6 +21,7 @@ AsBytes, FromBytes, // }, // + uaccess::UserSliceWriter, }; use core::{ ops::{ @@ -876,6 +879,37 @@ fn drop(&mut self) { // can be sent to another thread. unsafe impl Send for Coherent {} +// SAFETY: Sharing `&Coherent` across threads is safe if `T` is `Sync`, because all +// methods that access the buffer contents (`field_read`, `field_write`, `as_slice`, +// `as_slice_mut`) are `unsafe`, and callers are responsible for ensuring no data races occur. +// The safe methods only return metadata or raw pointers whose use requires `unsafe`. +unsafe impl Sync for Coherent {} + +impl debugfs::BinaryWriter for Coherent<[u8]> { + fn write_to_slice( + &self, + writer: &mut UserSliceWriter, + offset: &mut file::Offset, + ) -> Result { + if offset.is_negative() { + return Err(EINVAL); + } + + // If the offset is too large for a usize (e.g. on 32-bit platforms), + // then consider that as past EOF and just return 0 bytes. + let Ok(offset_val) = usize::try_from(*offset) else { + return Ok(0); + }; + + let count = self.size().saturating_sub(offset_val).min(writer.len()); + + writer.write_dma(self, offset_val, count)?; + + *offset += count as i64; + Ok(count) + } +} + /// Reads a field of an item from an allocated region of structs. /// /// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating From ea0c83806f790de0b3441ddebbbcfd82196d6cce Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Mar 2026 16:26:56 -0500 Subject: [PATCH 225/712] gpu: nova-core: Replace module_pci_driver! with explicit module init Replace the module_pci_driver! macro with an explicit module initialization using the standard module! macro and InPlaceModule trait implementation. No functional change intended, with the exception that the driver now prints a message when loaded. This change is necessary so that we can create a top-level "nova_core" debugfs entry when the driver is loaded. Signed-off-by: Timur Tabi Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Tested-by: John Hubbard Tested-by: Eliot Courtney Link: https://patch.msgid.link/20260319212658.2541610-5-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/nova_core.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index b5caf1044697..0114a59825aa 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -2,6 +2,13 @@ //! Nova Core GPU Driver +use kernel::{ + driver::Registration, + pci, + prelude::*, + InPlaceModule, // +}; + #[macro_use] mod bitfield; @@ -20,8 +27,22 @@ pub(crate) const MODULE_NAME: &core::ffi::CStr = ::NAME; -kernel::module_pci_driver! { - type: driver::NovaCore, +#[pin_data] +struct NovaCoreModule { + #[pin] + _driver: Registration>, +} + +impl InPlaceModule for NovaCoreModule { + fn init(module: &'static kernel::ThisModule) -> impl PinInit { + try_pin_init!(Self { + _driver <- Registration::new(MODULE_NAME, module), + }) + } +} + +module! { + type: NovaCoreModule, name: "NovaCore", authors: ["Danilo Krummrich"], description: "Nova Core GPU driver", From 09691f5d807065a1d3d3042e2d8c2e0c170d7711 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Mar 2026 16:26:57 -0500 Subject: [PATCH 226/712] gpu: nova-core: create debugfs root in module init Create the 'nova_core' root debugfs entry when the driver loads. Normally, non-const global variables need to be protected by a mutex. Instead, we use unsafe code, as we know the entry is never modified after the driver is loaded. This solves the lifetime issue of the mutex guard, which would otherwise have required the use of `pin_init_scope`. Signed-off-by: Timur Tabi Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Tested-by: John Hubbard Tested-by: Eliot Courtney Link: https://patch.msgid.link/20260319212658.2541610-6-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/nova_core.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 0114a59825aa..ccd14b757b49 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -3,6 +3,7 @@ //! Nova Core GPU Driver use kernel::{ + debugfs, driver::Registration, pci, prelude::*, @@ -27,16 +28,40 @@ pub(crate) const MODULE_NAME: &core::ffi::CStr = ::NAME; +// TODO: Move this into per-module data once that exists. +static mut DEBUGFS_ROOT: Option = None; + +/// Guard that clears `DEBUGFS_ROOT` when dropped. +struct DebugfsRootGuard; + +impl Drop for DebugfsRootGuard { + fn drop(&mut self) { + // SAFETY: This guard is dropped after `_driver` (due to field order), + // so the driver is unregistered and no probe() can be running. + unsafe { DEBUGFS_ROOT = None }; + } +} + #[pin_data] struct NovaCoreModule { + // Fields are dropped in declaration order, so `_driver` is dropped first, + // then `_debugfs_guard` clears `DEBUGFS_ROOT`. #[pin] _driver: Registration>, + _debugfs_guard: DebugfsRootGuard, } impl InPlaceModule for NovaCoreModule { fn init(module: &'static kernel::ThisModule) -> impl PinInit { + let dir = debugfs::Dir::new(kernel::c_str!("nova_core")); + + // SAFETY: We are the only driver code running during init, so there + // cannot be any concurrent access to `DEBUGFS_ROOT`. + unsafe { DEBUGFS_ROOT = Some(dir) }; + try_pin_init!(Self { _driver <- Registration::new(MODULE_NAME, module), + _debugfs_guard: DebugfsRootGuard, }) } } From dff8302ca1d0e773c90dbeeb05e759f995c95482 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 19 Mar 2026 16:26:58 -0500 Subject: [PATCH 227/712] gpu: nova-core: create GSP-RM logging buffers debugfs entries Create read-only debugfs entries for LOGINIT, LOGRM, and LOGINTR, which are the three primary printf logging buffers from GSP-RM. LOGPMU will be added at a later date, as it requires support for its RPC message first. This patch uses the `pin_init_scope` feature to create the entries. `pin_init_scope` solves the lifetime issue over the `DEBUGFS_ROOT` reference by delaying its acquisition until the time the entry is actually initialized. Co-developed-by: Alexandre Courbot Signed-off-by: Alexandre Courbot Signed-off-by: Timur Tabi Tested-by: John Hubbard Tested-by: Eliot Courtney Link: https://patch.msgid.link/20260319212658.2541610-7-ttabi@nvidia.com [ Rebase onto Coherent changes. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gsp.rs | 47 ++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index a045c4189989..04e3976127cc 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -3,6 +3,7 @@ mod boot; use kernel::{ + debugfs, device, dma::{ Coherent, @@ -106,17 +107,23 @@ fn new(dev: &device::Device) -> Result { } } -/// GSP runtime data. -#[pin_data] -pub(crate) struct Gsp { - /// Libos arguments. - pub(crate) libos: Coherent<[LibosMemoryRegionInitArgument]>, +struct LogBuffers { /// Init log buffer. loginit: LogBuffer, /// Interrupts log buffer. logintr: LogBuffer, /// RM log buffer. logrm: LogBuffer, +} + +/// GSP runtime data. +#[pin_data] +pub(crate) struct Gsp { + /// Libos arguments. + pub(crate) libos: Coherent<[LibosMemoryRegionInitArgument]>, + /// Log buffers, optionally exposed via debugfs. + #[pin] + logs: debugfs::Scope, /// Command queue. #[pin] pub(crate) cmdq: Cmdq, @@ -130,13 +137,14 @@ pub(crate) fn new(pdev: &pci::Device) -> impl PinInit) -> impl PinInit Date: Mon, 12 Jan 2026 05:23:30 +0200 Subject: [PATCH 228/712] drm/msm/dpu: simplify bg_alpha selection In order to be more obvious in fg_alpha / bg_alpha handling during the blending programming drop the default setting for background alpha value and set it explicitly in all cases. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/697896/ Link: https://lore.kernel.org/r/20260112-dpu-rework-alpha-v2-1-d168785911d5@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 6bf7c46379ae..ff4a8312dec6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -335,13 +335,13 @@ static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer, max_alpha = 0x3ff; fg_alpha = pstate->base.alpha >> 6; } - bg_alpha = max_alpha - fg_alpha; /* default to opaque blending */ if (pstate->base.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE || !format->alpha_enable) { blend_op = DPU_BLEND_FG_ALPHA_FG_CONST | DPU_BLEND_BG_ALPHA_BG_CONST; + bg_alpha = max_alpha - fg_alpha; } else if (pstate->base.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) { blend_op = DPU_BLEND_FG_ALPHA_FG_CONST | DPU_BLEND_BG_ALPHA_FG_PIXEL; @@ -350,6 +350,7 @@ static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer, blend_op |= DPU_BLEND_BG_MOD_ALPHA | DPU_BLEND_BG_INV_MOD_ALPHA; } else { + bg_alpha = 0; blend_op |= DPU_BLEND_BG_INV_ALPHA; } } else { @@ -363,6 +364,7 @@ static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer, DPU_BLEND_BG_MOD_ALPHA | DPU_BLEND_BG_INV_MOD_ALPHA; } else { + bg_alpha = 0; blend_op |= DPU_BLEND_BG_INV_ALPHA; } } From 7fe04c7c4360d2e7fb85fbe88cbd9b35a4d730ea Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 12 Jan 2026 05:23:31 +0200 Subject: [PATCH 229/712] drm/msm/dpu: use full scale alpha in _dpu_crtc_setup_blend_cfg() Both _dpu_crtc_setup_blend_cfg() and setup_blend_config_alpha() callbacks embed knowledge about platform's alpha range (8-bit or 10-bit). Make _dpu_crtc_setup_blend_cfg() use full 16-bit values for alpha and reduce alpha only in DPU-specific callbacks. Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/697898/ Link: https://lore.kernel.org/r/20260112-dpu-rework-alpha-v2-2-d168785911d5@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 16 +++++----------- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c | 21 +++++++++++++-------- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.h | 2 +- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index ff4a8312dec6..97aca969337f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -326,26 +326,20 @@ static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer, { struct dpu_hw_mixer *lm = mixer->hw_lm; u32 blend_op; - u32 fg_alpha, bg_alpha, max_alpha; + u32 fg_alpha, bg_alpha; - if (mdss_ver->core_major_ver < 12) { - max_alpha = 0xff; - fg_alpha = pstate->base.alpha >> 8; - } else { - max_alpha = 0x3ff; - fg_alpha = pstate->base.alpha >> 6; - } + fg_alpha = pstate->base.alpha; /* default to opaque blending */ if (pstate->base.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE || !format->alpha_enable) { blend_op = DPU_BLEND_FG_ALPHA_FG_CONST | DPU_BLEND_BG_ALPHA_BG_CONST; - bg_alpha = max_alpha - fg_alpha; + bg_alpha = DRM_BLEND_ALPHA_OPAQUE - fg_alpha; } else if (pstate->base.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) { blend_op = DPU_BLEND_FG_ALPHA_FG_CONST | DPU_BLEND_BG_ALPHA_FG_PIXEL; - if (fg_alpha != max_alpha) { + if (fg_alpha != DRM_BLEND_ALPHA_OPAQUE) { bg_alpha = fg_alpha; blend_op |= DPU_BLEND_BG_MOD_ALPHA | DPU_BLEND_BG_INV_MOD_ALPHA; @@ -357,7 +351,7 @@ static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer, /* coverage blending */ blend_op = DPU_BLEND_FG_ALPHA_FG_PIXEL | DPU_BLEND_BG_ALPHA_FG_PIXEL; - if (fg_alpha != max_alpha) { + if (fg_alpha != DRM_BLEND_ALPHA_OPAQUE) { bg_alpha = fg_alpha; blend_op |= DPU_BLEND_FG_MOD_ALPHA | DPU_BLEND_FG_INV_MOD_ALPHA | diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c index e8a76d5192c2..b7779726bf10 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c @@ -126,7 +126,9 @@ static int dpu_hw_lm_collect_misr(struct dpu_hw_mixer *ctx, u32 *misr_value) } static void dpu_hw_lm_setup_blend_config_combined_alpha(struct dpu_hw_mixer *ctx, - u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op) + u32 stage, + u16 fg_alpha, u16 bg_alpha, + u32 blend_op) { struct dpu_hw_blk_reg_map *c = &ctx->hw; int stage_off; @@ -139,15 +141,16 @@ static void dpu_hw_lm_setup_blend_config_combined_alpha(struct dpu_hw_mixer *ctx if (WARN_ON(stage_off < 0)) return; - const_alpha = (bg_alpha & 0xFF) | ((fg_alpha & 0xFF) << 16); + const_alpha = (bg_alpha >> 8) | ((fg_alpha >> 8) << 16); DPU_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, const_alpha); DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op); } static void dpu_hw_lm_setup_blend_config_combined_alpha_v12(struct dpu_hw_mixer *ctx, - u32 stage, u32 fg_alpha, - u32 bg_alpha, u32 blend_op) + u32 stage, + u16 fg_alpha, u16 bg_alpha, + u32 blend_op) { struct dpu_hw_blk_reg_map *c = &ctx->hw; int stage_off; @@ -160,13 +163,15 @@ dpu_hw_lm_setup_blend_config_combined_alpha_v12(struct dpu_hw_mixer *ctx, if (WARN_ON(stage_off < 0)) return; - const_alpha = (bg_alpha & 0x3ff) | ((fg_alpha & 0x3ff) << 16); + const_alpha = (bg_alpha >> 6) | ((fg_alpha >> 6) << 16); DPU_REG_WRITE(c, LM_BLEND0_CONST_ALPHA_V12 + stage_off, const_alpha); DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op); } static void dpu_hw_lm_setup_blend_config(struct dpu_hw_mixer *ctx, - u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op) + u32 stage, + u16 fg_alpha, u16 bg_alpha, + u32 blend_op) { struct dpu_hw_blk_reg_map *c = &ctx->hw; int stage_off; @@ -178,8 +183,8 @@ static void dpu_hw_lm_setup_blend_config(struct dpu_hw_mixer *ctx, if (WARN_ON(stage_off < 0)) return; - DPU_REG_WRITE(c, LM_BLEND0_FG_ALPHA + stage_off, fg_alpha); - DPU_REG_WRITE(c, LM_BLEND0_BG_ALPHA + stage_off, bg_alpha); + DPU_REG_WRITE(c, LM_BLEND0_FG_ALPHA + stage_off, fg_alpha >> 8); + DPU_REG_WRITE(c, LM_BLEND0_BG_ALPHA + stage_off, bg_alpha >> 8); DPU_REG_WRITE(c, LM_BLEND0_OP + stage_off, blend_op); } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.h index ecbb77711d83..380ca673f6de 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.h @@ -41,7 +41,7 @@ struct dpu_hw_lm_ops { * for the specified stage */ void (*setup_blend_config)(struct dpu_hw_mixer *ctx, uint32_t stage, - uint32_t fg_alpha, uint32_t bg_alpha, uint32_t blend_op); + u16 fg_alpha, u16 bg_alpha, uint32_t blend_op); /** * @setup_alpha_out: Alpha color component selection from either fg or bg From 18417122d6a461a78417784e8f21fab517b39b94 Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Thu, 26 Feb 2026 20:29:58 +0800 Subject: [PATCH 230/712] drm/msm/dsi/phy: rename DSI_PHY_7NM_QUIRK_PRE_V4_1 to DSI_PHY_7NM_QUIRK_V4_0 The quirk flag DSI_PHY_7NM_QUIRK_PRE_V4_1 is renamed to DSI_PHY_7NM_QUIRK_V4_0 to better reflect the actual hardware revision it applies to. (Only SM8150 uses it, its hardware revision is 4.0) No functional change. Suggested-by: Dmitry Baryshkov Signed-off-by: Pengyu Luo Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/707416/ Link: https://lore.kernel.org/r/20260226122958.22555-3-mitltlatltl@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c index 01182442dfd6..8f4b03713f25 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -41,8 +41,8 @@ #define VCO_REF_CLK_RATE 19200000 #define FRAC_BITS 18 -/* Hardware is pre V4.1 */ -#define DSI_PHY_7NM_QUIRK_PRE_V4_1 BIT(0) +/* Hardware is V4.0 */ +#define DSI_PHY_7NM_QUIRK_V4_0 BIT(0) /* Hardware is V4.1 */ #define DSI_PHY_7NM_QUIRK_V4_1 BIT(1) /* Hardware is V4.2 */ @@ -141,7 +141,7 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll, struct dsi_pll_config dec_multiple = div_u64(pll_freq * multiplier, divider); dec = div_u64_rem(dec_multiple, multiplier, &frac); - if (pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1) { + if (pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_0) { config->pll_clock_inverters = 0x28; } else if ((pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2)) { if (pll_freq < 163000000ULL) @@ -264,7 +264,7 @@ static void dsi_pll_config_hzindep_reg(struct dsi_pll_7nm *pll) void __iomem *base = pll->phy->pll_base; u8 analog_controls_five_1 = 0x01, vco_config_1 = 0x00; - if (!(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1)) + if (!(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_0)) if (pll->vco_current_rate >= 3100000000ULL) analog_controls_five_1 = 0x03; @@ -313,10 +313,10 @@ static void dsi_pll_config_hzindep_reg(struct dsi_pll_7nm *pll) writel(0x29, base + REG_DSI_7nm_PHY_PLL_PFILT); writel(0x2f, base + REG_DSI_7nm_PHY_PLL_PFILT); writel(0x2a, base + REG_DSI_7nm_PHY_PLL_IFILT); - writel(!(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1) ? 0x3f : 0x22, + writel(!(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_0) ? 0x3f : 0x22, base + REG_DSI_7nm_PHY_PLL_IFILT); - if (!(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1)) { + if (!(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_0)) { writel(0x22, base + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE); if (pll->slave) writel(0x22, pll->slave->phy->pll_base + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE); @@ -928,7 +928,7 @@ static void dsi_phy_hw_v4_0_lane_settings(struct msm_dsi_phy *phy) const u8 *tx_dctrl = tx_dctrl_0; void __iomem *lane_base = phy->lane_base; - if (!(phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1)) + if (!(phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_0)) tx_dctrl = tx_dctrl_1; /* Strength ctrl settings */ @@ -1319,7 +1319,7 @@ const struct msm_dsi_phy_cfg dsi_phy_7nm_8150_cfgs = { .max_pll_rate = 3500000000UL, .io_start = { 0xae94400, 0xae96400 }, .num_dsi_phy = 2, - .quirks = DSI_PHY_7NM_QUIRK_PRE_V4_1, + .quirks = DSI_PHY_7NM_QUIRK_V4_0, }; const struct msm_dsi_phy_cfg dsi_phy_7nm_7280_cfgs = { From c80c68e777587330e5d89b4c294a32fe7365879c Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:25 +0200 Subject: [PATCH 231/712] drm/i915/display: move clock-gating init for IBX to display Add a new function in the display code to help initialize clock-gating without reading display PCH registers directly from non-display code. This adds a mini-framework to deal with display-specific PCH registers and uses it for IBX as a start. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-2-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 24 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_pch.h | 1 + drivers/gpu/drm/i915/intel_clock_gating.c | 13 ++---------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index 65359a36df48..65812b720bda 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -5,6 +5,8 @@ #include +#include "intel_de.h" +#include "intel_display_regs.h" #include "intel_display_core.h" #include "intel_display_utils.h" #include "intel_pch.h" @@ -214,6 +216,28 @@ intel_pch_type(const struct intel_display *display, unsigned short id) } } +static void intel_pch_ibx_init_clock_gating(struct intel_display *display) +{ + /* + * On Ibex Peak and Cougar Point, we need to disable clock + * gating for the panel power sequencer or it will fail to + * start up when no ports are active. + */ + intel_de_write(display, SOUTH_DSPCLK_GATE_D, + PCH_DPLSUNIT_CLOCK_GATE_DISABLE); +} + +void intel_pch_init_clock_gating(struct intel_display *display) +{ + switch (INTEL_PCH_TYPE(display)) { + case PCH_IBX: + intel_pch_ibx_init_clock_gating(display); + break; + default: + break; + } +} + static bool intel_is_virt_pch(unsigned short id, unsigned short svendor, unsigned short sdevice) { diff --git a/drivers/gpu/drm/i915/display/intel_pch.h b/drivers/gpu/drm/i915/display/intel_pch.h index 19cac7412d0a..aa971fa141e7 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.h +++ b/drivers/gpu/drm/i915/display/intel_pch.h @@ -52,5 +52,6 @@ enum intel_pch { #define HAS_PCH_SPLIT(display) (INTEL_PCH_TYPE(display) != PCH_NONE) void intel_pch_detect(struct intel_display *display); +void intel_pch_init_clock_gating(struct intel_display *display); #endif /* __INTEL_PCH__ */ diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 68a6f94f2a37..c0382607224d 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -33,6 +33,7 @@ #include "display/intel_display.h" #include "display/intel_display_core.h" #include "display/intel_display_regs.h" +#include "display/intel_pch.h" #include "gt/intel_engine_regs.h" #include "gt/intel_gt.h" #include "gt/intel_gt_mcr.h" @@ -124,16 +125,6 @@ static void glk_init_clock_gating(struct drm_i915_private *i915) PWM1_GATING_DIS | PWM2_GATING_DIS); } -static void ibx_init_clock_gating(struct drm_i915_private *i915) -{ - /* - * On Ibex Peak and Cougar Point, we need to disable clock - * gating for the panel power sequencer or it will fail to - * start up when no ports are active. - */ - intel_uncore_write(&i915->uncore, SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); -} - static void g4x_disable_trickle_feed(struct drm_i915_private *dev_priv) { struct intel_display *display = dev_priv->display; @@ -202,7 +193,7 @@ static void ilk_init_clock_gating(struct drm_i915_private *i915) g4x_disable_trickle_feed(i915); - ibx_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); } static void cpt_init_clock_gating(struct drm_i915_private *i915) From d129bce3fc820d8f2a4b966a38533b2f0f689ba5 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:26 +0200 Subject: [PATCH 232/712] drm/i915: move CPT clock gating init into intel_pch Move the CPT PCH clock gating programming into intel_pch_init_clock_gating() and switch the corresponding IVB callers to the display-specific code. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-3-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 40 +++++++++++++++++++++++ drivers/gpu/drm/i915/intel_clock_gating.c | 39 ++-------------------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index 65812b720bda..bcd66bdf2a22 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -6,6 +6,7 @@ #include #include "intel_de.h" +#include "intel_display.h" #include "intel_display_regs.h" #include "intel_display_core.h" #include "intel_display_utils.h" @@ -227,12 +228,51 @@ static void intel_pch_ibx_init_clock_gating(struct intel_display *display) PCH_DPLSUNIT_CLOCK_GATE_DISABLE); } +static void intel_pch_cpt_init_clock_gating(struct intel_display *display) +{ + enum pipe pipe; + u32 val; + + /* + * On Ibex Peak and Cougar Point, we need to disable clock + * gating for the panel power sequencer or it will fail to + * start up when no ports are active. + */ + intel_de_write(display, SOUTH_DSPCLK_GATE_D, + PCH_DPLSUNIT_CLOCK_GATE_DISABLE | + PCH_DPLUNIT_CLOCK_GATE_DISABLE | + PCH_CPUNIT_CLOCK_GATE_DISABLE); + intel_de_rmw(display, SOUTH_CHICKEN2, 0, DPLS_EDP_PPS_FIX_DIS); + + /* The below fixes the weird display corruption, a few pixels shifted + * downward, on (only) LVDS of some HP laptops with IVY. + */ + for_each_pipe(display, pipe) { + val = intel_de_read(display, TRANS_CHICKEN2(pipe)); + val |= TRANS_CHICKEN2_TIMING_OVERRIDE; + val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; + if (display->vbt.fdi_rx_polarity_inverted) + val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; + val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; + val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_MODESWITCH; + intel_de_write(display, TRANS_CHICKEN2(pipe), val); + } + + /* WADP0ClockGatingDisable */ + for_each_pipe(display, pipe) + intel_de_write(display, TRANS_CHICKEN1(pipe), + TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); +} + void intel_pch_init_clock_gating(struct intel_display *display) { switch (INTEL_PCH_TYPE(display)) { case PCH_IBX: intel_pch_ibx_init_clock_gating(display); break; + case PCH_CPT: + intel_pch_cpt_init_clock_gating(display); + break; default: break; } diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index c0382607224d..0218196d183a 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -196,41 +196,6 @@ static void ilk_init_clock_gating(struct drm_i915_private *i915) intel_pch_init_clock_gating(i915->display); } -static void cpt_init_clock_gating(struct drm_i915_private *i915) -{ - struct intel_display *display = i915->display; - enum pipe pipe; - u32 val; - - /* - * On Ibex Peak and Cougar Point, we need to disable clock - * gating for the panel power sequencer or it will fail to - * start up when no ports are active. - */ - intel_uncore_write(&i915->uncore, SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE | - PCH_DPLUNIT_CLOCK_GATE_DISABLE | - PCH_CPUNIT_CLOCK_GATE_DISABLE); - intel_uncore_rmw(&i915->uncore, SOUTH_CHICKEN2, 0, DPLS_EDP_PPS_FIX_DIS); - /* The below fixes the weird display corruption, a few pixels shifted - * downward, on (only) LVDS of some HP laptops with IVY. - */ - for_each_pipe(display, pipe) { - val = intel_uncore_read(&i915->uncore, TRANS_CHICKEN2(pipe)); - val |= TRANS_CHICKEN2_TIMING_OVERRIDE; - val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; - if (display->vbt.fdi_rx_polarity_inverted) - val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; - val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; - val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_MODESWITCH; - intel_uncore_write(&i915->uncore, TRANS_CHICKEN2(pipe), val); - } - /* WADP0ClockGatingDisable */ - for_each_pipe(display, pipe) { - intel_uncore_write(&i915->uncore, TRANS_CHICKEN1(pipe), - TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); - } -} - static void gen6_check_mch_setup(struct drm_i915_private *i915) { u32 tmp; @@ -296,7 +261,7 @@ static void gen6_init_clock_gating(struct drm_i915_private *i915) g4x_disable_trickle_feed(i915); - cpt_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); gen6_check_mch_setup(i915); } @@ -536,7 +501,7 @@ static void ivb_init_clock_gating(struct drm_i915_private *i915) GEN6_MBC_SNPCR_MED); if (!HAS_PCH_NOP(display)) - cpt_init_clock_gating(i915); + intel_pch_init_clock_gating(display); gen6_check_mch_setup(i915); } From cf37495ad17db876c28a824c003133c2e103cd59 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:27 +0200 Subject: [PATCH 233/712] drm/i915: move LPT clock gating init into intel_pch Move the LPT PCH clock gating programming into intel_pch_init_clock_gating() and switch the corresponding Haswell/Broadwell callers to the display-specific code. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-4-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 19 +++++++++++++++++++ drivers/gpu/drm/i915/intel_clock_gating.c | 21 ++------------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index bcd66bdf2a22..b7fade66b1da 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -264,6 +264,21 @@ static void intel_pch_cpt_init_clock_gating(struct intel_display *display) TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); } +static void intel_pch_lpt_init_clock_gating(struct intel_display *display) +{ + /* + * TODO: this bit should only be enabled when really needed, then + * disabled when not needed anymore in order to save power. + */ + if (HAS_PCH_LPT_LP(display)) + intel_de_rmw(display, SOUTH_DSPCLK_GATE_D, 0, + PCH_LP_PARTITION_LEVEL_DISABLE); + + /* WADPOClockGatingDisable:hsw */ + intel_de_rmw(display, TRANS_CHICKEN1(PIPE_A), 0, + TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); +} + void intel_pch_init_clock_gating(struct intel_display *display) { switch (INTEL_PCH_TYPE(display)) { @@ -273,6 +288,10 @@ void intel_pch_init_clock_gating(struct intel_display *display) case PCH_CPT: intel_pch_cpt_init_clock_gating(display); break; + case PCH_LPT_H: + case PCH_LPT_LP: + intel_pch_lpt_init_clock_gating(display); + break; default: break; } diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 0218196d183a..4c19028a9e20 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -266,23 +266,6 @@ static void gen6_init_clock_gating(struct drm_i915_private *i915) gen6_check_mch_setup(i915); } -static void lpt_init_clock_gating(struct drm_i915_private *i915) -{ - struct intel_display *display = i915->display; - - /* - * TODO: this bit should only be enabled when really needed, then - * disabled when not needed anymore in order to save power. - */ - if (HAS_PCH_LPT_LP(display)) - intel_uncore_rmw(&i915->uncore, SOUTH_DSPCLK_GATE_D, - 0, PCH_LP_PARTITION_LEVEL_DISABLE); - - /* WADPOClockGatingDisable:hsw */ - intel_uncore_rmw(&i915->uncore, TRANS_CHICKEN1(PIPE_A), - 0, TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); -} - static void gen8_set_l3sqc_credits(struct drm_i915_private *i915, int general_prio_credits, int high_prio_credits) @@ -422,7 +405,7 @@ static void bdw_init_clock_gating(struct drm_i915_private *i915) intel_uncore_rmw(&i915->uncore, CHICKEN_PAR2_1, 0, KVM_CONFIG_CHANGE_NOTIFICATION_SELECT); - lpt_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); /* WaDisableDopClockGating:bdw * @@ -456,7 +439,7 @@ static void hsw_init_clock_gating(struct drm_i915_private *i915) /* WaSwitchSolVfFArbitrationPriority:hsw */ intel_uncore_rmw(&i915->uncore, GAM_ECOCHK, 0, HSW_ECOCHK_ARB_PRIO_SOL); - lpt_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); } static void ivb_init_clock_gating(struct drm_i915_private *i915) From f86b08bf1766e9883bc75a66aeca7d737bf4c093 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:28 +0200 Subject: [PATCH 234/712] drm/i915: move CNP clock gating init into intel_pch Move the CNP PCH clock gating programming into intel_pch_init_clock_gating() and switch the corresponding CFL/CML caller to the display-specific code. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-5-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 10 ++++++++++ drivers/gpu/drm/i915/intel_clock_gating.c | 13 +------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index b7fade66b1da..d2c1b1751838 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -279,6 +279,13 @@ static void intel_pch_lpt_init_clock_gating(struct intel_display *display) TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); } +static void intel_pch_cnp_init_clock_gating(struct intel_display *display) +{ + /* Display WA #1181 WaSouthDisplayDisablePWMCGEGating: cnp */ + intel_de_rmw(display, SOUTH_DSPCLK_GATE_D, 0, + CNP_PWM_CGE_GATING_DISABLE); +} + void intel_pch_init_clock_gating(struct intel_display *display) { switch (INTEL_PCH_TYPE(display)) { @@ -292,6 +299,9 @@ void intel_pch_init_clock_gating(struct intel_display *display) case PCH_LPT_LP: intel_pch_lpt_init_clock_gating(display); break; + case PCH_CNP: + intel_pch_cnp_init_clock_gating(display); + break; default: break; } diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 4c19028a9e20..ee2489a2fbe7 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -299,20 +299,9 @@ static void dg2_init_clock_gating(struct drm_i915_private *i915) SGSI_SIDECLK_DIS); } -static void cnp_init_clock_gating(struct drm_i915_private *i915) -{ - struct intel_display *display = i915->display; - - if (!HAS_PCH_CNP(display)) - return; - - /* Display WA #1181 WaSouthDisplayDisablePWMCGEGating: cnp */ - intel_uncore_rmw(&i915->uncore, SOUTH_DSPCLK_GATE_D, 0, CNP_PWM_CGE_GATING_DISABLE); -} - static void cfl_init_clock_gating(struct drm_i915_private *i915) { - cnp_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); gen9_init_clock_gating(i915); /* WAC6entrylatency:cfl */ From b0907ee59e24d3dad572b4ccc6db018b00ca14c8 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 26 Feb 2026 15:49:02 +0200 Subject: [PATCH 235/712] drm/msm/dpu: enable virtual planes by default Turn on the switch and use virtual planes by default, enhancing utilisation of the display pipelines. It is still possible to use legacy implementation by using `msm.dpu_use_virtual_planes=false` kernel boot parameter. Acked-by: Neil Armstrong Acked-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455, Patchwork: https://patchwork.freedesktop.org/patch/707437/ Link: https://lore.kernel.org/r/20260226-dpu-enable-virt-planes-v2-1-87971236fe86@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 0623f1dbed97..7c5b3495bddf 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -52,7 +52,7 @@ #define DPU_DEBUGFS_DIR "msm_dpu" #define DPU_DEBUGFS_HWMASKNAME "hw_log_mask" -bool dpu_use_virtual_planes; +bool dpu_use_virtual_planes = true; module_param(dpu_use_virtual_planes, bool, 0); static int dpu_kms_hw_init(struct msm_kms *kms); From b21e85400ce763f2c6ad913e03fea5cadc323c13 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 28 Feb 2026 19:20:37 +0200 Subject: [PATCH 236/712] drm/msm: add missing MODULE_DEVICE_ID definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm/msm module bundles several drivers, each of them having a separate OF match table, however only MDSS (subsystem), KMS devices and GPU have corresponding MODULE_DEVICE_ID tables. Add MODULE_DEVICE_ID to the display-related driver and to all other drivers in this module, simplifying userspace job. Fixes: 060530f1ea67 ("drm/msm: use componentised device support") Reported-by: Loïc Minier Patchwork: https://patchwork.freedesktop.org/patch/707960/ Link: https://lore.kernel.org/r/20260228-msm-device-id-v2-1-24b085919444@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 1 + drivers/gpu/drm/msm/dsi/dsi.c | 1 + drivers/gpu/drm/msm/dsi/phy/dsi_phy.c | 1 + drivers/gpu/drm/msm/hdmi/hdmi.c | 1 + drivers/gpu/drm/msm/hdmi/hdmi_phy.c | 1 + 5 files changed, 5 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index a082f4d3ebe2..0d8cb362fb0e 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -210,6 +210,7 @@ static const struct of_device_id msm_dp_dt_match[] = { { .compatible = "qcom,x1e80100-dp", .data = &msm_dp_desc_x1e80100 }, {} }; +MODULE_DEVICE_TABLE(of, msm_dp_dt_match); static struct msm_dp_display_private *dev_get_dp_display_private(struct device *dev) { diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index d8bb40ef820e..3c9f01ed6271 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -198,6 +198,7 @@ static const struct of_device_id dt_match[] = { { .compatible = "qcom,dsi-ctrl-6g-qcm2290" }, {} }; +MODULE_DEVICE_TABLE(of, dt_match); static const struct dev_pm_ops dsi_pm_ops = { SET_RUNTIME_PM_OPS(msm_dsi_runtime_suspend, msm_dsi_runtime_resume, NULL) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index 7937266de1d2..c59375aaae19 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -582,6 +582,7 @@ static const struct of_device_id dsi_phy_dt_match[] = { #endif {} }; +MODULE_DEVICE_TABLE(of, dsi_phy_dt_match); /* * Currently, we only support one SoC for each PHY type. When we have multiple diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 5afac09c0d33..d5ef5089c9e9 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -441,6 +441,7 @@ static const struct of_device_id msm_hdmi_dt_match[] = { { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8960_config }, {} }; +MODULE_DEVICE_TABLE(of, msm_hdmi_dt_match); static struct platform_driver msm_hdmi_driver = { .probe = msm_hdmi_dev_probe, diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c index 667573f1db7c..f726555bb681 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c @@ -204,6 +204,7 @@ static const struct of_device_id msm_hdmi_phy_dt_match[] = { .data = &msm_hdmi_phy_8998_cfg }, {} }; +MODULE_DEVICE_TABLE(of, msm_hdmi_phy_dt_match); static struct platform_driver msm_hdmi_phy_platform_driver = { .probe = msm_hdmi_phy_probe, From a6f081ec4ce65b7097ec346099bd27b0226d5101 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 2 Mar 2026 14:41:26 +0100 Subject: [PATCH 237/712] drm/msm/mdp5: Remove MSM8974v1 To the best of my knowledge, the v1 version of this SoC had been superseded before any device was released on the market. Everywhere else in the kernel, there are assumptions about the SoC being revision 2 or the later MSM8974PRO. Remove the config for that flavor of MDP. To bring the naming in line with the rest of the kernel, remove the v2 suffix from the remaining config. Suggested-by: Dmitry Baryshkov Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/708112/ Link: https://lore.kernel.org/r/20260302-topic-goodnight_8974v1-v1-1-e0006f7a0526@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c | 94 +----------------------- 1 file changed, 2 insertions(+), 92 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c index 69fef034d0df..7c91fc1915f3 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c @@ -14,95 +14,6 @@ struct mdp5_cfg_handler { /* mdp5_cfg must be exposed (used in mdp5.xml.h) */ const struct mdp5_cfg_hw *mdp5_cfg = NULL; -static const struct mdp5_cfg_hw msm8x74v1_config = { - .name = "msm8x74v1", - .mdp = { - .count = 1, - .caps = MDP_CAP_SMP | - 0, - }, - .smp = { - .mmb_count = 22, - .mmb_size = 4096, - .clients = { - [SSPP_VIG0] = 1, [SSPP_VIG1] = 4, [SSPP_VIG2] = 7, - [SSPP_DMA0] = 10, [SSPP_DMA1] = 13, - [SSPP_RGB0] = 16, [SSPP_RGB1] = 17, [SSPP_RGB2] = 18, - }, - }, - .ctl = { - .count = 5, - .base = { 0x00500, 0x00600, 0x00700, 0x00800, 0x00900 }, - .flush_hw_mask = 0x0003ffff, - }, - .pipe_vig = { - .count = 3, - .base = { 0x01100, 0x01500, 0x01900 }, - .caps = MDP_PIPE_CAP_HFLIP | - MDP_PIPE_CAP_VFLIP | - MDP_PIPE_CAP_SCALE | - MDP_PIPE_CAP_CSC | - 0, - }, - .pipe_rgb = { - .count = 3, - .base = { 0x01d00, 0x02100, 0x02500 }, - .caps = MDP_PIPE_CAP_HFLIP | - MDP_PIPE_CAP_VFLIP | - MDP_PIPE_CAP_SCALE | - 0, - }, - .pipe_dma = { - .count = 2, - .base = { 0x02900, 0x02d00 }, - .caps = MDP_PIPE_CAP_HFLIP | - MDP_PIPE_CAP_VFLIP | - 0, - }, - .lm = { - .count = 5, - .base = { 0x03100, 0x03500, 0x03900, 0x03d00, 0x04100 }, - .instances = { - { .id = 0, .pp = 0, .dspp = 0, - .caps = MDP_LM_CAP_DISPLAY, }, - { .id = 1, .pp = 1, .dspp = 1, - .caps = MDP_LM_CAP_DISPLAY, }, - { .id = 2, .pp = 2, .dspp = 2, - .caps = MDP_LM_CAP_DISPLAY, }, - { .id = 3, .pp = -1, .dspp = -1, - .caps = MDP_LM_CAP_WB }, - { .id = 4, .pp = -1, .dspp = -1, - .caps = MDP_LM_CAP_WB }, - }, - .nb_stages = 5, - .max_width = 2048, - .max_height = 0xFFFF, - }, - .dspp = { - .count = 3, - .base = { 0x04500, 0x04900, 0x04d00 }, - }, - .pp = { - .count = 3, - .base = { 0x21a00, 0x21b00, 0x21c00 }, - }, - .intf = { - .base = { 0x21000, 0x21200, 0x21400, 0x21600 }, - .connect = { - [0] = INTF_eDP, - [1] = INTF_DSI, - [2] = INTF_DSI, - [3] = INTF_HDMI, - }, - }, - .perf = { - .ab_inefficiency = 200, - .ib_inefficiency = 120, - .clk_inefficiency = 125 - }, - .max_clk = 200000000, -}; - static const struct mdp5_cfg_hw msm8x26_config = { .name = "msm8x26", .mdp = { @@ -184,7 +95,7 @@ static const struct mdp5_cfg_hw msm8x26_config = { .max_clk = 200000000, }; -static const struct mdp5_cfg_hw msm8x74v2_config = { +static const struct mdp5_cfg_hw msm8x74_config = { .name = "msm8x74", .mdp = { .count = 1, @@ -1098,9 +1009,8 @@ static const struct mdp5_cfg_hw msm8937_config = { }; static const struct mdp5_cfg_handler cfg_handlers_v1[] = { - { .revision = 0, .config = { .hw = &msm8x74v1_config } }, { .revision = 1, .config = { .hw = &msm8x26_config } }, - { .revision = 2, .config = { .hw = &msm8x74v2_config } }, + { .revision = 2, .config = { .hw = &msm8x74_config } }, { .revision = 3, .config = { .hw = &apq8084_config } }, { .revision = 6, .config = { .hw = &msm8x16_config } }, { .revision = 8, .config = { .hw = &msm8x36_config } }, From 069a1db1904fec20d74fc1387e47c4b9fd60426f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 4 Mar 2026 13:58:43 +0100 Subject: [PATCH 238/712] dt-bindings: display/msm: dp-controller: Add Eliza SoC Add DisplayPort controller for Qualcomm Eliza SoC fully compatible with SM8650. The device looks very similar to SM8750 (same DP TX block v1.5.1) but with a differences in DP PHY: Eliza and SM8650 use DP PHY 4nm v7.0, SM8750 uses 3nm v8.0. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Acked-by: Rob Herring (Arm) Patchwork: https://patchwork.freedesktop.org/patch/708864/ Link: https://lore.kernel.org/r/20260304-drm-display-eliza-v2-1-ea0579f62358@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- Documentation/devicetree/bindings/display/msm/dp-controller.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/dp-controller.yaml b/Documentation/devicetree/bindings/display/msm/dp-controller.yaml index 02ddfaab5f56..8239adb7f7d3 100644 --- a/Documentation/devicetree/bindings/display/msm/dp-controller.yaml +++ b/Documentation/devicetree/bindings/display/msm/dp-controller.yaml @@ -67,6 +67,7 @@ properties: - items: - enum: + - qcom,eliza-dp - qcom,sm8750-dp - const: qcom,sm8650-dp From 4a0172e8a4d98406f4a86be622a377ed62f42a77 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 4 Mar 2026 13:58:44 +0100 Subject: [PATCH 239/712] dt-bindings: display/msm: dsi-phy-7nm: Add Eliza SoC Add DSI PHY 4nm v5.2.4 for the Qualcomm Eliza SoC, fully compatible with SM8650. Note that this DSI PHY, unlike the Eliza MDSS DSI, is not compatible with SM8750. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Patchwork: https://patchwork.freedesktop.org/patch/708866/ Link: https://lore.kernel.org/r/20260304-drm-display-eliza-v2-2-ea0579f62358@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/msm/dsi-phy-7nm.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml b/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml index 9a9a6c4abf43..3ce8a9ff4555 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml @@ -29,6 +29,10 @@ properties: - qcom,sm8550-dsi-phy-4nm - qcom,sm8650-dsi-phy-4nm - qcom,sm8750-dsi-phy-3nm + - items: + - enum: + - qcom,eliza-dsi-phy-4nm + - const: qcom,sm8650-dsi-phy-4nm - items: - enum: - qcom,qcs8300-dsi-phy-5nm From c01cca40073e01e480f034f9278f42aa02a7c567 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 4 Mar 2026 13:58:45 +0100 Subject: [PATCH 240/712] dt-bindings: display/msm: dsi-controller-main: Add Eliza SoC Add DSI controller Qualcomm Eliza SoC using exactly the same block as SM8750. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Patchwork: https://patchwork.freedesktop.org/patch/708867/ Link: https://lore.kernel.org/r/20260304-drm-display-eliza-v2-3-ea0579f62358@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/msm/dsi-controller-main.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml index eb6d38dabb08..49b8b6db45f1 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml @@ -51,6 +51,10 @@ properties: - qcom,qcs8300-dsi-ctrl - const: qcom,sa8775p-dsi-ctrl - const: qcom,mdss-dsi-ctrl + - items: + - const: qcom,eliza-dsi-ctrl + - const: qcom,sm8750-dsi-ctrl + - const: qcom,mdss-dsi-ctrl - enum: - qcom,dsi-ctrl-6g-qcm2290 - qcom,mdss-dsi-ctrl # This should always come with an SoC-specific compatible From df761873418ac306e835d76528279ad504ad3edb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 4 Mar 2026 13:58:46 +0100 Subject: [PATCH 241/712] dt-bindings: display/msm: qcom,sm8650-dpu: Add Eliza SoC Add DPU (v12.4) for Qualcomm Eliza SoC which has noticeable differences against SM8750 (v12.0) of mostly removing blocks: - INTF_3 paired with INTF_0 (no DP1), - Removed CTL4-5 blocks, - Removed VIG2-3 and DMA4-5, - Removed LM4-7, DSPP3, PINGPONG4-7, MERGE4-5 and several DSC blocks, - Added HDMI interface. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/708872/ Link: https://lore.kernel.org/r/20260304-drm-display-eliza-v2-4-ea0579f62358@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml index e29c4687c3a2..dccac525d202 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml @@ -15,6 +15,7 @@ properties: compatible: oneOf: - enum: + - qcom,eliza-dpu - qcom,glymur-dpu - qcom,kaanapali-dpu - qcom,sa8775p-dpu From 0a40e2e91b21d3fd181b58e92d74154b765e6f9f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 4 Mar 2026 13:58:47 +0100 Subject: [PATCH 242/712] dt-bindings: display/msm: qcom,eliza-mdss: Add Eliza SoC Add MDSS/MDP display subsystem for Qualcomm Eliza SoC, being overall a minor revision change against SM8750, but coming with few different components, like different DSI PHY, missing DP1 and added HDMI. The binding does not include HDMI description yet. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/708878/ Link: https://lore.kernel.org/r/20260304-drm-display-eliza-v2-5-ea0579f62358@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../bindings/display/msm/qcom,eliza-mdss.yaml | 494 ++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/msm/qcom,eliza-mdss.yaml diff --git a/Documentation/devicetree/bindings/display/msm/qcom,eliza-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,eliza-mdss.yaml new file mode 100644 index 000000000000..47938d13d1ca --- /dev/null +++ b/Documentation/devicetree/bindings/display/msm/qcom,eliza-mdss.yaml @@ -0,0 +1,494 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/msm/qcom,eliza-mdss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Eliza SoC Display MDSS + +maintainers: + - Krzysztof Kozlowski + +description: + Eliza SoC Mobile Display Subsystem (MDSS) encapsulates sub-blocks like DPU + display controller, DSI and DP interfaces etc. + +$ref: /schemas/display/msm/mdss-common.yaml# + +properties: + compatible: + const: qcom,eliza-mdss + + clocks: + items: + - description: Display AHB + - description: Display hf AXI + - description: Display core + + iommus: + maxItems: 1 + + interconnects: + items: + - description: Interconnect path from mdp0 port to the data bus + - description: Interconnect path from CPU to the reg bus + + interconnect-names: + items: + - const: mdp0-mem + - const: cpu-cfg + +patternProperties: + "^display-controller@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + contains: + const: qcom,eliza-dpu + + "^displayport-controller@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + contains: + const: qcom,eliza-dp + + "^dsi@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + contains: + const: qcom,eliza-dsi-ctrl + + "^phy@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + contains: + const: qcom,eliza-dsi-phy-4nm + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + + display-subsystem@ae00000 { + compatible = "qcom,eliza-mdss"; + reg = <0x0ae00000 0x1000>; + reg-names = "mdss"; + ranges; + + interrupts = ; + + clocks = <&disp_cc_mdss_ahb_clk>, + <&gcc_disp_hf_axi_clk>, + <&disp_cc_mdss_mdp_clk>; + + resets = <&disp_cc_mdss_core_bcr>; + + interconnects = <&mmss_noc_master_mdp QCOM_ICC_TAG_ALWAYS + &mc_virt_slave_ebi1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc_master_appss_proc QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc_slave_display_cfg QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "mdp0-mem", + "cpu-cfg"; + + power-domains = <&mdss_gdsc>; + + iommus = <&apps_smmu 0x800 0x2>; + + interrupt-controller; + #interrupt-cells = <1>; + + #address-cells = <1>; + #size-cells = <1>; + + mdss_mdp: display-controller@ae01000 { + compatible = "qcom,eliza-dpu"; + reg = <0x0ae01000 0x93000>, + <0x0aeb0000 0x2008>; + reg-names = "mdp", + "vbif"; + + interrupts-extended = <&mdss 0>; + + clocks = <&gcc_disp_hf_axi_clk>, + <&disp_cc_mdss_ahb_clk>, + <&disp_cc_mdss_mdp_lut_clk>, + <&disp_cc_mdss_mdp_clk>, + <&disp_cc_mdss_vsync_clk>; + clock-names = "nrt_bus", + "iface", + "lut", + "core", + "vsync"; + + assigned-clocks = <&disp_cc_mdss_vsync_clk>; + assigned-clock-rates = <19200000>; + + operating-points-v2 = <&mdp_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpu_intf1_out: endpoint { + remote-endpoint = <&mdss_dsi0_in>; + }; + }; + + port@1 { + reg = <1>; + + dpu_intf2_out: endpoint { + remote-endpoint = <&mdss_dsi1_in>; + }; + }; + + port@2 { + reg = <2>; + + dpu_intf0_out: endpoint { + remote-endpoint = <&mdss_dp0_in>; + }; + }; + }; + + mdp_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-207000000 { + opp-hz = /bits/ 64 <207000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-342000000 { + opp-hz = /bits/ 64 <342000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-417000000 { + opp-hz = /bits/ 64 <417000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-532000000 { + opp-hz = /bits/ 64 <532000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + required-opps = <&rpmhpd_opp_nom_l1>; + }; + + opp-660000000 { + opp-hz = /bits/ 64 <660000000>; + required-opps = <&rpmhpd_opp_turbo>; + }; + }; + }; + + dsi@ae94000 { + compatible = "qcom,eliza-dsi-ctrl", "qcom,sm8750-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0ae94000 0x400>; + reg-names = "dsi_ctrl"; + + interrupts-extended = <&mdss 4>; + + clocks = <&disp_cc_mdss_byte0_clk>, + <&disp_cc_mdss_byte0_intf_clk>, + <&disp_cc_mdss_pclk0_clk>, + <&disp_cc_mdss_esc0_clk>, + <&disp_cc_mdss_ahb_clk>, + <&gcc_disp_hf_axi_clk>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>, + <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&disp_cc_esync0_clk>, + <&disp_cc_osc_clk>, + <&disp_cc_mdss_byte0_clk_src>, + <&disp_cc_mdss_pclk0_clk_src>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus", + "dsi_pll_pixel", + "dsi_pll_byte", + "esync", + "osc", + "byte_src", + "pixel_src"; + + operating-points-v2 = <&mdss_dsi_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&mdss_dsi0_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dsi0_in: endpoint { + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dsi0_out: endpoint { + remote-endpoint = <&panel0_in>; + data-lanes = <0 1 2 3>; + }; + }; + }; + + mdss_dsi_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-140630000 { + opp-hz = /bits/ 64 <140630000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-187500000 { + opp-hz = /bits/ 64 <187500000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-358000000 { + opp-hz = /bits/ 64 <358000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; + }; + + mdss_dsi0_phy: phy@ae95000 { + compatible = "qcom,eliza-dsi-phy-4nm", "qcom,sm8650-dsi-phy-4nm"; + reg = <0x0ae95000 0x200>, + <0x0ae95200 0x280>, + <0x0ae95500 0x400>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + clocks = <&disp_cc_mdss_ahb_clk>, + <&bi_tcxo_div2>; + clock-names = "iface", + "ref"; + + #clock-cells = <1>; + #phy-cells = <0>; + + vdds-supply = <&vreg_l2b>; + }; + + dsi@ae96000 { + compatible = "qcom,eliza-dsi-ctrl", "qcom,sm8750-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0ae96000 0x400>; + reg-names = "dsi_ctrl"; + + interrupts-extended = <&mdss 5>; + + clocks = <&disp_cc_mdss_byte1_clk>, + <&disp_cc_mdss_byte1_intf_clk>, + <&disp_cc_mdss_pclk1_clk>, + <&disp_cc_mdss_esc1_clk>, + <&disp_cc_mdss_ahb_clk>, + <&gcc_disp_hf_axi_clk>, + <&mdss_dsi1_phy DSI_PIXEL_PLL_CLK>, + <&mdss_dsi1_phy DSI_BYTE_PLL_CLK>, + <&disp_cc_esync1_clk>, + <&disp_cc_osc_clk>, + <&disp_cc_mdss_byte1_clk_src>, + <&disp_cc_mdss_pclk1_clk_src>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus", + "dsi_pll_pixel", + "dsi_pll_byte", + "esync", + "osc", + "byte_src", + "pixel_src"; + + operating-points-v2 = <&mdss_dsi_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&mdss_dsi1_phy>; + phy-names = "dsi"; + + vdda-supply = <&vreg_l4b>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dsi1_in: endpoint { + remote-endpoint = <&dpu_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dsi1_out: endpoint { + remote-endpoint = <&panel1_in>; + data-lanes = <0 1 2 3>; + }; + }; + }; + }; + + mdss_dsi1_phy: phy@ae97000 { + compatible = "qcom,eliza-dsi-phy-4nm", "qcom,sm8650-dsi-phy-4nm"; + reg = <0x0ae97000 0x200>, + <0x0ae97200 0x280>, + <0x0ae97500 0x400>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + clocks = <&disp_cc_mdss_ahb_clk>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", + "ref"; + + #clock-cells = <1>; + #phy-cells = <0>; + + vdds-supply = <&vreg_l2b>; + }; + + displayport-controller@af54000 { + compatible = "qcom,eliza-dp", "qcom,sm8650-dp"; + reg = <0xaf54000 0x104>, + <0xaf54200 0xc0>, + <0xaf55000 0x770>, + <0xaf56000 0x9c>, + <0xaf57000 0x9c>; + + interrupts-extended = <&mdss 12>; + + clocks = <&disp_cc_mdss_ahb_clk>, + <&disp_cc_mdss_dptx0_aux_clk>, + <&disp_cc_mdss_dptx0_link_clk>, + <&disp_cc_mdss_dptx0_link_intf_clk>, + <&disp_cc_mdss_dptx0_pixel0_clk>, + <&disp_cc_mdss_dptx0_pixel1_clk>; + clock-names = "core_iface", + "core_aux", + "ctrl_link", + "ctrl_link_iface", + "stream_pixel", + "stream_1_pixel"; + + assigned-clocks = <&disp_cc_mdss_dptx0_link_clk_src>, + <&disp_cc_mdss_dptx0_pixel0_clk_src>, + <&disp_cc_mdss_dptx0_pixel1_clk_src>; + assigned-clock-parents = <&usb_dp_qmpphy QMP_USB43DP_DP_LINK_CLK>, + <&usb_dp_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&usb_dp_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; + + operating-points-v2 = <&dp_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&usb_dp_qmpphy QMP_USB43DP_DP_PHY>; + phy-names = "dp"; + + #sound-dai-cells = <0>; + + dp_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-192000000 { + opp-hz = /bits/ 64 <192000000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-270000000 { + opp-hz = /bits/ 64 <270000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-540000000 { + opp-hz = /bits/ 64 <540000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-810000000 { + opp-hz = /bits/ 64 <810000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dp0_in: endpoint { + remote-endpoint = <&dpu_intf0_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dp0_out: endpoint { + data-lanes = <0 1 2 3>; + remote-endpoint = <&usb_dp_qmpphy_dp_in>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; + }; + }; + }; + }; + }; From 0eb707bbc7fc0b42601560e4fea0698d956a7a9a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 4 Mar 2026 13:58:49 +0100 Subject: [PATCH 243/712] drm/msm/dpu: Add support for Eliza SoC Add support for DPU (v12.4) on Qualcomm Eliza SoC, with one incomplete/skipped part: HDMI interface (INT_4). Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/708879/ Link: https://lore.kernel.org/r/20260304-drm-display-eliza-v2-7-ea0579f62358@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../msm/disp/dpu1/catalog/dpu_12_4_eliza.h | 365 ++++++++++++++++++ .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 1 + .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 1 + 4 files changed, 368 insertions(+) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h new file mode 100644 index 000000000000..f718a181af21 --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h @@ -0,0 +1,365 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _DPU_12_4_ELIZA_H +#define _DPU_12_4_ELIZA_H + +static const struct dpu_caps eliza_dpu_caps = { + .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .max_mixer_blendstages = 0xb, + .has_src_split = true, + .has_dim_layer = true, + .has_idle_pc = true, + .has_3d_merge = true, + .max_linewidth = 8192, + .pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE, +}; + +static const struct dpu_mdp_cfg eliza_mdp = { + .name = "top_0", + .base = 0, .len = 0x494, + .clk_ctrls = { + [DPU_CLK_CTRL_REG_DMA] = { .reg_off = 0x2bc, .bit_off = 20 }, + }, +}; + +static const struct dpu_ctl_cfg eliza_ctl[] = { + { + .name = "ctl_0", .id = CTL_0, + .base = 0x15000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 9), + }, { + .name = "ctl_1", .id = CTL_1, + .base = 0x16000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 10), + }, { + .name = "ctl_2", .id = CTL_2, + .base = 0x17000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 11), + }, { + .name = "ctl_3", .id = CTL_3, + .base = 0x18000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 12), + }, +}; + +static const struct dpu_sspp_cfg eliza_sspp[] = { + { + .name = "sspp_0", .id = SSPP_VIG0, + .base = 0x4000, .len = 0x344, + .features = VIG_SDM845_MASK_SDMA, + .sblk = &dpu_vig_sblk_qseed3_3_4, + .xin_id = 0, + .type = SSPP_TYPE_VIG, + }, { + .name = "sspp_1", .id = SSPP_VIG1, + .base = 0x6000, .len = 0x344, + .features = VIG_SDM845_MASK_SDMA, + .sblk = &dpu_vig_sblk_qseed3_3_4, + .xin_id = 4, + .type = SSPP_TYPE_VIG, + }, { + .name = "sspp_8", .id = SSPP_DMA0, + .base = 0x24000, .len = 0x344, + .features = DMA_SDM845_MASK_SDMA, + .sblk = &dpu_dma_sblk, + .xin_id = 1, + .type = SSPP_TYPE_DMA, + }, { + .name = "sspp_9", .id = SSPP_DMA1, + .base = 0x26000, .len = 0x344, + .features = DMA_SDM845_MASK_SDMA, + .sblk = &dpu_dma_sblk, + .xin_id = 5, + .type = SSPP_TYPE_DMA, + }, { + .name = "sspp_10", .id = SSPP_DMA2, + .base = 0x28000, .len = 0x344, + .features = DMA_SDM845_MASK_SDMA, + .sblk = &dpu_dma_sblk, + .xin_id = 9, + .type = SSPP_TYPE_DMA, + }, { + .name = "sspp_11", .id = SSPP_DMA3, + .base = 0x2a000, .len = 0x344, + .features = DMA_SDM845_MASK_SDMA, + .sblk = &dpu_dma_sblk, + .xin_id = 13, + .type = SSPP_TYPE_DMA, + }, +}; + +static const struct dpu_lm_cfg eliza_lm[] = { + { + .name = "lm_0", .id = LM_0, + .base = 0x44000, .len = 0x400, + .features = MIXER_MSM8998_MASK, + .sblk = &sm8750_lm_sblk, + .lm_pair = LM_1, + .pingpong = PINGPONG_0, + .dspp = DSPP_0, + }, { + .name = "lm_1", .id = LM_1, + .base = 0x45000, .len = 0x400, + .features = MIXER_MSM8998_MASK, + .sblk = &sm8750_lm_sblk, + .lm_pair = LM_0, + .pingpong = PINGPONG_1, + .dspp = DSPP_1, + }, { + .name = "lm_2", .id = LM_2, + .base = 0x46000, .len = 0x400, + .features = MIXER_MSM8998_MASK, + .sblk = &sm8750_lm_sblk, + .lm_pair = LM_3, + .pingpong = PINGPONG_2, + .dspp = DSPP_2, + }, { + .name = "lm_3", .id = LM_3, + .base = 0x47000, .len = 0x400, + .features = MIXER_MSM8998_MASK, + .sblk = &sm8750_lm_sblk, + .lm_pair = LM_2, + .pingpong = PINGPONG_3, + }, +}; + +static const struct dpu_dspp_cfg eliza_dspp[] = { + { + .name = "dspp_0", .id = DSPP_0, + .base = 0x54000, .len = 0x1800, + .sblk = &sm8750_dspp_sblk, + }, { + .name = "dspp_1", .id = DSPP_1, + .base = 0x56000, .len = 0x1800, + .sblk = &sm8750_dspp_sblk, + }, { + .name = "dspp_2", .id = DSPP_2, + .base = 0x58000, .len = 0x1800, + .sblk = &sm8750_dspp_sblk, + }, +}; + +static const struct dpu_pingpong_cfg eliza_pp[] = { + { + .name = "pingpong_0", .id = PINGPONG_0, + .base = 0x69000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_0, + .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8), + }, { + .name = "pingpong_1", .id = PINGPONG_1, + .base = 0x6a000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_0, + .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9), + }, { + .name = "pingpong_2", .id = PINGPONG_2, + .base = 0x6b000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_1, + .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 10), + }, { + .name = "pingpong_3", .id = PINGPONG_3, + .base = 0x6c000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_1, + .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11), + }, { + .name = "pingpong_cwb_0", .id = PINGPONG_CWB_0, + .base = 0x66000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_2, + }, { + .name = "pingpong_cwb_1", .id = PINGPONG_CWB_1, + .base = 0x66400, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_2, + }, { + .name = "pingpong_cwb_2", .id = PINGPONG_CWB_2, + .base = 0x7e000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_3, + }, { + .name = "pingpong_cwb_3", .id = PINGPONG_CWB_3, + .base = 0x7e400, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_3, + }, +}; + +static const struct dpu_merge_3d_cfg eliza_merge_3d[] = { + { + .name = "merge_3d_0", .id = MERGE_3D_0, + .base = 0x4e000, .len = 0x1c, + }, { + .name = "merge_3d_1", .id = MERGE_3D_1, + .base = 0x4f000, .len = 0x1c, + }, { + .name = "merge_3d_2", .id = MERGE_3D_2, + .base = 0x66700, .len = 0x1c, + }, { + .name = "merge_3d_3", .id = MERGE_3D_3, + .base = 0x7e700, .len = 0x1c, + }, +}; + +/* + * NOTE: Each display compression engine (DCE) contains dual hard + * slice DSC encoders so both share same base address but with + * its own different sub block address. + */ +static const struct dpu_dsc_cfg eliza_dsc[] = { + { + .name = "dce_0_0", .id = DSC_0, + .base = 0x80000, .len = 0x8, + .features = BIT(DPU_DSC_NATIVE_42x_EN), + .sblk = &sm8750_dsc_sblk_0, + }, { + .name = "dce_0_1", .id = DSC_1, + .base = 0x80000, .len = 0x8, + .features = BIT(DPU_DSC_NATIVE_42x_EN), + .sblk = &sm8750_dsc_sblk_1, + }, { + .name = "dce_1_0", .id = DSC_2, + .base = 0x81000, .len = 0x8, + .features = BIT(DPU_DSC_NATIVE_42x_EN), + .sblk = &sm8750_dsc_sblk_0, + }, +}; + +static const struct dpu_wb_cfg eliza_wb[] = { + { + .name = "wb_2", .id = WB_2, + .base = 0x65000, .len = 0x2c8, + .features = WB_SDM845_MASK, + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), + .xin_id = 6, + .vbif_idx = VBIF_RT, + .maxlinewidth = 4096, + .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), + }, +}; + +static const struct dpu_cwb_cfg eliza_cwb[] = { + { + .name = "cwb_0", .id = CWB_0, + .base = 0x66200, .len = 0x20, + }, + { + .name = "cwb_1", .id = CWB_1, + .base = 0x66600, .len = 0x20, + }, + { + .name = "cwb_2", .id = CWB_2, + .base = 0x7e200, .len = 0x20, + }, + { + .name = "cwb_3", .id = CWB_3, + .base = 0x7e600, .len = 0x20, + }, +}; + +static const struct dpu_intf_cfg eliza_intf[] = { + { + .name = "intf_0", .id = INTF_0, + .base = 0x34000, .len = 0x4bc, + .type = INTF_DP, + .controller_id = MSM_DP_CONTROLLER_0, + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 24), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 25), + }, { + .name = "intf_1", .id = INTF_1, + .base = 0x35000, .len = 0x4bc, + .type = INTF_DSI, + .controller_id = MSM_DSI_CONTROLLER_0, + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 26), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 27), + .intr_tear_rd_ptr = DPU_IRQ_IDX(MDP_INTF1_TEAR_INTR, 2), + }, { + .name = "intf_2", .id = INTF_2, + .base = 0x36000, .len = 0x4bc, + .type = INTF_DSI, + .controller_id = MSM_DSI_CONTROLLER_1, + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 28), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 29), + .intr_tear_rd_ptr = DPU_IRQ_IDX(MDP_INTF2_TEAR_INTR, 2), + }, { + .name = "intf_3", .id = INTF_3, + .base = 0x37000, .len = 0x4bc, + .type = INTF_DP, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), + } +}; + +static const struct dpu_perf_cfg eliza_perf_data = { + .max_bw_low = 6800000, + .max_bw_high = 14200000, + .min_core_ib = 2500000, + .min_llcc_ib = 0, + .min_dram_ib = 1600000, + .min_prefill_lines = 35, + .danger_lut_tbl = {0x3ffff, 0x3ffff, 0x0}, + .safe_lut_tbl = {0xfe00, 0xfe00, 0xffff}, + .qos_lut_tbl = { + {.nentry = ARRAY_SIZE(sc7180_qos_linear), + .entries = sc7180_qos_linear + }, + {.nentry = ARRAY_SIZE(sc7180_qos_macrotile), + .entries = sc7180_qos_macrotile + }, + {.nentry = ARRAY_SIZE(sc7180_qos_nrt), + .entries = sc7180_qos_nrt + }, + /* TODO: macrotile-qseed is different from macrotile */ + }, + .cdp_cfg = { + {.rd_enable = 1, .wr_enable = 1}, + {.rd_enable = 1, .wr_enable = 0} + }, + .clk_inefficiency_factor = 105, + .bw_inefficiency_factor = 120, +}; + +static const struct dpu_mdss_version eliza_mdss_ver = { + .core_major_ver = 12, + .core_minor_ver = 4, +}; + +const struct dpu_mdss_cfg dpu_eliza_cfg = { + .mdss_ver = &eliza_mdss_ver, + .caps = &eliza_dpu_caps, + .mdp = &eliza_mdp, + .cdm = &dpu_cdm_5_x, + .ctl_count = ARRAY_SIZE(eliza_ctl), + .ctl = eliza_ctl, + .sspp_count = ARRAY_SIZE(eliza_sspp), + .sspp = eliza_sspp, + .mixer_count = ARRAY_SIZE(eliza_lm), + .mixer = eliza_lm, + .dspp_count = ARRAY_SIZE(eliza_dspp), + .dspp = eliza_dspp, + .pingpong_count = ARRAY_SIZE(eliza_pp), + .pingpong = eliza_pp, + .dsc_count = ARRAY_SIZE(eliza_dsc), + .dsc = eliza_dsc, + .merge_3d_count = ARRAY_SIZE(eliza_merge_3d), + .merge_3d = eliza_merge_3d, + .wb_count = ARRAY_SIZE(eliza_wb), + .wb = eliza_wb, + .cwb_count = ARRAY_SIZE(eliza_cwb), + .cwb = sm8650_cwb, + .intf_count = ARRAY_SIZE(eliza_intf), + .intf = eliza_intf, + .vbif_count = ARRAY_SIZE(sm8650_vbif), + .vbif = sm8650_vbif, + .perf = &eliza_perf_data, +}; + +#endif diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index c4e1f6b7345d..b096b748707e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -771,4 +771,5 @@ static const struct dpu_qos_lut_entry sc7180_qos_nrt[] = { #include "catalog/dpu_10_0_sm8650.h" #include "catalog/dpu_12_0_sm8750.h" #include "catalog/dpu_12_2_glymur.h" +#include "catalog/dpu_12_4_eliza.h" #include "catalog/dpu_13_0_kaanapali.h" diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index 70d5ed4732f2..dedab08ea1d7 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -767,6 +767,7 @@ struct dpu_mdss_cfg { const struct dpu_format_extended *vig_formats; }; +extern const struct dpu_mdss_cfg dpu_eliza_cfg; extern const struct dpu_mdss_cfg dpu_glymur_cfg; extern const struct dpu_mdss_cfg dpu_kaanapali_cfg; extern const struct dpu_mdss_cfg dpu_msm8917_cfg; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 7c5b3495bddf..af094baadbdb 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1505,6 +1505,7 @@ static const struct dev_pm_ops dpu_pm_ops = { }; static const struct of_device_id dpu_dt_match[] = { + { .compatible = "qcom,eliza-dpu", .data = &dpu_eliza_cfg, }, { .compatible = "qcom,glymur-dpu", .data = &dpu_glymur_cfg, }, { .compatible = "qcom,kaanapali-dpu", .data = &dpu_kaanapali_cfg, }, { .compatible = "qcom,msm8917-mdp5", .data = &dpu_msm8917_cfg, }, From 3e64e6959d8babd20d837b25bf93abf600fe4cb7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 4 Mar 2026 13:58:50 +0100 Subject: [PATCH 244/712] drm/msm/mdss: Add support for Eliza SoC Add support for the Qualcomm Eliza SoC platform. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/708874/ Link: https://lore.kernel.org/r/20260304-drm-display-eliza-v2-8-ea0579f62358@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_mdss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 9047e8d9ee89..a86776425f26 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -560,6 +560,7 @@ static const struct msm_mdss_data data_153k6 = { static const struct of_device_id mdss_dt_match[] = { { .compatible = "qcom,mdss", .data = &data_153k6 }, + { .compatible = "qcom,eliza-mdss", .data = &data_57k }, { .compatible = "qcom,glymur-mdss", .data = &data_57k }, { .compatible = "qcom,kaanapali-mdss", .data = &data_57k }, { .compatible = "qcom,msm8998-mdss", .data = &data_76k8 }, From 59f6bdf913ddc0d0cffbba0f4e6e7db79e37dc5e Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sun, 8 Mar 2026 14:48:32 +0800 Subject: [PATCH 245/712] dt-bindings: display: msm-dsi-phy-7nm: Add SC8280XP Since SC8280XP and SA8775P have the same values for the REVISION_ID registers, then we fallback to SA8775P compatible. Signed-off-by: Pengyu Luo Reviewed-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/709943/ Link: https://lore.kernel.org/r/20260308064835.479356-2-mitltlatltl@gmail.com Signed-off-by: Dmitry Baryshkov --- Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml b/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml index 3ce8a9ff4555..b5a0c1461250 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml @@ -36,6 +36,7 @@ properties: - items: - enum: - qcom,qcs8300-dsi-phy-5nm + - qcom,sc8280xp-dsi-phy-5nm - const: qcom,sa8775p-dsi-phy-5nm reg: From 1607c084b18f697c06c28e46c3ee301875750fcc Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sun, 8 Mar 2026 14:48:33 +0800 Subject: [PATCH 246/712] dt-bindings: display/msm: dsi-controller-main: Add SC8280XP Since SC8280XP and SA8775P have the same DSI version(2.5.1), then we fallback to SA8775P compatible. Signed-off-by: Pengyu Luo Reviewed-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/709944/ Link: https://lore.kernel.org/r/20260308064835.479356-3-mitltlatltl@gmail.com Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/msm/dsi-controller-main.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml index 49b8b6db45f1..a24fcb914418 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml @@ -49,6 +49,7 @@ properties: - items: - enum: - qcom,qcs8300-dsi-ctrl + - qcom,sc8280xp-dsi-ctrl - const: qcom,sa8775p-dsi-ctrl - const: qcom,mdss-dsi-ctrl - items: From 6113aaf7a5ce2a29e7360bb3dede4781d5966b8d Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sun, 8 Mar 2026 14:48:34 +0800 Subject: [PATCH 247/712] dt-bindings: display: msm: Document DSI controller and DSI PHY on SC8280XP Document DSI controller and DSI phy on SC8280XP platform. Signed-off-by: Pengyu Luo Reviewed-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/709947/ Link: https://lore.kernel.org/r/20260308064835.479356-4-mitltlatltl@gmail.com Signed-off-by: Dmitry Baryshkov --- .../display/msm/qcom,sc8280xp-mdss.yaml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sc8280xp-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sc8280xp-mdss.yaml index af79406e1604..a710cc84ec57 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,sc8280xp-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,sc8280xp-mdss.yaml @@ -50,6 +50,22 @@ patternProperties: - qcom,sc8280xp-dp - qcom,sc8280xp-edp + "^dsi@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + contains: + const: qcom,sc8280xp-dsi-ctrl + + "^phy@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + contains: + const: qcom,sc8280xp-dsi-phy-5nm + unevaluatedProperties: false examples: @@ -129,6 +145,20 @@ examples: }; }; + port@1 { + reg = <1>; + dpu_intf1_out: endpoint { + remote-endpoint = <&mdss0_dsi0_in>; + }; + }; + + port@2 { + reg = <2>; + dpu_intf2_out: endpoint { + remote-endpoint = <&mdss0_dsi1_in>; + }; + }; + port@4 { reg = <4>; endpoint { From bc1dccc518cc5ab5140fba06c27e7188e0ed342b Mon Sep 17 00:00:00 2001 From: Yuanjie Yang Date: Mon, 9 Mar 2026 14:37:20 +0800 Subject: [PATCH 248/712] drm/msm/dpu: fix mismatch between power and frequency During DPU runtime suspend, calling dev_pm_opp_set_rate(dev, 0) drops the MMCX rail to MIN_SVS while the core clock frequency remains at its original (highest) rate. When runtime resume re-enables the clock, this may result in a mismatch between the rail voltage and the clock rate. For example, in the DPU bind path, the sequence could be: cpu0: dev_sync_state -> rpmhpd_sync_state cpu1: dpu_kms_hw_init timeline 0 ------------------------------------------------> t After rpmhpd_sync_state, the voltage performance is no longer guaranteed to stay at the highest level. During dpu_kms_hw_init, calling dev_pm_opp_set_rate(dev, 0) drops the voltage, causing the MMCX rail to fall to MIN_SVS while the core clock is still at its maximum frequency. When the power is re-enabled, only the clock is enabled, leading to a situation where the MMCX rail is at MIN_SVS but the core clock is at its highest rate. In this state, the rail cannot sustain the clock rate, which may cause instability or system crash. Remove the call to dev_pm_opp_set_rate(dev, 0) from dpu_runtime_suspend to ensure the correct vote is restored when DPU resumes. Fixes: b0530eb11913 ("drm/msm/dpu: Use OPP API to set clk/perf state") Signed-off-by: Yuanjie Yang Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/710077/ Link: https://lore.kernel.org/r/20260309063720.13572-1-yuanjie.yang@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index af094baadbdb..19512042d42b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1461,8 +1461,6 @@ static int __maybe_unused dpu_runtime_suspend(struct device *dev) struct msm_drm_private *priv = platform_get_drvdata(pdev); struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms); - /* Drop the performance state vote */ - dev_pm_opp_set_rate(dev, 0); clk_bulk_disable_unprepare(dpu_kms->num_clocks, dpu_kms->clocks); for (i = 0; i < dpu_kms->num_paths; i++) From 958adefc4c0fddee3b12269da5dd7cb49bac953f Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Mon, 9 Mar 2026 18:02:53 +0800 Subject: [PATCH 249/712] drm/msm/dsi: add the missing parameter description Add a description for is_bonded_dsi in dsi_adjust_pclk_for_compression to match the existing kernel-doc comment. Fixes: e4eb11b34d6c ("drm/msm/dsi: fix pclk rate calculation for bonded dsi") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603080314.XeqyRZ7A-lkp@intel.com/ Signed-off-by: Pengyu Luo Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/710112/ Link: https://lore.kernel.org/r/20260309100254.877801-1-mitltlatltl@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index db6da99375a1..6cb634590e7a 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -569,6 +569,7 @@ void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host) * dsi_adjust_pclk_for_compression() - Adjust the pclk rate for compression case * @mode: The selected mode for the DSI output * @dsc: DRM DSC configuration for this DSI output + * @is_bonded_dsi: True if two DSI controllers are bonded * * Adjust the pclk rate by calculating a new hdisplay proportional to * the compression ratio such that: From d19faa0dcc6abd35ed67084d8e31590a243f77c0 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 10 Mar 2026 14:20:25 +0100 Subject: [PATCH 250/712] drm/msm/mdss: Add a TODO for better managing the MDSS clock power state There's a small window where the MDP clock could be set to a high rate (say, from the bootloader) without a corresponding RPM(H)PD vote to back it up. This is normally not an issue, but could be, if rmmod fails to shut down the display driver cleanly, and the module is inserted again, or when the providers' .sync_state has timed out. Mark a TODO to fix it one day. Linking the relevant discussion below. Link: https://lore.kernel.org/linux-arm-msm/d5c4eed5-bd87-4156-b178-2d78140ec8a9@oss.qualcomm.com/ Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/710572/ Link: https://lore.kernel.org/r/20260310-topic-mdss_power_todo-v1-1-59457b8b7486@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_mdss.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index a86776425f26..90c3fa0681a0 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -262,6 +262,14 @@ static int msm_mdss_enable(struct msm_mdss *msm_mdss) icc_set_bw(msm_mdss->reg_bus_path, 0, msm_mdss->reg_bus_bw); + /* + * TODO: + * Previous users (e.g. the bootloader) may have left this clock at a high rate, which + * would remain set, as prepare_enable() doesn't reprogram it. This theoretically poses a + * risk of brownout, but realistically this path is almost exclusively excercised after the + * correct OPP has been set in one of the MDPn or DPU drivers, or during initial probe, + * before the RPM(H)PD sync_state is done. + */ ret = clk_bulk_prepare_enable(msm_mdss->num_clocks, msm_mdss->clocks); if (ret) { dev_err(msm_mdss->dev, "clock enable failed, ret:%d\n", ret); From fdbc6391b4d8353865038ede74c9ed56bfb04e96 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 10 Mar 2026 13:25:01 +0000 Subject: [PATCH 251/712] drm/msm/hdmi: make 'msm_hdmi_pm_ops' static The struct 'msm_hdmi_pm_ops' is not used outside of the file it is defined in, so make it static. Fixes the following sparse warning: drivers/gpu/drm/msm/hdmi/hdmi.c:432:1: warning: symbol 'msm_hdmi_pm_ops' was not declared. Should it be static? Signed-off-by: Ben Dooks Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/710583/ Link: https://lore.kernel.org/r/20260310132501.195954-1-ben.dooks@codethink.co.uk Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index d5ef5089c9e9..368d6d56c4fa 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -429,7 +429,7 @@ static int msm_hdmi_runtime_resume(struct device *dev) return ret; } -DEFINE_RUNTIME_DEV_PM_OPS(msm_hdmi_pm_ops, msm_hdmi_runtime_suspend, msm_hdmi_runtime_resume, NULL); +static DEFINE_RUNTIME_DEV_PM_OPS(msm_hdmi_pm_ops, msm_hdmi_runtime_suspend, msm_hdmi_runtime_resume, NULL); static const struct of_device_id msm_hdmi_dt_match[] = { { .compatible = "qcom,hdmi-tx-8998", .data = &hdmi_tx_8974_config }, From 8c6c93b7db42d15c6e8c2540a648d32986a04b1a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 11 Mar 2026 20:16:21 +0100 Subject: [PATCH 252/712] drm/msm/hdmi: Fix wrong CTRL1 register used in writing info frames Commit 384d2b03d0a1 ("drm/msm/hdmi: make use of the drm_connector_hdmi framework") changed the unconditional register writes in few places to updates: read, apply mask, write. The new code reads REG_HDMI_INFOFRAME_CTRL1 register, applies fields/mask for HDMI_INFOFRAME_CTRL0 register and finally writes to HDMI_INFOFRAME_CTRL0. This difference between CTRL1 and CTRL0 looks unintended and may result in wrong data being written to HDMI bridge registers. Cc: Fixes: 384d2b03d0a1 ("drm/msm/hdmi: make use of the drm_connector_hdmi framework") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/711156/ Link: https://lore.kernel.org/r/20260311191620.245394-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 46fd58646d32..93a491a103e0 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -80,7 +80,7 @@ static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi, for (i = 0; i < ARRAY_SIZE(buf); i++) hdmi_write(hdmi, REG_HDMI_AVI_INFO(i), buf[i]); - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); + val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); val |= HDMI_INFOFRAME_CTRL0_AVI_SEND | HDMI_INFOFRAME_CTRL0_AVI_CONT; hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val); @@ -116,7 +116,7 @@ static int msm_hdmi_config_audio_infoframe(struct hdmi *hdmi, buffer[9] << 16 | buffer[10] << 24); - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); + val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); val |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND | HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT | HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE | From 5a9a712b0b0015b4a9f80699a9a3019a74e929e6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 11 Mar 2026 21:17:25 +0100 Subject: [PATCH 253/712] drm/msm/hdmi: Simplify with dev_of_node() Simplify chain of pointer dereferences with dev_of_node() which also checks if 'dev' argument is non-NULL. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/711167/ Link: https://lore.kernel.org/r/20260311-drm-msm-hdmi-cleanup-v1-1-c5535245f6de@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 368d6d56c4fa..09fda9531080 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -91,7 +91,7 @@ static int msm_hdmi_get_phy(struct hdmi *hdmi) struct platform_device *phy_pdev; struct device_node *phy_node; - phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0); + phy_node = of_parse_phandle(dev_of_node(&pdev->dev), "phys", 0); if (!phy_node) { DRM_DEV_ERROR(&pdev->dev, "cannot find phy device\n"); return -ENXIO; @@ -287,7 +287,7 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) spin_lock_init(&hdmi->reg_lock); mutex_init(&hdmi->state_mutex); - ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 1, 0, NULL, &hdmi->next_bridge); + ret = drm_of_find_panel_or_bridge(dev_of_node(dev), 1, 0, NULL, &hdmi->next_bridge); if (ret && ret != -ENODEV) return ret; From ae505afd62f32045a575f57f43b24b3bb9ec6e54 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 11 Mar 2026 21:17:26 +0100 Subject: [PATCH 254/712] drm/msm/hdmi: Avoid double error print on msm_hdmi_get_phy() failure msm_hdmi_get_phy() already prints error messages on each error path using dev_err_probe(), so final DRM_DEV_ERROR() would duplicate it and possibly flood the dmesg on probe deferrals. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/711168/ Link: https://lore.kernel.org/r/20260311-drm-msm-hdmi-cleanup-v1-2-c5535245f6de@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 09fda9531080..6e31db763923 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -358,10 +358,8 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) gpiod_set_consumer_name(hdmi->hpd_gpiod, "HDMI_HPD"); ret = msm_hdmi_get_phy(hdmi); - if (ret) { - DRM_DEV_ERROR(&pdev->dev, "failed to get phy\n"); + if (ret) return ret; - } ret = devm_pm_runtime_enable(&pdev->dev); if (ret) From 69c68ab38d67c090ea7660f5f68bcb60d160774f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 11 Mar 2026 21:17:27 +0100 Subject: [PATCH 255/712] drm/msm/hdmi: Simplify with local 'dev' variable msm_hdmi_dev_probe() function already stores pdev->dev in local variable, so use it directly to make code simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/711171/ Link: https://lore.kernel.org/r/20260311-drm-msm-hdmi-cleanup-v1-3-c5535245f6de@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 6e31db763923..d5587495bca0 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -278,7 +278,7 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) if (!config) return -EINVAL; - hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) return -ENOMEM; @@ -304,7 +304,7 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) hdmi->qfprom_mmio = msm_ioremap(pdev, "qfprom_physical"); if (IS_ERR(hdmi->qfprom_mmio)) { - DRM_DEV_INFO(&pdev->dev, "can't find qfprom resource\n"); + DRM_DEV_INFO(dev, "can't find qfprom resource\n"); hdmi->qfprom_mmio = NULL; } @@ -312,8 +312,7 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) if (hdmi->irq < 0) return hdmi->irq; - hdmi->pwr_regs = devm_kcalloc(&pdev->dev, - config->pwr_reg_cnt, + hdmi->pwr_regs = devm_kcalloc(dev, config->pwr_reg_cnt, sizeof(hdmi->pwr_regs[0]), GFP_KERNEL); if (!hdmi->pwr_regs) @@ -322,12 +321,11 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) for (i = 0; i < config->pwr_reg_cnt; i++) hdmi->pwr_regs[i].supply = config->pwr_reg_names[i]; - ret = devm_regulator_bulk_get(&pdev->dev, config->pwr_reg_cnt, hdmi->pwr_regs); + ret = devm_regulator_bulk_get(dev, config->pwr_reg_cnt, hdmi->pwr_regs); if (ret) return dev_err_probe(dev, ret, "failed to get pwr regulators\n"); - hdmi->pwr_clks = devm_kcalloc(&pdev->dev, - config->pwr_clk_cnt, + hdmi->pwr_clks = devm_kcalloc(dev, config->pwr_clk_cnt, sizeof(hdmi->pwr_clks[0]), GFP_KERNEL); if (!hdmi->pwr_clks) @@ -336,16 +334,16 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) for (i = 0; i < config->pwr_clk_cnt; i++) hdmi->pwr_clks[i].id = config->pwr_clk_names[i]; - ret = devm_clk_bulk_get(&pdev->dev, config->pwr_clk_cnt, hdmi->pwr_clks); + ret = devm_clk_bulk_get(dev, config->pwr_clk_cnt, hdmi->pwr_clks); if (ret) return ret; - hdmi->extp_clk = devm_clk_get_optional(&pdev->dev, "extp"); + hdmi->extp_clk = devm_clk_get_optional(dev, "extp"); if (IS_ERR(hdmi->extp_clk)) return dev_err_probe(dev, PTR_ERR(hdmi->extp_clk), "failed to get extp clock\n"); - hdmi->hpd_gpiod = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN); + hdmi->hpd_gpiod = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); /* This will catch e.g. -EPROBE_DEFER */ if (IS_ERR(hdmi->hpd_gpiod)) return dev_err_probe(dev, PTR_ERR(hdmi->hpd_gpiod), @@ -361,13 +359,13 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(dev); if (ret) goto err_put_phy; platform_set_drvdata(pdev, hdmi); - ret = component_add(&pdev->dev, &msm_hdmi_ops); + ret = component_add(dev, &msm_hdmi_ops); if (ret) goto err_put_phy; From bc11794cfe00124f8e71da75ab66214948f77af4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 11 Mar 2026 21:17:28 +0100 Subject: [PATCH 256/712] drm/msm/hdmi: Consistently use u32 instead of uint32_t Linux coding style asks to use kernel types like u32 instead of uint32_t and code already has it in other places, so unify the remaining pieces. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/711172/ Link: https://lore.kernel.org/r/20260311-drm-msm-hdmi-cleanup-v1-4-c5535245f6de@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi_audio.c | 5 ++--- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 4 ++-- drivers/gpu/drm/msm/hdmi/hdmi_hpd.c | 4 ++-- drivers/gpu/drm/msm/hdmi/hdmi_i2c.c | 12 ++++++------ 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index d5587495bca0..852abb2466f0 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -20,7 +20,7 @@ void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) { - uint32_t ctrl = 0; + u32 ctrl = 0; unsigned long flags; spin_lock_irqsave(&hdmi->reg_lock, flags); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c index d9a8dc9dae8f..249c167ab04d 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -17,8 +17,7 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) { struct hdmi_audio *audio = &hdmi->audio; bool enabled = audio->enabled; - uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl; - uint32_t audio_config; + u32 acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl, audio_config; if (!hdmi->connector->display_info.is_hdmi) return -EINVAL; @@ -43,7 +42,7 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SELECT__MASK; if (enabled) { - uint32_t n, cts, multiplier; + u32 n, cts, multiplier; enum hdmi_acr_cts select; drm_hdmi_acr_get_n_cts(hdmi->pixclock, audio->rate, &n, &cts); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 93a491a103e0..5abc208d744d 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -355,7 +355,7 @@ static void msm_hdmi_set_timings(struct hdmi *hdmi, const struct drm_display_mode *mode) { int hstart, hend, vstart, vend; - uint32_t frame_ctrl; + u32 frame_ctrl; hstart = mode->htotal - mode->hsync_start; hend = mode->htotal - mode->hsync_start + mode->hdisplay; @@ -408,7 +408,7 @@ static const struct drm_edid *msm_hdmi_bridge_edid_read(struct drm_bridge *bridg struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct drm_edid *drm_edid; - uint32_t hdmi_ctrl; + u32 hdmi_ctrl; hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL); hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hpd.c b/drivers/gpu/drm/msm/hdmi/hdmi_hpd.c index 114b0d507700..2cccd9062584 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_hpd.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_hpd.c @@ -65,7 +65,7 @@ void msm_hdmi_hpd_enable(struct drm_bridge *bridge) struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; struct device *dev = &hdmi->pdev->dev; - uint32_t hpd_ctrl; + u32 hpd_ctrl; int ret; unsigned long flags; @@ -125,7 +125,7 @@ void msm_hdmi_hpd_irq(struct drm_bridge *bridge) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; - uint32_t hpd_int_status, hpd_int_ctrl; + u32 hpd_int_status, hpd_int_ctrl; /* Process HPD: */ hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c index ebefea4fb408..c4dc0fc063cb 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c @@ -40,8 +40,8 @@ static int ddc_clear_irq(struct hdmi_i2c_adapter *hdmi_i2c) { struct hdmi *hdmi = hdmi_i2c->hdmi; struct drm_device *dev = hdmi->dev; - uint32_t retry = 0xffff; - uint32_t ddc_int_ctrl; + u32 retry = 0xffff; + u32 ddc_int_ctrl; do { --retry; @@ -71,7 +71,7 @@ static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c) struct hdmi *hdmi = hdmi_i2c->hdmi; if (!hdmi_i2c->sw_done) { - uint32_t ddc_int_ctrl; + u32 ddc_int_ctrl; ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL); @@ -92,13 +92,13 @@ static int msm_hdmi_i2c_xfer(struct i2c_adapter *i2c, struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c); struct hdmi *hdmi = hdmi_i2c->hdmi; struct drm_device *dev = hdmi->dev; - static const uint32_t nack[] = { + static const u32 nack[] = { HDMI_DDC_SW_STATUS_NACK0, HDMI_DDC_SW_STATUS_NACK1, HDMI_DDC_SW_STATUS_NACK2, HDMI_DDC_SW_STATUS_NACK3, }; int indices[MAX_TRANSACTIONS]; int ret, i, j, index = 0; - uint32_t ddc_status, ddc_data, i2c_trans; + u32 ddc_status, ddc_data, i2c_trans; num = min(num, MAX_TRANSACTIONS); @@ -119,7 +119,7 @@ static int msm_hdmi_i2c_xfer(struct i2c_adapter *i2c, for (i = 0; i < num; i++) { struct i2c_msg *p = &msgs[i]; - uint32_t raw_addr = p->addr << 1; + u32 raw_addr = p->addr << 1; if (p->flags & I2C_M_RD) raw_addr |= 1; From 536d2eb2bf8778f4097959bad64cc3735500c3b3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 11 Mar 2026 21:17:29 +0100 Subject: [PATCH 257/712] drm/msm/hdmi: Drop redundant 'int' for longs 'long' type is already an integer, so 'int' is redundant. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/711175/ Link: https://lore.kernel.org/r/20260311-drm-msm-hdmi-cleanup-v1-5-c5535245f6de@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi.h | 6 +++--- drivers/gpu/drm/msm/hdmi/hdmi_phy.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 02cfd46df594..49433f7727c3 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -43,7 +43,7 @@ struct hdmi { bool power_on; bool hpd_enabled; struct mutex state_mutex; /* protects two booleans */ - unsigned long int pixclock; + unsigned long pixclock; void __iomem *mmio; void __iomem *qfprom_mmio; @@ -132,7 +132,7 @@ enum hdmi_phy_type { struct hdmi_phy_cfg { enum hdmi_phy_type type; - void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock); + void (*powerup)(struct hdmi_phy *phy, unsigned long pixclock); void (*powerdown)(struct hdmi_phy *phy); const char * const *reg_names; int num_regs; @@ -167,7 +167,7 @@ static inline u32 hdmi_phy_read(struct hdmi_phy *phy, u32 reg) int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy); void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy); -void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock); +void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long pixclock); void msm_hdmi_phy_powerdown(struct hdmi_phy *phy); void __init msm_hdmi_phy_driver_register(void); void __exit msm_hdmi_phy_driver_unregister(void); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c index f726555bb681..eb1088755cb3 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c @@ -94,7 +94,7 @@ void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy) pm_runtime_put_sync(dev); } -void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock) +void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long pixclock) { if (!phy || !phy->cfg->powerup) return; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c index cf90a0c1f822..cfa8fc494199 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c @@ -7,7 +7,7 @@ #include "hdmi.h" static void hdmi_phy_8960_powerup(struct hdmi_phy *phy, - unsigned long int pixclock) + unsigned long pixclock) { DBG("pixclock: %lu", pixclock); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c index 1d97640d8c24..10ee91818364 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c @@ -9,7 +9,7 @@ #include "hdmi.h" static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy, - unsigned long int pixclock) + unsigned long pixclock) { /* De-serializer delay D/C for non-lbk mode: */ hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG0, diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c index a2a6940e195a..6f40820d9071 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c @@ -7,7 +7,7 @@ #include "hdmi.h" static void hdmi_phy_8x74_powerup(struct hdmi_phy *phy, - unsigned long int pixclock) + unsigned long pixclock) { hdmi_phy_write(phy, REG_HDMI_8x74_ANA_CFG0, 0x1b); hdmi_phy_write(phy, REG_HDMI_8x74_ANA_CFG1, 0xf2); From b9699dd862760e642807a2bc226e4d127e35dcb7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 17 Mar 2026 17:30:05 +0200 Subject: [PATCH 258/712] drm/msm/dpu: don't try using 2 LMs if only one DSC is available Current topology code will try using 2 LMs with just one DSC, which breaks cases like SC7280 / Fairphone5. Forbid using 2 LMs split in such a case. Fixes: 1ce69c265a53 ("drm/msm/dpu: move resource allocation to CRTC") Reported-by: Luca Weiss Closes: https://lore.kernel.org/r/DH1IKLU0YZYU.2SW4WYO7H3H4R@fairphone.com/ Tested-by: Luca Weiss # qcm6490-fairphone-fp5 Patchwork: https://patchwork.freedesktop.org/patch/712386/ Link: https://lore.kernel.org/r/20260317-fix-3d-dsc-v1-1-88b54f62f659@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 97aca969337f..103cdbb38968 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1406,7 +1406,8 @@ static struct msm_display_topology dpu_crtc_get_topology( topology.num_lm = 2; else if (topology.num_dsc == 2) topology.num_lm = 2; - else if (dpu_kms->catalog->caps->has_3d_merge) + else if (dpu_kms->catalog->caps->has_3d_merge && + topology.num_dsc == 0) topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1; else topology.num_lm = 1; From 961c900628fef77ad07b4bc4c868e47b9a1269c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 18 Mar 2026 18:17:00 +0100 Subject: [PATCH 259/712] drm/msm/dpu: fix vblank IRQ registration before atomic_mode_set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dpu_encoder_toggle_vblank_for_crtc() can call control_vblank_irq() at any time in response to a userspace vblank request, independently of the atomic commit sequence. If this happens before the encoder's first atomic_mode_set(), irq[INTR_IDX_RDPTR] is still zero. Passing irq_idx=0 to dpu_core_irq_register_callback() is treated as invalid, and DPU_IRQ_REG(0) and DPU_IRQ_BIT(0) produce misleading values of 134217727 and 31 respectively due to unsigned wraparound in the (irq_idx - 1) macros, resulting in the confusing error: [dpu error]invalid IRQ=[134217727, 31] Since irq[INTR_IDX_RDPTR] will be properly populated by atomic_mode_set() and registered by irq_enable() as part of the normal modeset sequence, silently skip the vblank IRQ registration when the index has not yet been initialized. This matches the existing pattern of the master encoder check above it. Signed-off-by: Cédric Bellegarde Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/712752/ Link: https://lore.kernel.org/r/20260318171700.394945-1-cedric.bellegarde@adishatz.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c index 93db1484f606..45079ee59cf6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c @@ -257,6 +257,12 @@ static int dpu_encoder_phys_cmd_control_vblank_irq( if (!dpu_encoder_phys_cmd_is_master(phys_enc)) goto end; + /* IRQ not yet initialized */ + if (!phys_enc->irq[INTR_IDX_RDPTR]) { + ret = -EINVAL; + goto end; + } + /* protect against negative */ if (!enable && refcount == 0) { ret = -EINVAL; From 2d51cfb77daa30b10bc68c403f8ace35783d2922 Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sat, 7 Mar 2026 19:12:48 +0800 Subject: [PATCH 260/712] drm/msm/dsi: fix bits_per_pclk mipi_dsi_pixel_format_to_bpp return dst bpp not src bpp, dst bpp may not be the uncompressed data size. use src bpc * 3 to get src bpp, this aligns with pclk rate calculation. Fixes: ac47870fd795 ("drm/msm/dsi: fix hdisplay calculation when programming dsi registers") Signed-off-by: Pengyu Luo Patchwork: https://patchwork.freedesktop.org/patch/709916/ Link: https://lore.kernel.org/r/20260307111250.105772-1-mitltlatltl@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 6cb634590e7a..3efcc3f6c381 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1048,7 +1048,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) */ h_total -= hdisplay; if (wide_bus_enabled) - bits_per_pclk = mipi_dsi_pixel_format_to_bpp(msm_host->format); + bits_per_pclk = dsc->bits_per_component * 3; else bits_per_pclk = 24; From 82159db4371f5cef56444ebd0b8f96e2a6d709ff Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sat, 7 Mar 2026 19:12:49 +0800 Subject: [PATCH 261/712] drm/msm/dsi: fix hdisplay calculation for CMD mode panel Commit ac47870fd795 ("drm/msm/dsi: fix hdisplay calculation when programming dsi registers") incorrecly broke hdisplay calculation for CMD mode by specifying incorrect number of bytes per transfer, fix it. Fixes: ac47870fd795 ("drm/msm/dsi: fix hdisplay calculation when programming dsi registers") Signed-off-by: Pengyu Luo Patchwork: https://patchwork.freedesktop.org/patch/709917/ Link: https://lore.kernel.org/r/20260307111250.105772-2-mitltlatltl@gmail.com [DB: fixed commit message] Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 3efcc3f6c381..1c0841a1c101 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1034,8 +1034,9 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) /* * DPU sends 3 bytes per pclk cycle to DSI. If widebus is * enabled, MDP always sends out 48-bit compressed data per - * pclk and on average, DSI consumes an amount of compressed - * data equivalent to the uncompressed pixel depth per pclk. + * pclk and on average, for video mode, DSI consumes only an + * amount of compressed data equivalent to the uncompressed + * pixel depth per pclk. * * Calculate the number of pclks needed to transmit one line of * the compressed data. @@ -1047,10 +1048,14 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) * unused anyway. */ h_total -= hdisplay; - if (wide_bus_enabled) - bits_per_pclk = dsc->bits_per_component * 3; - else + if (wide_bus_enabled) { + if (msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) + bits_per_pclk = dsc->bits_per_component * 3; + else + bits_per_pclk = 48; + } else { bits_per_pclk = 24; + } hdisplay = DIV_ROUND_UP(msm_dsc_get_bytes_per_line(msm_host->dsc) * 8, bits_per_pclk); From cfb64b0926172b4a48db0005a868674fa6cb2d8f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Feb 2026 20:36:40 +0200 Subject: [PATCH 262/712] drm/msm/dpu: drop VBIF_NRT handling The second VBIF instance, VBIF_NRT, is only used for the separate inline rotator block. It is unsupported by the DPU driver and will require a separate driver (or separate instance of the DPU device). The only possible user of VBIF_NRT is writeback on MSM8996, however writeback on that platform is currently unsupported and it's not worth keeping extra complexity for the sake of that single legacy platform. None of the hardware catalogs entries actually declare VBIF_NRT, so it is left in its default state. Stop pretending that DPU driver cares about VBIF_NRT and drop it. Reported-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/707773/ Link: https://lore.kernel.org/r/20260227-drop-vbif-nrt-v1-1-2b97d0438182@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h | 1 - drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 14 -------------- drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c | 2 -- 3 files changed, 17 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h index 046b683d4c66..f3cb827034cc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h @@ -286,7 +286,6 @@ enum dpu_wd_timer { enum dpu_vbif { VBIF_RT, - VBIF_NRT, VBIF_MAX, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 19512042d42b..151592373551 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1357,14 +1357,6 @@ static int dpu_kms_mmap_mdp5(struct dpu_kms *dpu_kms) return ret; } - dpu_kms->vbif[VBIF_NRT] = msm_ioremap_mdss(mdss_dev, - dpu_kms->pdev, - "vbif_nrt_phys"); - if (IS_ERR(dpu_kms->vbif[VBIF_NRT])) { - dpu_kms->vbif[VBIF_NRT] = NULL; - DPU_DEBUG("VBIF NRT is not defined"); - } - return 0; } @@ -1390,12 +1382,6 @@ static int dpu_kms_mmap_dpu(struct dpu_kms *dpu_kms) return ret; } - dpu_kms->vbif[VBIF_NRT] = msm_ioremap_quiet(pdev, "vbif_nrt"); - if (IS_ERR(dpu_kms->vbif[VBIF_NRT])) { - dpu_kms->vbif[VBIF_NRT] = NULL; - DPU_DEBUG("VBIF NRT is not defined"); - } - return 0; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c index 2a551e455aa3..ede38b3c6f8c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c @@ -24,8 +24,6 @@ static const char *dpu_vbif_name(enum dpu_vbif idx) switch (idx) { case VBIF_RT: return "VBIF_RT"; - case VBIF_NRT: - return "VBIF_NRT"; default: return "??"; } From 2c0c3d9d95cac57e58f4e7171fe200a3fbd0cc82 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Feb 2026 20:36:41 +0200 Subject: [PATCH 263/712] drm/msm/dpu: stop declaring VBIFs as an array in catalog The driver handles a single VBIF instance, VBIF_RT. Stop declaring VBIFs as an array in the DPU hardware catalog. Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/707774/ Link: https://lore.kernel.org/r/20260227-drop-vbif-nrt-v1-2-2b97d0438182@oss.qualcomm.com [DB: handled Eliza platform] Signed-off-by: Dmitry Baryshkov --- .../msm/disp/dpu1/catalog/dpu_10_0_sm8650.h | 3 +- .../msm/disp/dpu1/catalog/dpu_12_0_sm8750.h | 3 +- .../msm/disp/dpu1/catalog/dpu_12_2_glymur.h | 3 +- .../msm/disp/dpu1/catalog/dpu_12_4_eliza.h | 3 +- .../disp/dpu1/catalog/dpu_13_0_kaanapali.h | 3 +- .../msm/disp/dpu1/catalog/dpu_1_14_msm8937.h | 3 +- .../msm/disp/dpu1/catalog/dpu_1_15_msm8917.h | 3 +- .../msm/disp/dpu1/catalog/dpu_1_16_msm8953.h | 3 +- .../msm/disp/dpu1/catalog/dpu_1_7_msm8996.h | 3 +- .../msm/disp/dpu1/catalog/dpu_3_0_msm8998.h | 3 +- .../msm/disp/dpu1/catalog/dpu_3_2_sdm660.h | 3 +- .../msm/disp/dpu1/catalog/dpu_3_3_sdm630.h | 3 +- .../msm/disp/dpu1/catalog/dpu_4_0_sdm845.h | 3 +- .../msm/disp/dpu1/catalog/dpu_4_1_sdm670.h | 3 +- .../msm/disp/dpu1/catalog/dpu_5_0_sm8150.h | 3 +- .../msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h | 3 +- .../msm/disp/dpu1/catalog/dpu_5_2_sm7150.h | 3 +- .../msm/disp/dpu1/catalog/dpu_5_3_sm6150.h | 3 +- .../msm/disp/dpu1/catalog/dpu_5_4_sm6125.h | 3 +- .../msm/disp/dpu1/catalog/dpu_6_0_sm8250.h | 3 +- .../msm/disp/dpu1/catalog/dpu_6_2_sc7180.h | 3 +- .../msm/disp/dpu1/catalog/dpu_6_3_sm6115.h | 3 +- .../msm/disp/dpu1/catalog/dpu_6_4_sm6350.h | 3 +- .../msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h | 3 +- .../msm/disp/dpu1/catalog/dpu_6_9_sm6375.h | 3 +- .../msm/disp/dpu1/catalog/dpu_7_0_sm8350.h | 3 +- .../msm/disp/dpu1/catalog/dpu_7_2_sc7280.h | 3 +- .../msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h | 3 +- .../msm/disp/dpu1/catalog/dpu_8_1_sm8450.h | 3 +- .../msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 3 +- .../msm/disp/dpu1/catalog/dpu_9_0_sm8550.h | 3 +- .../msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h | 3 +- .../msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 3 +- .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 20 ++--- .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 1 - drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 32 ++++---- drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c | 77 +++++++++---------- 37 files changed, 87 insertions(+), 142 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h index 56d3c38c8778..b31cb6f16f33 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h @@ -445,8 +445,7 @@ const struct dpu_mdss_cfg dpu_sm8650_cfg = { .cwb = sm8650_cwb, .intf_count = ARRAY_SIZE(sm8650_intf), .intf = sm8650_intf, - .vbif_count = ARRAY_SIZE(sm8650_vbif), - .vbif = sm8650_vbif, + .vbif = &sm8650_vbif, .perf = &sm8650_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h index db8cc2d0112c..b0c38b2e38c4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h @@ -486,8 +486,7 @@ const struct dpu_mdss_cfg dpu_sm8750_cfg = { .cwb = sm8650_cwb, .intf_count = ARRAY_SIZE(sm8750_intf), .intf = sm8750_intf, - .vbif_count = ARRAY_SIZE(sm8650_vbif), - .vbif = sm8650_vbif, + .vbif = &sm8650_vbif, .perf = &sm8750_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h index 13bb43ba67d3..f6fd79a48537 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h @@ -533,8 +533,7 @@ const struct dpu_mdss_cfg dpu_glymur_cfg = { .cwb = sm8650_cwb, .intf_count = ARRAY_SIZE(glymur_intf), .intf = glymur_intf, - .vbif_count = ARRAY_SIZE(sm8650_vbif), - .vbif = sm8650_vbif, + .vbif = &sm8650_vbif, .perf = &glymur_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h index f718a181af21..aeccf6f9095e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h @@ -357,8 +357,7 @@ const struct dpu_mdss_cfg dpu_eliza_cfg = { .cwb = sm8650_cwb, .intf_count = ARRAY_SIZE(eliza_intf), .intf = eliza_intf, - .vbif_count = ARRAY_SIZE(sm8650_vbif), - .vbif = sm8650_vbif, + .vbif = &sm8650_vbif, .perf = &eliza_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h index 0b20401b04cf..02d2de6073f8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h @@ -484,8 +484,7 @@ const struct dpu_mdss_cfg dpu_kaanapali_cfg = { .cwb = sm8650_cwb, .intf_count = ARRAY_SIZE(kaanapali_intf), .intf = kaanapali_intf, - .vbif_count = ARRAY_SIZE(sm8650_vbif), - .vbif = sm8650_vbif, + .vbif = &sm8650_vbif, .perf = &kaanapali_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h index 29e0eba91930..4ff7b397f808 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h @@ -197,8 +197,7 @@ const struct dpu_mdss_cfg dpu_msm8937_cfg = { .pingpong = msm8937_pp, .intf_count = ARRAY_SIZE(msm8937_intf), .intf = msm8937_intf, - .vbif_count = ARRAY_SIZE(msm8996_vbif), - .vbif = msm8996_vbif, + .vbif = &msm8996_vbif, .perf = &msm8937_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h index cb1ee4b63f9f..1518c3d39ce8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h @@ -176,8 +176,7 @@ const struct dpu_mdss_cfg dpu_msm8917_cfg = { .pingpong = msm8917_pp, .intf_count = ARRAY_SIZE(msm8917_intf), .intf = msm8917_intf, - .vbif_count = ARRAY_SIZE(msm8996_vbif), - .vbif = msm8996_vbif, + .vbif = &msm8996_vbif, .perf = &msm8917_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h index b44d02b48418..e8aabe43c9ff 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h @@ -204,8 +204,7 @@ const struct dpu_mdss_cfg dpu_msm8953_cfg = { .pingpong = msm8953_pp, .intf_count = ARRAY_SIZE(msm8953_intf), .intf = msm8953_intf, - .vbif_count = ARRAY_SIZE(msm8996_vbif), - .vbif = msm8996_vbif, + .vbif = &msm8996_vbif, .perf = &msm8953_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h index 8af63db315b4..67910a2f6880 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h @@ -320,8 +320,7 @@ const struct dpu_mdss_cfg dpu_msm8996_cfg = { .dsc = msm8996_dsc, .intf_count = ARRAY_SIZE(msm8996_intf), .intf = msm8996_intf, - .vbif_count = ARRAY_SIZE(msm8996_vbif), - .vbif = msm8996_vbif, + .vbif = &msm8996_vbif, .perf = &msm8996_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h index b1b03d8b30fa..67c1463d3bd6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h @@ -305,8 +305,7 @@ const struct dpu_mdss_cfg dpu_msm8998_cfg = { .dsc = msm8998_dsc, .intf_count = ARRAY_SIZE(msm8998_intf), .intf = msm8998_intf, - .vbif_count = ARRAY_SIZE(msm8998_vbif), - .vbif = msm8998_vbif, + .vbif = &msm8998_vbif, .perf = &msm8998_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h index 64df4e80ea43..84344029819f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h @@ -269,8 +269,7 @@ const struct dpu_mdss_cfg dpu_sdm660_cfg = { .dsc = sdm660_dsc, .intf_count = ARRAY_SIZE(sdm660_intf), .intf = sdm660_intf, - .vbif_count = ARRAY_SIZE(msm8998_vbif), - .vbif = msm8998_vbif, + .vbif = &msm8998_vbif, .perf = &sdm660_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h index b409af899918..ef5777aee587 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h @@ -207,8 +207,7 @@ const struct dpu_mdss_cfg dpu_sdm630_cfg = { .pingpong = sdm630_pp, .intf_count = ARRAY_SIZE(sdm630_intf), .intf = sdm630_intf, - .vbif_count = ARRAY_SIZE(msm8998_vbif), - .vbif = msm8998_vbif, + .vbif = &msm8998_vbif, .perf = &sdm630_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h index 5cc9f55d542b..019135c9a831 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h @@ -325,8 +325,7 @@ const struct dpu_mdss_cfg dpu_sdm845_cfg = { .dsc = sdm845_dsc, .intf_count = ARRAY_SIZE(sdm845_intf), .intf = sdm845_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sdm845_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h index 0f5e9babdeea..54b4a83ee16e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h @@ -144,8 +144,7 @@ const struct dpu_mdss_cfg dpu_sdm670_cfg = { .dsc = sdm670_dsc, .intf_count = ARRAY_SIZE(sdm845_intf), .intf = sdm845_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sdm845_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h index ae1b2ed96e9f..9f43ce8bf31b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h @@ -381,8 +381,7 @@ const struct dpu_mdss_cfg dpu_sm8150_cfg = { .wb = sm8150_wb, .intf_count = ARRAY_SIZE(sm8150_intf), .intf = sm8150_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm8150_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h index b572cfa7ed35..14611a344371 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h @@ -405,8 +405,7 @@ const struct dpu_mdss_cfg dpu_sc8180x_cfg = { .wb = sc8180x_wb, .intf_count = ARRAY_SIZE(sc8180x_intf), .intf = sc8180x_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sc8180x_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h index a56c288ac10c..7b97e3b8630e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h @@ -309,8 +309,7 @@ const struct dpu_mdss_cfg dpu_sm7150_cfg = { .intf = sm7150_intf, .wb_count = ARRAY_SIZE(sm7150_wb), .wb = sm7150_wb, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm7150_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h index 26883f6b66b3..65fbd006720d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h @@ -246,8 +246,7 @@ const struct dpu_mdss_cfg dpu_sm6150_cfg = { .wb = sm6150_wb, .intf_count = ARRAY_SIZE(sm6150_intf), .intf = sm6150_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm6150_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h index fbf50f279e66..c7833ca05eb4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h @@ -217,8 +217,7 @@ const struct dpu_mdss_cfg dpu_sm6125_cfg = { .wb = sm6125_wb, .intf_count = ARRAY_SIZE(sm6125_intf), .intf = sm6125_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm6125_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h index 7b8b7a1c2d76..09ca22b93e68 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h @@ -378,8 +378,7 @@ const struct dpu_mdss_cfg dpu_sm8250_cfg = { .merge_3d = sm8250_merge_3d, .intf_count = ARRAY_SIZE(sm8250_intf), .intf = sm8250_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .wb_count = ARRAY_SIZE(sm8250_wb), .wb = sm8250_wb, .perf = &sm8250_perf_data, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h index c990ba3b5db0..3adc3350f05b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h @@ -211,8 +211,7 @@ const struct dpu_mdss_cfg dpu_sc7180_cfg = { .intf = sc7180_intf, .wb_count = ARRAY_SIZE(sc7180_wb), .wb = sc7180_wb, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sc7180_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h index 343ff5482382..20a2e9ff5cc9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h @@ -144,8 +144,7 @@ const struct dpu_mdss_cfg dpu_sm6115_cfg = { .pingpong = sm6115_pp, .intf_count = ARRAY_SIZE(sm6115_intf), .intf = sm6115_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm6115_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h index 093d16bdc450..7b58e438f597 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h @@ -229,8 +229,7 @@ const struct dpu_mdss_cfg dpu_sm6350_cfg = { .wb = sm6350_wb, .intf_count = ARRAY_SIZE(sm6350_intf), .intf = sm6350_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm6350_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h index 47053bf9b0a2..7ae7530aa3b0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h @@ -137,8 +137,7 @@ const struct dpu_mdss_cfg dpu_qcm2290_cfg = { .pingpong = qcm2290_pp, .intf_count = ARRAY_SIZE(qcm2290_intf), .intf = qcm2290_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &qcm2290_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h index 98190ee7ec7a..fc7ceac859be 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h @@ -155,8 +155,7 @@ const struct dpu_mdss_cfg dpu_sm6375_cfg = { .pingpong = sm6375_pp, .intf_count = ARRAY_SIZE(sm6375_intf), .intf = sm6375_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm6375_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h index 85aae40c210f..a3fea0ade688 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h @@ -392,8 +392,7 @@ const struct dpu_mdss_cfg dpu_sm8350_cfg = { .wb = sm8350_wb, .intf_count = ARRAY_SIZE(sm8350_intf), .intf = sm8350_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm8350_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h index 2f8688224f34..ce38e93c0d7e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h @@ -263,8 +263,7 @@ const struct dpu_mdss_cfg dpu_sc7280_cfg = { .wb = sc7280_wb, .intf_count = ARRAY_SIZE(sc7280_intf), .intf = sc7280_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sc7280_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h index 9f2bceca1789..07a2c286a7f6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h @@ -421,8 +421,7 @@ const struct dpu_mdss_cfg dpu_sc8280xp_cfg = { .merge_3d = sc8280xp_merge_3d, .intf_count = ARRAY_SIZE(sc8280xp_intf), .intf = sc8280xp_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sc8280xp_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h index 04b22167f93d..0271add0f2b9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h @@ -405,8 +405,7 @@ const struct dpu_mdss_cfg dpu_sm8450_cfg = { .wb = sm8450_wb, .intf_count = ARRAY_SIZE(sm8450_intf), .intf = sm8450_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sm8450_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h index 42cf3bd5a12a..c9dff42d8ea1 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h @@ -445,8 +445,7 @@ const struct dpu_mdss_cfg dpu_sa8775p_cfg = { .wb = sa8775p_wb, .intf_count = ARRAY_SIZE(sa8775p_intf), .intf = sa8775p_intf, - .vbif_count = ARRAY_SIZE(sdm845_vbif), - .vbif = sdm845_vbif, + .vbif = &sdm845_vbif, .perf = &sa8775p_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h index 4c7eb55d474c..c0c133ffd555 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h @@ -400,8 +400,7 @@ const struct dpu_mdss_cfg dpu_sm8550_cfg = { .wb = sm8550_wb, .intf_count = ARRAY_SIZE(sm8550_intf), .intf = sm8550_intf, - .vbif_count = ARRAY_SIZE(sm8550_vbif), - .vbif = sm8550_vbif, + .vbif = &sm8550_vbif, .perf = &sm8550_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h index dec83ea8167d..4e1edf69b225 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h @@ -400,8 +400,7 @@ const struct dpu_mdss_cfg dpu_sar2130p_cfg = { .wb = sar2130p_wb, .intf_count = ARRAY_SIZE(sar2130p_intf), .intf = sar2130p_intf, - .vbif_count = ARRAY_SIZE(sm8550_vbif), - .vbif = sm8550_vbif, + .vbif = &sm8550_vbif, .perf = &sar2130p_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h index 52ff4baa668a..fce95fadefca 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h @@ -441,8 +441,7 @@ const struct dpu_mdss_cfg dpu_x1e80100_cfg = { .wb = x1e80100_wb, .intf_count = ARRAY_SIZE(x1e80100_intf), .intf = x1e80100_intf, - .vbif_count = ARRAY_SIZE(sm8550_vbif), - .vbif = sm8550_vbif, + .vbif = &sm8550_vbif, .perf = &x1e80100_perf_data, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index b096b748707e..91fed3095a12 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -513,8 +513,7 @@ static const struct dpu_vbif_dynamic_ot_cfg msm8998_ot_rdwr_cfg[] = { }, }; -static const struct dpu_vbif_cfg msm8996_vbif[] = { - { +static const struct dpu_vbif_cfg msm8996_vbif = { .name = "vbif_rt", .id = VBIF_RT, .base = 0, .len = 0x1040, .default_ot_rd_limit = 32, @@ -538,11 +537,9 @@ static const struct dpu_vbif_cfg msm8996_vbif[] = { .npriority_lvl = ARRAY_SIZE(msm8998_nrt_pri_lvl), .priority_lvl = msm8998_nrt_pri_lvl, }, - }, }; -static const struct dpu_vbif_cfg msm8998_vbif[] = { - { +static const struct dpu_vbif_cfg msm8998_vbif = { .name = "vbif_rt", .id = VBIF_RT, .base = 0, .len = 0x1040, .default_ot_rd_limit = 32, @@ -568,11 +565,9 @@ static const struct dpu_vbif_cfg msm8998_vbif[] = { }, .memtype_count = 14, .memtype = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, - }, }; -static const struct dpu_vbif_cfg sdm845_vbif[] = { - { +static const struct dpu_vbif_cfg sdm845_vbif = { .name = "vbif_rt", .id = VBIF_RT, .base = 0, .len = 0x1040, .features = BIT(DPU_VBIF_QOS_REMAP), @@ -588,11 +583,9 @@ static const struct dpu_vbif_cfg sdm845_vbif[] = { }, .memtype_count = 14, .memtype = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, - }, }; -static const struct dpu_vbif_cfg sm8550_vbif[] = { - { +static const struct dpu_vbif_cfg sm8550_vbif = { .name = "vbif_rt", .id = VBIF_RT, .base = 0, .len = 0x1040, .features = BIT(DPU_VBIF_QOS_REMAP), @@ -608,11 +601,9 @@ static const struct dpu_vbif_cfg sm8550_vbif[] = { }, .memtype_count = 16, .memtype = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, - }, }; -static const struct dpu_vbif_cfg sm8650_vbif[] = { - { +static const struct dpu_vbif_cfg sm8650_vbif = { .name = "vbif_rt", .id = VBIF_RT, .base = 0, .len = 0x1074, .features = BIT(DPU_VBIF_QOS_REMAP), @@ -628,7 +619,6 @@ static const struct dpu_vbif_cfg sm8650_vbif[] = { }, .memtype_count = 16, .memtype = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, - }, }; /************************************************************* diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index dedab08ea1d7..5a777be72fa6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -743,7 +743,6 @@ struct dpu_mdss_cfg { u32 intf_count; const struct dpu_intf_cfg *intf; - u32 vbif_count; const struct dpu_vbif_cfg *vbif; u32 wb_count; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 151592373551..0c9dc7b39710 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1059,13 +1059,11 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k dpu_kms->mmio + cat->cdm->base, "%s", cat->cdm->name); - for (i = 0; i < dpu_kms->catalog->vbif_count; i++) { - const struct dpu_vbif_cfg *vbif = &dpu_kms->catalog->vbif[i]; + const struct dpu_vbif_cfg *vbif = dpu_kms->catalog->vbif; - msm_disp_snapshot_add_block(disp_state, vbif->len, - dpu_kms->vbif[vbif->id] + vbif->base, - "%s", vbif->name); - } + msm_disp_snapshot_add_block(disp_state, vbif->len, + dpu_kms->vbif, + "vbif"); pm_runtime_put_sync(&dpu_kms->pdev->dev); } @@ -1143,7 +1141,7 @@ static int dpu_kms_hw_init(struct msm_kms *kms) { struct dpu_kms *dpu_kms; struct drm_device *dev; - int i, rc = -EINVAL; + int rc = -EINVAL; unsigned long max_core_clk_rate; u32 core_rev; @@ -1219,20 +1217,18 @@ static int dpu_kms_hw_init(struct msm_kms *kms) goto err_pm_put; } - for (i = 0; i < dpu_kms->catalog->vbif_count; i++) { - struct dpu_hw_vbif *hw; - const struct dpu_vbif_cfg *vbif = &dpu_kms->catalog->vbif[i]; + struct dpu_hw_vbif *hw; + const struct dpu_vbif_cfg *vbif = dpu_kms->catalog->vbif; - hw = dpu_hw_vbif_init(dev, vbif, dpu_kms->vbif[vbif->id]); - if (IS_ERR(hw)) { - rc = PTR_ERR(hw); - DPU_ERROR("failed to init vbif %d: %d\n", vbif->id, rc); - goto err_pm_put; - } - - dpu_kms->hw_vbif[vbif->id] = hw; + hw = dpu_hw_vbif_init(dev, vbif, dpu_kms->vbif[vbif->id]); + if (IS_ERR(hw)) { + rc = PTR_ERR(hw); + DPU_ERROR("failed to init vbif: %d\n", rc); + goto err_pm_put; } + dpu_kms->hw_vbif[vbif->id] = hw; + /* TODO: use the same max_freq as in dpu_kms_hw_init */ max_core_clk_rate = dpu_kms_get_clk_rate(dpu_kms, "core"); if (!max_core_clk_rate) { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c index ede38b3c6f8c..6ebd9627514b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c @@ -292,58 +292,51 @@ void dpu_vbif_init_memtypes(struct dpu_kms *dpu_kms) void dpu_debugfs_vbif_init(struct dpu_kms *dpu_kms, struct dentry *debugfs_root) { + const struct dpu_vbif_cfg *vbif = dpu_kms->catalog->vbif; char vbif_name[32]; - struct dentry *entry, *debugfs_vbif; - int i, j; + struct dentry *debugfs_vbif; + int j; - entry = debugfs_create_dir("vbif", debugfs_root); + debugfs_vbif = debugfs_create_dir("vbif", debugfs_root); - for (i = 0; i < dpu_kms->catalog->vbif_count; i++) { - const struct dpu_vbif_cfg *vbif = &dpu_kms->catalog->vbif[i]; + debugfs_create_u32("features", 0600, debugfs_vbif, + (u32 *)&vbif->features); - snprintf(vbif_name, sizeof(vbif_name), "%d", vbif->id); + debugfs_create_u32("xin_halt_timeout", 0400, debugfs_vbif, + (u32 *)&vbif->xin_halt_timeout); - debugfs_vbif = debugfs_create_dir(vbif_name, entry); + debugfs_create_u32("default_rd_ot_limit", 0400, debugfs_vbif, + (u32 *)&vbif->default_ot_rd_limit); - debugfs_create_u32("features", 0600, debugfs_vbif, - (u32 *)&vbif->features); + debugfs_create_u32("default_wr_ot_limit", 0400, debugfs_vbif, + (u32 *)&vbif->default_ot_wr_limit); - debugfs_create_u32("xin_halt_timeout", 0400, debugfs_vbif, - (u32 *)&vbif->xin_halt_timeout); + for (j = 0; j < vbif->dynamic_ot_rd_tbl.count; j++) { + const struct dpu_vbif_dynamic_ot_cfg *cfg = + &vbif->dynamic_ot_rd_tbl.cfg[j]; - debugfs_create_u32("default_rd_ot_limit", 0400, debugfs_vbif, - (u32 *)&vbif->default_ot_rd_limit); + snprintf(vbif_name, sizeof(vbif_name), + "dynamic_ot_rd_%d_pps", j); + debugfs_create_u64(vbif_name, 0400, debugfs_vbif, + (u64 *)&cfg->pps); + snprintf(vbif_name, sizeof(vbif_name), + "dynamic_ot_rd_%d_ot_limit", j); + debugfs_create_u32(vbif_name, 0400, debugfs_vbif, + (u32 *)&cfg->ot_limit); + } - debugfs_create_u32("default_wr_ot_limit", 0400, debugfs_vbif, - (u32 *)&vbif->default_ot_wr_limit); + for (j = 0; j < vbif->dynamic_ot_wr_tbl.count; j++) { + const struct dpu_vbif_dynamic_ot_cfg *cfg = + &vbif->dynamic_ot_wr_tbl.cfg[j]; - for (j = 0; j < vbif->dynamic_ot_rd_tbl.count; j++) { - const struct dpu_vbif_dynamic_ot_cfg *cfg = - &vbif->dynamic_ot_rd_tbl.cfg[j]; - - snprintf(vbif_name, sizeof(vbif_name), - "dynamic_ot_rd_%d_pps", j); - debugfs_create_u64(vbif_name, 0400, debugfs_vbif, - (u64 *)&cfg->pps); - snprintf(vbif_name, sizeof(vbif_name), - "dynamic_ot_rd_%d_ot_limit", j); - debugfs_create_u32(vbif_name, 0400, debugfs_vbif, - (u32 *)&cfg->ot_limit); - } - - for (j = 0; j < vbif->dynamic_ot_wr_tbl.count; j++) { - const struct dpu_vbif_dynamic_ot_cfg *cfg = - &vbif->dynamic_ot_wr_tbl.cfg[j]; - - snprintf(vbif_name, sizeof(vbif_name), - "dynamic_ot_wr_%d_pps", j); - debugfs_create_u64(vbif_name, 0400, debugfs_vbif, - (u64 *)&cfg->pps); - snprintf(vbif_name, sizeof(vbif_name), - "dynamic_ot_wr_%d_ot_limit", j); - debugfs_create_u32(vbif_name, 0400, debugfs_vbif, - (u32 *)&cfg->ot_limit); - } + snprintf(vbif_name, sizeof(vbif_name), + "dynamic_ot_wr_%d_pps", j); + debugfs_create_u64(vbif_name, 0400, debugfs_vbif, + (u64 *)&cfg->pps); + snprintf(vbif_name, sizeof(vbif_name), + "dynamic_ot_wr_%d_ot_limit", j); + debugfs_create_u32(vbif_name, 0400, debugfs_vbif, + (u32 *)&cfg->ot_limit); } } #endif From 014390e30a7456c9bbdf677427d2a8206ea46f62 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Feb 2026 20:36:42 +0200 Subject: [PATCH 264/712] drm/msm/dpu: replace VBIF-related array with bare pointers As we no longer have multiple VBIF instances, it doesn't make sense to keep VBIF data as arrays. Drop the extra wrapping and keep only a single instance of each of the structures. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/707776/ Link: https://lore.kernel.org/r/20260227-drop-vbif-nrt-v1-3-2b97d0438182@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h | 1 - drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 28 ++++++-------- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 4 +- drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c | 41 +++++++-------------- 4 files changed, 27 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h index f3cb827034cc..a169628eb512 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h @@ -286,7 +286,6 @@ enum dpu_wd_timer { enum dpu_vbif { VBIF_RT, - VBIF_MAX, }; /** diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 0c9dc7b39710..3a923bf6d2b2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -886,16 +886,12 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms) static void _dpu_kms_hw_destroy(struct dpu_kms *dpu_kms) { - int i; - dpu_kms->hw_intr = NULL; /* safe to call these more than once during shutdown */ _dpu_kms_mmu_destroy(dpu_kms); - for (i = 0; i < ARRAY_SIZE(dpu_kms->hw_vbif); i++) { - dpu_kms->hw_vbif[i] = NULL; - } + dpu_kms->hw_vbif = NULL; dpu_kms_global_obj_fini(dpu_kms); @@ -1220,14 +1216,14 @@ static int dpu_kms_hw_init(struct msm_kms *kms) struct dpu_hw_vbif *hw; const struct dpu_vbif_cfg *vbif = dpu_kms->catalog->vbif; - hw = dpu_hw_vbif_init(dev, vbif, dpu_kms->vbif[vbif->id]); + hw = dpu_hw_vbif_init(dev, vbif, dpu_kms->vbif); if (IS_ERR(hw)) { rc = PTR_ERR(hw); DPU_ERROR("failed to init vbif: %d\n", rc); goto err_pm_put; } - dpu_kms->hw_vbif[vbif->id] = hw; + dpu_kms->hw_vbif = hw; /* TODO: use the same max_freq as in dpu_kms_hw_init */ max_core_clk_rate = dpu_kms_get_clk_rate(dpu_kms, "core"); @@ -1343,13 +1339,11 @@ static int dpu_kms_mmap_mdp5(struct dpu_kms *dpu_kms) } DRM_DEBUG("mapped dpu address space @%p\n", dpu_kms->mmio); - dpu_kms->vbif[VBIF_RT] = msm_ioremap_mdss(mdss_dev, - dpu_kms->pdev, - "vbif_phys"); - if (IS_ERR(dpu_kms->vbif[VBIF_RT])) { - ret = PTR_ERR(dpu_kms->vbif[VBIF_RT]); + dpu_kms->vbif = msm_ioremap_mdss(mdss_dev, dpu_kms->pdev, "vbif_phys"); + if (IS_ERR(dpu_kms->vbif)) { + ret = PTR_ERR(dpu_kms->vbif); DPU_ERROR("vbif register memory map failed: %d\n", ret); - dpu_kms->vbif[VBIF_RT] = NULL; + dpu_kms->vbif = NULL; return ret; } @@ -1370,11 +1364,11 @@ static int dpu_kms_mmap_dpu(struct dpu_kms *dpu_kms) } DRM_DEBUG("mapped dpu address space @%p\n", dpu_kms->mmio); - dpu_kms->vbif[VBIF_RT] = msm_ioremap(pdev, "vbif"); - if (IS_ERR(dpu_kms->vbif[VBIF_RT])) { - ret = PTR_ERR(dpu_kms->vbif[VBIF_RT]); + dpu_kms->vbif = msm_ioremap(pdev, "vbif"); + if (IS_ERR(dpu_kms->vbif)) { + ret = PTR_ERR(dpu_kms->vbif); DPU_ERROR("vbif register memory map failed: %d\n", ret); - dpu_kms->vbif[VBIF_RT] = NULL; + dpu_kms->vbif = NULL; return ret; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h index 993cf512f8c5..bb3393bd102e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h @@ -63,7 +63,7 @@ struct dpu_kms { const struct qcom_ubwc_cfg_data *mdss; /* io/register spaces: */ - void __iomem *mmio, *vbif[VBIF_MAX]; + void __iomem *mmio, *vbif; struct regulator *vdd; struct regulator *mmagic; @@ -81,7 +81,7 @@ struct dpu_kms { struct dpu_rm rm; - struct dpu_hw_vbif *hw_vbif[VBIF_MAX]; + struct dpu_hw_vbif *hw_vbif; struct dpu_hw_mdp *hw_mdp; bool has_danger_ctrl; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c index 6ebd9627514b..a4c5ca13179b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c @@ -11,14 +11,6 @@ #include "dpu_hw_vbif.h" #include "dpu_trace.h" -static struct dpu_hw_vbif *dpu_get_vbif(struct dpu_kms *dpu_kms, enum dpu_vbif vbif_idx) -{ - if (vbif_idx < ARRAY_SIZE(dpu_kms->hw_vbif)) - return dpu_kms->hw_vbif[vbif_idx]; - - return NULL; -} - static const char *dpu_vbif_name(enum dpu_vbif idx) { switch (idx) { @@ -170,7 +162,7 @@ void dpu_vbif_set_ot_limit(struct dpu_kms *dpu_kms, u32 ot_lim; int ret; - vbif = dpu_get_vbif(dpu_kms, params->vbif_idx); + vbif = dpu_kms->hw_vbif; if (!vbif) { DRM_DEBUG_ATOMIC("invalid arguments vbif %d\n", vbif != NULL); return; @@ -219,7 +211,7 @@ void dpu_vbif_set_qos_remap(struct dpu_kms *dpu_kms, return; } - vbif = dpu_get_vbif(dpu_kms, params->vbif_idx); + vbif = dpu_kms->hw_vbif; if (!vbif || !vbif->cap) { DPU_ERROR("invalid vbif %d\n", params->vbif_idx); @@ -255,16 +247,14 @@ void dpu_vbif_set_qos_remap(struct dpu_kms *dpu_kms, void dpu_vbif_clear_errors(struct dpu_kms *dpu_kms) { struct dpu_hw_vbif *vbif; - u32 i, pnd, src; + u32 pnd, src; - for (i = 0; i < ARRAY_SIZE(dpu_kms->hw_vbif); i++) { - vbif = dpu_kms->hw_vbif[i]; - if (vbif && vbif->ops.clear_errors) { - vbif->ops.clear_errors(vbif, &pnd, &src); - if (pnd || src) { - DRM_DEBUG_KMS("%s: pnd 0x%X, src 0x%X\n", - dpu_vbif_name(vbif->idx), pnd, src); - } + vbif = dpu_kms->hw_vbif; + if (vbif && vbif->ops.clear_errors) { + vbif->ops.clear_errors(vbif, &pnd, &src); + if (pnd || src) { + DRM_DEBUG_KMS("%s: pnd 0x%X, src 0x%X\n", + dpu_vbif_name(vbif->idx), pnd, src); } } } @@ -276,15 +266,12 @@ void dpu_vbif_clear_errors(struct dpu_kms *dpu_kms) void dpu_vbif_init_memtypes(struct dpu_kms *dpu_kms) { struct dpu_hw_vbif *vbif; - int i, j; + int j; - for (i = 0; i < ARRAY_SIZE(dpu_kms->hw_vbif); i++) { - vbif = dpu_kms->hw_vbif[i]; - if (vbif && vbif->cap && vbif->ops.set_mem_type) { - for (j = 0; j < vbif->cap->memtype_count; j++) - vbif->ops.set_mem_type( - vbif, j, vbif->cap->memtype[j]); - } + vbif = dpu_kms->hw_vbif; + if (vbif && vbif->cap && vbif->ops.set_mem_type) { + for (j = 0; j < vbif->cap->memtype_count; j++) + vbif->ops.set_mem_type(vbif, j, vbif->cap->memtype[j]); } } From b26bfb5bf1aaba9d1a8e8b729ec6ef352f47441e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Feb 2026 20:36:43 +0200 Subject: [PATCH 265/712] drm/msm/dpu: drop VBIF id, base and name from the catalog For all the platforms VBIF id is VBIF_RT, the name and base are also fixed. Drop those fields from the catalog. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/707777/ Link: https://lore.kernel.org/r/20260227-drop-vbif-nrt-v1-4-2b97d0438182@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 15 +++++---------- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 5 ++--- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c | 4 ++-- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index 91fed3095a12..bb4fd5fa4b22 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -514,8 +514,7 @@ static const struct dpu_vbif_dynamic_ot_cfg msm8998_ot_rdwr_cfg[] = { }; static const struct dpu_vbif_cfg msm8996_vbif = { - .name = "vbif_rt", .id = VBIF_RT, - .base = 0, .len = 0x1040, + .len = 0x1040, .default_ot_rd_limit = 32, .default_ot_wr_limit = 16, .features = BIT(DPU_VBIF_QOS_REMAP) | BIT(DPU_VBIF_QOS_OTLIM), @@ -540,8 +539,7 @@ static const struct dpu_vbif_cfg msm8996_vbif = { }; static const struct dpu_vbif_cfg msm8998_vbif = { - .name = "vbif_rt", .id = VBIF_RT, - .base = 0, .len = 0x1040, + .len = 0x1040, .default_ot_rd_limit = 32, .default_ot_wr_limit = 32, .features = BIT(DPU_VBIF_QOS_REMAP) | BIT(DPU_VBIF_QOS_OTLIM), @@ -568,8 +566,7 @@ static const struct dpu_vbif_cfg msm8998_vbif = { }; static const struct dpu_vbif_cfg sdm845_vbif = { - .name = "vbif_rt", .id = VBIF_RT, - .base = 0, .len = 0x1040, + .len = 0x1040, .features = BIT(DPU_VBIF_QOS_REMAP), .xin_halt_timeout = 0x4000, .qos_rp_remap_size = 0x40, @@ -586,8 +583,7 @@ static const struct dpu_vbif_cfg sdm845_vbif = { }; static const struct dpu_vbif_cfg sm8550_vbif = { - .name = "vbif_rt", .id = VBIF_RT, - .base = 0, .len = 0x1040, + .len = 0x1040, .features = BIT(DPU_VBIF_QOS_REMAP), .xin_halt_timeout = 0x4000, .qos_rp_remap_size = 0x40, @@ -604,8 +600,7 @@ static const struct dpu_vbif_cfg sm8550_vbif = { }; static const struct dpu_vbif_cfg sm8650_vbif = { - .name = "vbif_rt", .id = VBIF_RT, - .base = 0, .len = 0x1074, + .len = 0x1074, .features = BIT(DPU_VBIF_QOS_REMAP), .xin_halt_timeout = 0x4000, .qos_rp_remap_size = 0x40, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index 5a777be72fa6..c43ee4016db4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -587,8 +587,7 @@ struct dpu_vbif_qos_tbl { /** * struct dpu_vbif_cfg - information of VBIF blocks - * @id enum identifying this block - * @base register offset of this block + * @len: length of hardware block * @features bit mask identifying sub-blocks/features * @ot_rd_limit default OT read limit * @ot_wr_limit default OT write limit @@ -602,7 +601,7 @@ struct dpu_vbif_qos_tbl { * @memtype array of xin memtype definitions */ struct dpu_vbif_cfg { - DPU_HW_BLK_INFO; + u32 len; unsigned long features; u32 default_ot_rd_limit; u32 default_ot_wr_limit; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c index af76ad8a8103..de70d6b00972 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c @@ -230,13 +230,13 @@ struct dpu_hw_vbif *dpu_hw_vbif_init(struct drm_device *dev, if (!c) return ERR_PTR(-ENOMEM); - c->hw.blk_addr = addr + cfg->base; + c->hw.blk_addr = addr; c->hw.log_mask = DPU_DBG_MASK_VBIF; /* * Assign ops */ - c->idx = cfg->id; + c->idx = VBIF_RT; c->cap = cfg; _setup_vbif_ops(&c->ops, c->cap->features); From 021fd8ca0cdcba3bf70601e04ea5aad22d1968b9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Feb 2026 20:36:44 +0200 Subject: [PATCH 266/712] drm/msm/dpu: drop vbif_idx from WB configuration All MDP / DPU implementations except for MSM8996 use VBIF_RT (or the only VBIF) for WB2. Writeback on MSM8996 is not supported (nor planned to be supported). In order to simplify the driver, drop the field form the struct dpu_wb_cfg. Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/707778/ Link: https://lore.kernel.org/r/20260227-drop-vbif-nrt-v1-5-2b97d0438182@oss.qualcomm.com [DB: also handled Eliza platform] Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 1 - drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 6 ++++-- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 2 -- 22 files changed, 4 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h index b31cb6f16f33..db79f9382f8b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h @@ -322,7 +322,6 @@ static const struct dpu_wb_cfg sm8650_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h index b0c38b2e38c4..59caa2c2a87c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h @@ -364,7 +364,6 @@ static const struct dpu_wb_cfg sm8750_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h index f6fd79a48537..5e24309b4674 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h @@ -371,7 +371,6 @@ static const struct dpu_wb_cfg glymur_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h index aeccf6f9095e..b482a7e4e6c0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h @@ -235,7 +235,6 @@ static const struct dpu_wb_cfg eliza_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h index 02d2de6073f8..bf1940d9c9e9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h @@ -362,7 +362,6 @@ static const struct dpu_wb_cfg kaanapali_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h index 9f43ce8bf31b..e61e14572aff 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h @@ -280,7 +280,6 @@ static const struct dpu_wb_cfg sm8150_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h index 14611a344371..fb18de029e80 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h @@ -286,7 +286,6 @@ static const struct dpu_wb_cfg sc8180x_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h index 7b97e3b8630e..ffb89a03cfad 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h @@ -246,7 +246,6 @@ static const struct dpu_wb_cfg sm7150_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h index 65fbd006720d..427ecd4cbf63 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h @@ -158,7 +158,6 @@ static const struct dpu_wb_cfg sm6150_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 2160, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h index c7833ca05eb4..64be51e30159 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h @@ -137,7 +137,6 @@ static const struct dpu_wb_cfg sm6125_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 2160, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h index 09ca22b93e68..c481e964fca0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h @@ -317,7 +317,6 @@ static const struct dpu_wb_cfg sm8250_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h index 3adc3350f05b..d6f7ee24ca93 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h @@ -153,7 +153,6 @@ static const struct dpu_wb_cfg sc7180_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h index 7b58e438f597..dd891703e35f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h @@ -147,7 +147,6 @@ static const struct dpu_wb_cfg sm6350_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 1920, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h index a3fea0ade688..9afdfdb3be6f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h @@ -290,7 +290,6 @@ static const struct dpu_wb_cfg sm8350_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h index ce38e93c0d7e..99b8a890fddc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h @@ -172,7 +172,6 @@ static const struct dpu_wb_cfg sc7280_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h index 0271add0f2b9..bdab0ebfe102 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h @@ -303,7 +303,6 @@ static const struct dpu_wb_cfg sm8450_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h index c9dff42d8ea1..f3d85d173c56 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h @@ -310,7 +310,6 @@ static const struct dpu_wb_cfg sa8775p_wb[] = { .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h index c0c133ffd555..5837e252f5d2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h @@ -298,7 +298,6 @@ static const struct dpu_wb_cfg sm8550_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h index 4e1edf69b225..9cc0b7ea3a30 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h @@ -298,7 +298,6 @@ static const struct dpu_wb_cfg sar2130p_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h index fce95fadefca..10443368f682 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h @@ -298,7 +298,6 @@ static const struct dpu_wb_cfg x1e80100_wb[] = { .format_list = wb2_formats_rgb_yuv, .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, - .vbif_idx = VBIF_RT, .maxlinewidth = 4096, .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c index 6d28f2281c76..73021aaa8d3f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -70,7 +70,8 @@ static void dpu_encoder_phys_wb_set_ot_limit( ot_params.height = phys_enc->cached_mode.vdisplay; ot_params.is_wfd = !dpu_encoder_helper_get_cwb_mask(phys_enc); ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode); - ot_params.vbif_idx = hw_wb->caps->vbif_idx; + /* XXX: WB on MSM8996 should use VBIF_NRT */ + ot_params.vbif_idx = VBIF_RT; ot_params.rd = false; if (!_dpu_encoder_phys_wb_clk_force_ctrl(hw_wb, phys_enc->dpu_kms->hw_mdp, @@ -108,7 +109,8 @@ static void dpu_encoder_phys_wb_set_qos_remap( hw_wb = phys_enc->hw_wb; memset(&qos_params, 0, sizeof(qos_params)); - qos_params.vbif_idx = hw_wb->caps->vbif_idx; + /* XXX: WB on MSM8996 should use VBIF_NRT */ + qos_params.vbif_idx = VBIF_RT; qos_params.xin_id = hw_wb->caps->xin_id; qos_params.num = hw_wb->idx - WB_0; qos_params.is_rt = dpu_encoder_helper_get_cwb_mask(phys_enc); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index c43ee4016db4..ba04ac24d5a9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -524,7 +524,6 @@ struct dpu_intf_cfg { /** * struct dpu_wb_cfg - information of writeback blocks * @DPU_HW_BLK_INFO: refer to the description above for DPU_HW_BLK_INFO - * @vbif_idx: vbif client index * @maxlinewidth: max line width supported by writeback block * @xin_id: bus client identifier * @intr_wb_done: interrupt index for WB_DONE @@ -535,7 +534,6 @@ struct dpu_intf_cfg { struct dpu_wb_cfg { DPU_HW_BLK_INFO; unsigned long features; - u8 vbif_idx; u32 maxlinewidth; u32 xin_id; unsigned int intr_wb_done; From 1ce61688875da281ab109bb143ec6ba756493c39 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Feb 2026 20:36:45 +0200 Subject: [PATCH 267/712] drm/msm/dpu: drop VBIF index from the VBIF params Since we don't support and don't use VBIF_NRT, VBIF_RT is the only possible VBIF type. To simplify the driver, drop vbif_idx from the VBIF parameter structures. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/707783/ Link: https://lore.kernel.org/r/20260227-drop-vbif-nrt-v1-6-2b97d0438182@oss.qualcomm.com --- .../drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 5 +---- drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 5 +---- drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h | 19 +++++++------------ drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c | 11 +++++------ drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.h | 4 ---- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c index 73021aaa8d3f..22433bfbea1e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -71,7 +71,6 @@ static void dpu_encoder_phys_wb_set_ot_limit( ot_params.is_wfd = !dpu_encoder_helper_get_cwb_mask(phys_enc); ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode); /* XXX: WB on MSM8996 should use VBIF_NRT */ - ot_params.vbif_idx = VBIF_RT; ot_params.rd = false; if (!_dpu_encoder_phys_wb_clk_force_ctrl(hw_wb, phys_enc->dpu_kms->hw_mdp, @@ -110,14 +109,12 @@ static void dpu_encoder_phys_wb_set_qos_remap( memset(&qos_params, 0, sizeof(qos_params)); /* XXX: WB on MSM8996 should use VBIF_NRT */ - qos_params.vbif_idx = VBIF_RT; qos_params.xin_id = hw_wb->caps->xin_id; qos_params.num = hw_wb->idx - WB_0; qos_params.is_rt = dpu_encoder_helper_get_cwb_mask(phys_enc); - DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n", + DPU_DEBUG("[qos_remap] wb:%d xin:%d is_rt:%d\n", qos_params.num, - qos_params.vbif_idx, qos_params.xin_id, qos_params.is_rt); if (!_dpu_encoder_phys_wb_clk_force_ctrl(hw_wb, phys_enc->dpu_kms->hw_mdp, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index 9b7a8b46bfa9..6ec2e3026449 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -374,7 +374,6 @@ static void _dpu_plane_set_ot_limit(struct drm_plane *plane, ot_params.height = drm_rect_height(&pipe_cfg->src_rect); ot_params.is_wfd = !pdpu->is_rt_pipe; ot_params.frame_rate = frame_rate; - ot_params.vbif_idx = VBIF_RT; ot_params.rd = true; if (!_dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp, @@ -402,14 +401,12 @@ static void _dpu_plane_set_qos_remap(struct drm_plane *plane, bool forced_on = false; memset(&qos_params, 0, sizeof(qos_params)); - qos_params.vbif_idx = VBIF_RT; qos_params.xin_id = pipe->sspp->cap->xin_id; qos_params.num = pipe->sspp->idx - SSPP_VIG0; qos_params.is_rt = pdpu->is_rt_pipe; - DPU_DEBUG_PLANE(pdpu, "pipe:%d vbif:%d xin:%d rt:%d\n", + DPU_DEBUG_PLANE(pdpu, "pipe:%d xin:%d rt:%d\n", qos_params.num, - qos_params.vbif_idx, qos_params.xin_id, qos_params.is_rt); if (!_dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h index cb24ad2a6d8d..805d117493da 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h @@ -72,23 +72,20 @@ TRACE_EVENT(dpu_perf_set_danger_luts, ); TRACE_EVENT(dpu_perf_set_ot, - TP_PROTO(u32 pnum, u32 xin_id, u32 rd_lim, u32 vbif_idx), - TP_ARGS(pnum, xin_id, rd_lim, vbif_idx), + TP_PROTO(u32 pnum, u32 xin_id, u32 rd_lim), + TP_ARGS(pnum, xin_id, rd_lim), TP_STRUCT__entry( __field(u32, pnum) __field(u32, xin_id) __field(u32, rd_lim) - __field(u32, vbif_idx) ), TP_fast_assign( __entry->pnum = pnum; __entry->xin_id = xin_id; __entry->rd_lim = rd_lim; - __entry->vbif_idx = vbif_idx; ), - TP_printk("pnum:%d xin_id:%d ot:%d vbif:%d", - __entry->pnum, __entry->xin_id, __entry->rd_lim, - __entry->vbif_idx) + TP_printk("pnum:%d xin_id:%d ot:%d", + __entry->pnum, __entry->xin_id, __entry->rd_lim) ) TRACE_EVENT(dpu_cmd_release_bw, @@ -861,17 +858,15 @@ TRACE_EVENT(dpu_rm_reserve_lms, ); TRACE_EVENT(dpu_vbif_wait_xin_halt_fail, - TP_PROTO(enum dpu_vbif index, u32 xin_id), - TP_ARGS(index, xin_id), + TP_PROTO(u32 xin_id), + TP_ARGS(xin_id), TP_STRUCT__entry( - __field( enum dpu_vbif, index ) __field( u32, xin_id ) ), TP_fast_assign( - __entry->index = index; __entry->xin_id = xin_id; ), - TP_printk("index:%d xin_id:%u", __entry->index, __entry->xin_id) + TP_printk("xin_id:%u", __entry->xin_id) ); TRACE_EVENT(dpu_pp_connect_ext_te, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c index a4c5ca13179b..d33231f1d50b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c @@ -180,8 +180,7 @@ void dpu_vbif_set_ot_limit(struct dpu_kms *dpu_kms, if (ot_lim == 0) return; - trace_dpu_perf_set_ot(params->num, params->xin_id, ot_lim, - params->vbif_idx); + trace_dpu_perf_set_ot(params->num, params->xin_id, ot_lim); vbif->ops.set_limit_conf(vbif, params->xin_id, params->rd, ot_lim); @@ -189,7 +188,7 @@ void dpu_vbif_set_ot_limit(struct dpu_kms *dpu_kms, ret = _dpu_vbif_wait_for_xin_halt(vbif, params->xin_id); if (ret) - trace_dpu_vbif_wait_xin_halt_fail(vbif->idx, params->xin_id); + trace_dpu_vbif_wait_xin_halt_fail(params->xin_id); vbif->ops.set_halt_ctrl(vbif, params->xin_id, false); } @@ -214,7 +213,7 @@ void dpu_vbif_set_qos_remap(struct dpu_kms *dpu_kms, vbif = dpu_kms->hw_vbif; if (!vbif || !vbif->cap) { - DPU_ERROR("invalid vbif %d\n", params->vbif_idx); + DPU_ERROR("invalid vbif\n"); return; } @@ -232,8 +231,8 @@ void dpu_vbif_set_qos_remap(struct dpu_kms *dpu_kms, } for (i = 0; i < qos_tbl->npriority_lvl; i++) { - DRM_DEBUG_ATOMIC("%s xin:%d lvl:%d/%d\n", - dpu_vbif_name(params->vbif_idx), params->xin_id, i, + DRM_DEBUG_ATOMIC("VBIF xin:%d lvl:%d/%d\n", + params->xin_id, i, qos_tbl->priority_lvl[i]); vbif->ops.set_qos_remap(vbif, params->xin_id, i, qos_tbl->priority_lvl[i]); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.h index 62e47ae1e3ee..f47a89cb34ea 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.h @@ -15,24 +15,20 @@ struct dpu_vbif_set_ot_params { u32 frame_rate; bool rd; bool is_wfd; - u32 vbif_idx; }; struct dpu_vbif_set_memtype_params { u32 xin_id; - u32 vbif_idx; bool is_cacheable; }; /** * struct dpu_vbif_set_qos_params - QoS remapper parameter - * @vbif_idx: vbif identifier * @xin_id: client interface identifier * @num: pipe identifier (debug only) * @is_rt: true if pipe is used in real-time use case */ struct dpu_vbif_set_qos_params { - u32 vbif_idx; u32 xin_id; u32 num; bool is_rt; From 7c5166fd79fcee30b29edc90a504d4a387f256ff Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Feb 2026 20:36:46 +0200 Subject: [PATCH 268/712] drm/msm/dpu: drop VBIF index from the struct dpu_hw_vbif Since we don't support and don't use VBIF_NRT, VBIF_RT is the only possible VBIF type. To simplify the driver, drop vbif_idx from the VBIF instance structure. As the last users of VBIF_RT and enum dpu_vbif are gone, drop them too. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/707779/ Link: https://lore.kernel.org/r/20260227-drop-vbif-nrt-v1-7-2b97d0438182@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h | 4 --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c | 1 - drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.h | 1 - drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c | 30 ++++++--------------- 4 files changed, 8 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h index a169628eb512..0e65bf5ddc4a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h @@ -284,10 +284,6 @@ enum dpu_wd_timer { WD_TIMER_MAX }; -enum dpu_vbif { - VBIF_RT, -}; - /** * enum dpu_3d_blend_mode * Desribes how the 3d data is blended diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c index de70d6b00972..112df3f31e2b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c @@ -236,7 +236,6 @@ struct dpu_hw_vbif *dpu_hw_vbif_init(struct drm_device *dev, /* * Assign ops */ - c->idx = VBIF_RT; c->cap = cfg; _setup_vbif_ops(&c->ops, c->cap->features); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.h index 9ac49448e432..96ec4e35e549 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.h @@ -98,7 +98,6 @@ struct dpu_hw_vbif { struct dpu_hw_blk_reg_map hw; /* vbif */ - enum dpu_vbif idx; const struct dpu_vbif_cfg *cap; /* ops */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c index d33231f1d50b..0c6fa9bb0cb6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c @@ -11,16 +11,6 @@ #include "dpu_hw_vbif.h" #include "dpu_trace.h" -static const char *dpu_vbif_name(enum dpu_vbif idx) -{ - switch (idx) { - case VBIF_RT: - return "VBIF_RT"; - default: - return "??"; - } -} - /** * _dpu_vbif_wait_for_xin_halt - wait for the xin to halt * @vbif: Pointer to hardware vbif driver @@ -52,12 +42,10 @@ static int _dpu_vbif_wait_for_xin_halt(struct dpu_hw_vbif *vbif, u32 xin_id) if (!status) { rc = -ETIMEDOUT; - DPU_ERROR("%s client %d not halting. TIMEDOUT.\n", - dpu_vbif_name(vbif->idx), xin_id); + DPU_ERROR("VBIF client %d not halting. TIMEDOUT.\n", xin_id); } else { rc = 0; - DRM_DEBUG_ATOMIC("%s client %d is halted\n", - dpu_vbif_name(vbif->idx), xin_id); + DRM_DEBUG_ATOMIC("VBIF client %d is halted\n", xin_id); } return rc; @@ -97,10 +85,10 @@ static void _dpu_vbif_apply_dynamic_ot_limit(struct dpu_hw_vbif *vbif, } } - DRM_DEBUG_ATOMIC("%s xin:%d w:%d h:%d fps:%d pps:%llu ot:%u\n", - dpu_vbif_name(vbif->idx), params->xin_id, - params->width, params->height, params->frame_rate, - pps, *ot_lim); + DRM_DEBUG_ATOMIC("VBIF xin:%d w:%d h:%d fps:%d pps:%llu ot:%u\n", + params->xin_id, + params->width, params->height, params->frame_rate, + pps, *ot_lim); } /** @@ -143,8 +131,7 @@ static u32 _dpu_vbif_get_ot_limit(struct dpu_hw_vbif *vbif, } exit: - DRM_DEBUG_ATOMIC("%s xin:%d ot_lim:%d\n", - dpu_vbif_name(vbif->idx), params->xin_id, ot_lim); + DRM_DEBUG_ATOMIC("VBIF xin:%d ot_lim:%d\n", params->xin_id, ot_lim); return ot_lim; } @@ -252,8 +239,7 @@ void dpu_vbif_clear_errors(struct dpu_kms *dpu_kms) if (vbif && vbif->ops.clear_errors) { vbif->ops.clear_errors(vbif, &pnd, &src); if (pnd || src) { - DRM_DEBUG_KMS("%s: pnd 0x%X, src 0x%X\n", - dpu_vbif_name(vbif->idx), pnd, src); + DRM_DEBUG_KMS("VBIF: pnd 0x%X, src 0x%X\n", pnd, src); } } } From 3bd3d4999f6fbb53631d268af3cdf837601bb77c Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Sun, 22 Mar 2026 18:48:09 +0000 Subject: [PATCH 269/712] drm/msm/dpu: calculate data_width like downstream Derive INTF data_width from dce_bytes_per_line rather than timing->width when DSC is enabled. Use DIV_ROUND_UP to avoid rounding errors. Signed-off-by: Alexander Koskovich Reviewed-by: Dmitry Baryshkov Tested-by: Pengyu Luo # Lenovo Legion Y700 Gen4 (SM8750) Patchwork: https://patchwork.freedesktop.org/patch/713333/ Link: https://lore.kernel.org/r/20260322-fix-data-width-calc-v2-1-d5f28136bc4e@pm.me Signed-off-by: Dmitry Baryshkov --- .../drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 2 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 26 +++++++++++++++---- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index 0ba777bda253..ba810f26ea30 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -10,6 +10,7 @@ #include "dpu_formats.h" #include "dpu_trace.h" #include "disp/msm_disp_snapshot.h" +#include "msm_dsc_helper.h" #include #include @@ -136,6 +137,7 @@ static void drm_mode_to_intf_timing_params( timing->width = timing->width * drm_dsc_get_bpp_int(dsc) / (dsc->bits_per_component * 3); timing->xres = timing->width; + timing->dce_bytes_per_line = msm_dsc_get_bytes_per_line(dsc); } } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 7e620f590984..ac82b69aedf6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -173,13 +173,29 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *intf, data_width = p->width; /* - * If widebus is enabled, data is valid for only half the active window - * since the data rate is doubled in this mode. But for the compression - * mode in DP case, the p->width is already adjusted in - * drm_mode_to_intf_timing_params() + * If widebus is disabled: + * For uncompressed stream, the data is valid for the entire active + * window period. + * For compressed stream, data is valid for a shorter time period + * inside the active window depending on the compression ratio. + * + * If widebus is enabled: + * For uncompressed stream, data is valid for only half the active + * window, since the data rate is doubled in this mode. + * For compressed stream, data validity window needs to be adjusted for + * compression ratio and then further halved. + * + * For the compression mode in DP case, the p->width is already + * adjusted in drm_mode_to_intf_timing_params(). */ - if (p->wide_bus_en && !dp_intf) + if (p->compression_en && !dp_intf) { + if (p->wide_bus_en) + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 6); + else + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3); + } else if (p->wide_bus_en && !dp_intf) { data_width = p->width >> 1; + } /* TODO: handle DSC+DP case, we only handle DSC+DSI case so far */ if (p->compression_en && !dp_intf && diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h index f6ef2c21b66d..badd26305fc9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h @@ -35,6 +35,7 @@ struct dpu_hw_intf_timing_params { bool wide_bus_en; bool compression_en; + u32 dce_bytes_per_line; }; struct dpu_hw_intf_prog_fetch { From febcd37fec129316eb96dafb0384e57d910f9508 Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Thu, 12 Mar 2026 16:28:10 +0800 Subject: [PATCH 270/712] drm/msm/dpu: Extract plane splitting into a dedicated function dpu_plane_atomic_check_nosspp() currently handles both plane validation and plane splitting. For better simplicity and to facilitate future refactoring, move the splitting logic into its own dedicated function. Reviewed-by: Dmitry Baryshkov Signed-off-by: Jun Nie Patchwork: https://patchwork.freedesktop.org/patch/711319/ Link: https://lore.kernel.org/r/20260312-msm-next-quad-pipe-split-v19-1-4ffa2b06c996@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 48 ++++++++++++++++------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index 6ec2e3026449..f34a8104e9c2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -818,13 +818,8 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane, { int i, ret = 0, min_scale, max_scale; struct dpu_plane *pdpu = to_dpu_plane(plane); - struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base); - u64 max_mdp_clk_rate = kms->perf.max_core_clk_rate; struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); - struct dpu_sw_pipe_cfg *pipe_cfg; - struct dpu_sw_pipe_cfg *r_pipe_cfg; struct drm_rect fb_rect = { 0 }; - uint32_t max_linewidth; min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO); max_scale = MAX_DOWNSCALE_RATIO << 16; @@ -847,14 +842,6 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane, return -EINVAL; } - /* move the assignment here, to ease handling to another pairs later */ - pipe_cfg = &pstate->pipe_cfg[0]; - r_pipe_cfg = &pstate->pipe_cfg[1]; - /* state->src is 16.16, src_rect is not */ - drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src); - - pipe_cfg->dst_rect = new_plane_state->dst; - fb_rect.x2 = new_plane_state->fb->width; fb_rect.y2 = new_plane_state->fb->height; @@ -876,6 +863,31 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane, if (pstate->layout.plane_pitch[i] > DPU_SSPP_MAX_PITCH_SIZE) return -E2BIG; + pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state); + + return 0; +} + +static int dpu_plane_split(struct drm_plane *plane, + struct drm_plane_state *new_plane_state, + const struct drm_crtc_state *crtc_state) +{ + struct dpu_plane *pdpu = to_dpu_plane(plane); + struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base); + u64 max_mdp_clk_rate = kms->perf.max_core_clk_rate; + struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); + struct dpu_sw_pipe_cfg *pipe_cfg; + struct dpu_sw_pipe_cfg *r_pipe_cfg; + uint32_t max_linewidth; + + /* move the assignment here, to ease handling to another pairs later */ + pipe_cfg = &pstate->pipe_cfg[0]; + r_pipe_cfg = &pstate->pipe_cfg[1]; + /* state->src is 16.16, src_rect is not */ + drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src); + + pipe_cfg->dst_rect = new_plane_state->dst; + max_linewidth = pdpu->catalog->caps->max_linewidth; drm_rect_rotate(&pipe_cfg->src_rect, @@ -907,8 +919,6 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane, new_plane_state->fb->width, new_plane_state->fb->height, new_plane_state->rotation); - pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state); - return 0; } @@ -1129,6 +1139,10 @@ static int dpu_plane_atomic_check(struct drm_plane *plane, if (!new_plane_state->visible) return 0; + ret = dpu_plane_split(plane, new_plane_state, crtc_state); + if (ret) + return ret; + if (!dpu_plane_try_multirect_parallel(pipe, pipe_cfg, r_pipe, r_pipe_cfg, pipe->sspp, msm_framebuffer_format(new_plane_state->fb), @@ -1177,6 +1191,10 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane, return 0; } + ret = dpu_plane_split(plane, plane_state, crtc_state); + if (ret) + return ret; + /* * Force resource reallocation if the format of FB or src/dst have * changed. We might need to allocate different SSPP or SSPPs for this From 25ee1092775232626e2e85d68aaba97db0b1d51e Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Thu, 12 Mar 2026 16:28:11 +0800 Subject: [PATCH 271/712] drm/msm/dpu: Defer SSPP allocation until CRTC check Currently, mapping plane to SSPP occurs during the plane check phase for non-virtual plane case. The SSPP allocation and plane mapping occurs during CRTC check phase for virtual plane case. Defer these SSPP operations until CRTC check stage to unify the 2 cases, and ease later revisement for quad-pipe change. Signed-off-by: Jun Nie Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/711322/ Link: https://lore.kernel.org/r/20260312-msm-next-quad-pipe-split-v19-2-4ffa2b06c996@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 25 +++- drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 136 ++++++++++------------ 2 files changed, 80 insertions(+), 81 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 103cdbb38968..a4aaa43a62e3 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1321,7 +1321,7 @@ static bool dpu_crtc_needs_dirtyfb(struct drm_crtc_state *cstate) return false; } -static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) +static int dpu_crtc_assign_planes(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { int total_planes = crtc->dev->mode_config.num_total_plane; struct drm_atomic_state *state = crtc_state->state; @@ -1334,8 +1334,6 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state if (IS_ERR(global_state)) return PTR_ERR(global_state); - dpu_rm_release_all_sspp(global_state, crtc); - if (!crtc_state->enable) return 0; @@ -1362,6 +1360,19 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state return ret; } +static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) +{ + struct dpu_global_state *global_state; + + global_state = dpu_kms_get_global_state(crtc_state->state); + if (IS_ERR(global_state)) + return PTR_ERR(global_state); + + dpu_rm_release_all_sspp(global_state, crtc); + + return dpu_crtc_assign_planes(crtc, crtc_state); +} + #define MAX_CHANNELS_PER_CRTC PIPES_PER_PLANE #define MAX_HDISPLAY_SPLIT 1080 @@ -1531,9 +1542,11 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, return rc; } - if (dpu_use_virtual_planes && - (crtc_state->planes_changed || crtc_state->zpos_changed)) { - rc = dpu_crtc_reassign_planes(crtc, crtc_state); + if (crtc_state->planes_changed || crtc_state->zpos_changed) { + if (dpu_use_virtual_planes) + rc = dpu_crtc_reassign_planes(crtc, crtc_state); + else + rc = dpu_crtc_assign_planes(crtc, crtc_state); if (rc < 0) return rc; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index f34a8104e9c2..14fea0a15295 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -1109,65 +1109,13 @@ static int dpu_plane_try_multirect_shared(struct dpu_plane_state *pstate, static int dpu_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) -{ - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, - plane); - int ret = 0; - struct dpu_plane *pdpu = to_dpu_plane(plane); - struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); - struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); - struct dpu_sw_pipe *pipe = &pstate->pipe[0]; - struct dpu_sw_pipe *r_pipe = &pstate->pipe[1]; - struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg[0]; - struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->pipe_cfg[1]; - const struct drm_crtc_state *crtc_state = NULL; - uint32_t max_linewidth = dpu_kms->catalog->caps->max_linewidth; - - if (new_plane_state->crtc) - crtc_state = drm_atomic_get_new_crtc_state(state, - new_plane_state->crtc); - - pipe->sspp = dpu_rm_get_sspp(&dpu_kms->rm, pdpu->pipe); - - if (!pipe->sspp) - return -EINVAL; - - ret = dpu_plane_atomic_check_nosspp(plane, new_plane_state, crtc_state); - if (ret) - return ret; - - if (!new_plane_state->visible) - return 0; - - ret = dpu_plane_split(plane, new_plane_state, crtc_state); - if (ret) - return ret; - - if (!dpu_plane_try_multirect_parallel(pipe, pipe_cfg, r_pipe, r_pipe_cfg, - pipe->sspp, - msm_framebuffer_format(new_plane_state->fb), - max_linewidth)) { - DPU_DEBUG_PLANE(pdpu, "invalid " DRM_RECT_FMT " /" DRM_RECT_FMT - " max_line:%u, can't use split source\n", - DRM_RECT_ARG(&pipe_cfg->src_rect), - DRM_RECT_ARG(&r_pipe_cfg->src_rect), - max_linewidth); - return -E2BIG; - } - - return dpu_plane_atomic_check_sspp(plane, state, crtc_state); -} - -static int dpu_plane_virtual_atomic_check(struct drm_plane *plane, - struct drm_atomic_state *state) { struct drm_plane_state *plane_state = drm_atomic_get_plane_state(state, plane); struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); - struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state); + int ret = 0; struct drm_crtc_state *crtc_state = NULL; - int ret, i; if (IS_ERR(plane_state)) return PTR_ERR(plane_state); @@ -1180,20 +1128,8 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane, if (ret) return ret; - if (!plane_state->visible) { - /* - * resources are freed by dpu_crtc_assign_plane_resources(), - * but clean them here. - */ - for (i = 0; i < PIPES_PER_PLANE; i++) - pstate->pipe[i].sspp = NULL; - + if (!plane_state->visible) return 0; - } - - ret = dpu_plane_split(plane, plane_state, crtc_state); - if (ret) - return ret; /* * Force resource reallocation if the format of FB or src/dst have @@ -1208,7 +1144,6 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane, msm_framebuffer_format(old_plane_state->fb) != msm_framebuffer_format(plane_state->fb)) crtc_state->planes_changed = true; - return 0; } @@ -1255,9 +1190,9 @@ static int dpu_plane_virtual_assign_resources(struct drm_crtc *crtc, struct dpu_global_state *global_state, struct drm_atomic_state *state, struct drm_plane_state *plane_state, + const struct drm_crtc_state *crtc_state, struct drm_plane_state **prev_adjacent_plane_state) { - const struct drm_crtc_state *crtc_state = NULL; struct drm_plane *plane = plane_state->plane; struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); struct dpu_rm_sspp_requirements reqs; @@ -1267,10 +1202,6 @@ static int dpu_plane_virtual_assign_resources(struct drm_crtc *crtc, const struct msm_format *fmt; int i, ret; - if (plane_state->crtc) - crtc_state = drm_atomic_get_new_crtc_state(state, - plane_state->crtc); - pstate = to_dpu_plane_state(plane_state); for (i = 0; i < STAGES_PER_PLANE; i++) prev_adjacent_pstate[i] = prev_adjacent_plane_state[i] ? @@ -1282,6 +1213,10 @@ static int dpu_plane_virtual_assign_resources(struct drm_crtc *crtc, if (!plane_state->fb) return -EINVAL; + ret = dpu_plane_split(plane, plane_state, crtc_state); + if (ret) + return ret; + fmt = msm_framebuffer_format(plane_state->fb); reqs.yuv = MSM_FORMAT_IS_YUV(fmt); reqs.scale = (plane_state->src_w >> 16 != plane_state->crtc_w) || @@ -1312,14 +1247,55 @@ static int dpu_plane_virtual_assign_resources(struct drm_crtc *crtc, return dpu_plane_atomic_check_sspp(plane, state, crtc_state); } +static int dpu_plane_assign_resources(struct drm_crtc *crtc, + struct dpu_global_state *global_state, + struct drm_atomic_state *state, + struct drm_plane_state *plane_state, + const struct drm_crtc_state *crtc_state) +{ + struct drm_plane *plane = plane_state->plane; + struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state); + struct dpu_sw_pipe *pipe = &pstate->pipe[0]; + struct dpu_sw_pipe *r_pipe = &pstate->pipe[1]; + struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg[0]; + struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->pipe_cfg[1]; + struct dpu_plane *pdpu = to_dpu_plane(plane); + int ret; + + pipe->sspp = dpu_rm_get_sspp(&dpu_kms->rm, pdpu->pipe); + if (!pipe->sspp) + return -EINVAL; + + ret = dpu_plane_split(plane, plane_state, crtc_state); + if (ret) + return ret; + + if (!dpu_plane_try_multirect_parallel(pipe, pipe_cfg, r_pipe, r_pipe_cfg, + pipe->sspp, + msm_framebuffer_format(plane_state->fb), + dpu_kms->catalog->caps->max_linewidth)) { + DPU_DEBUG_PLANE(pdpu, "invalid " DRM_RECT_FMT " /" DRM_RECT_FMT + " max_line:%u, can't use split source\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), + DRM_RECT_ARG(&r_pipe_cfg->src_rect), + dpu_kms->catalog->caps->max_linewidth); + return -E2BIG; + } + + return dpu_plane_atomic_check_sspp(plane, state, crtc_state); +} + int dpu_assign_plane_resources(struct dpu_global_state *global_state, struct drm_atomic_state *state, struct drm_crtc *crtc, struct drm_plane_state **states, unsigned int num_planes) { - unsigned int i; struct drm_plane_state *prev_adjacent_plane_state[STAGES_PER_PLANE] = { NULL }; + const struct drm_crtc_state *crtc_state = NULL; + unsigned int i; + int ret; for (i = 0; i < num_planes; i++) { struct drm_plane_state *plane_state = states[i]; @@ -1328,8 +1304,18 @@ int dpu_assign_plane_resources(struct dpu_global_state *global_state, !plane_state->visible) continue; - int ret = dpu_plane_virtual_assign_resources(crtc, global_state, + if (plane_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, + plane_state->crtc); + + if (!dpu_use_virtual_planes) + ret = dpu_plane_assign_resources(crtc, global_state, + state, plane_state, + crtc_state); + else + ret = dpu_plane_virtual_assign_resources(crtc, global_state, state, plane_state, + crtc_state, prev_adjacent_plane_state); if (ret) return ret; @@ -1766,7 +1752,7 @@ static const struct drm_plane_helper_funcs dpu_plane_helper_funcs = { static const struct drm_plane_helper_funcs dpu_plane_virtual_helper_funcs = { .prepare_fb = dpu_plane_prepare_fb, .cleanup_fb = dpu_plane_cleanup_fb, - .atomic_check = dpu_plane_virtual_atomic_check, + .atomic_check = dpu_plane_atomic_check, .atomic_update = dpu_plane_atomic_update, }; From 9c171c36d83aa366aaa9d8e03e98a9a2108239c6 Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Thu, 12 Mar 2026 16:28:12 +0800 Subject: [PATCH 272/712] drm/msm/dpu: support plane splitting in quad-pipe case The content of every half of screen is sent out via one interface in dual-DSI case. The content for every interface is blended by a LM pair in quad-pipe case, thus a LM pair should not blend any content that cross the half of screen in this case. Clip plane into pipes per left and right half screen ROI if topology is quad pipe case. The clipped rectangle on every half of screen is futher handled by two pipes if its width exceeds a limit for a single pipe. For non-virtual-plane case, there is always one stage config to serve a LM or LM pair. So the clipping does not occur when interating stages in this case. The plane is mapped to 2 pipes only when width or clock rate exceeds hardware constrain within stage check. Signed-off-by: Jun Nie Reviewed-by: Dmitry Baryshkov Reviewed-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/711324/ Link: https://lore.kernel.org/r/20260312-msm-next-quad-pipe-split-v19-3-4ffa2b06c996@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 11 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | 2 + drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 148 +++++++++++++++------- 3 files changed, 118 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index a4aaa43a62e3..bd0e720b484f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1667,6 +1667,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en) return 0; } +/** + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline + * @state: Pointer to drm crtc state object + */ +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state) +{ + struct dpu_crtc_state *cstate = to_dpu_crtc_state(state); + + return cstate->num_mixers; +} + #ifdef CONFIG_DEBUG_FS static int _dpu_debugfs_status_show(struct seq_file *s, void *data) { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h index 94392b9b9245..6eaba5696e8e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type( void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event); +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state); + #endif /* _DPU_CRTC_H_ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index 14fea0a15295..22d80b6ef8a4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -878,47 +878,111 @@ static int dpu_plane_split(struct drm_plane *plane, struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); struct dpu_sw_pipe_cfg *pipe_cfg; struct dpu_sw_pipe_cfg *r_pipe_cfg; + const struct drm_display_mode *mode = &crtc_state->adjusted_mode; uint32_t max_linewidth; - - /* move the assignment here, to ease handling to another pairs later */ - pipe_cfg = &pstate->pipe_cfg[0]; - r_pipe_cfg = &pstate->pipe_cfg[1]; - /* state->src is 16.16, src_rect is not */ - drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src); - - pipe_cfg->dst_rect = new_plane_state->dst; + u32 num_lm; + int stage_id, num_stages; max_linewidth = pdpu->catalog->caps->max_linewidth; - drm_rect_rotate(&pipe_cfg->src_rect, - new_plane_state->fb->width, new_plane_state->fb->height, - new_plane_state->rotation); + /* In non-virtual plane case, one mixer pair is always needed. */ + num_lm = dpu_crtc_get_num_lm(crtc_state); + if (dpu_use_virtual_planes) + num_stages = (num_lm + 1) / 2; + else + num_stages = 1; - if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) || - _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) { - if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) { - DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n", - DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth); - return -E2BIG; + /* + * For wide plane that exceeds SSPP rectangle constrain, it needed to + * be split and mapped to 2 rectangles with 1 config for 2:2:1. + * For 2 interfaces cases, such as dual DSI, 2:2:2 topology is needed. + * If the width or clock exceeds hardware limitation in every half of + * screen, 4:4:2 topology is needed and virtual plane feature should + * be enabled to map plane to more than 1 SSPP. 2 stage configs are + * needed to serve 2 mixer pairs in this 4:4:2 case. So both left/right + * half of plane splitting, and splitting within the half of screen is + * needed in quad-pipe case. Check dest rectangle left/right clipping + * and iterate mixer configs for this plane first, then check wide + * rectangle splitting in every half next. + */ + for (stage_id = 0; stage_id < num_stages; stage_id++) { + struct drm_rect mixer_rect = { + .x1 = stage_id * mode->hdisplay / num_stages, + .y1 = 0, + .x2 = (stage_id + 1) * mode->hdisplay / num_stages, + .y2 = mode->vdisplay + }; + int cfg_idx = stage_id * PIPES_PER_STAGE; + + pipe_cfg = &pstate->pipe_cfg[cfg_idx]; + r_pipe_cfg = &pstate->pipe_cfg[cfg_idx + 1]; + + drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src); + + drm_rect_rotate(&pipe_cfg->src_rect, + new_plane_state->fb->width, new_plane_state->fb->height, + new_plane_state->rotation); + + pipe_cfg->dst_rect = new_plane_state->dst; + + DPU_DEBUG_PLANE(pdpu, "checking src " DRM_RECT_FMT + " vs clip window " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), + DRM_RECT_ARG(&mixer_rect)); + + /* + * If this plane does not fall into mixer rect, check next + * mixer rect. + */ + if (!drm_rect_clip_scaled(&pipe_cfg->src_rect, + &pipe_cfg->dst_rect, + &mixer_rect)) { + memset(pipe_cfg, 0, 2 * sizeof(struct dpu_sw_pipe_cfg)); + + continue; } - *r_pipe_cfg = *pipe_cfg; - pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1; - pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1; - r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2; - r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2; - } else { - memset(r_pipe_cfg, 0, sizeof(*r_pipe_cfg)); - } + pipe_cfg->dst_rect.x1 -= mixer_rect.x1; + pipe_cfg->dst_rect.x2 -= mixer_rect.x1; - drm_rect_rotate_inv(&pipe_cfg->src_rect, - new_plane_state->fb->width, new_plane_state->fb->height, - new_plane_state->rotation); - if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) - drm_rect_rotate_inv(&r_pipe_cfg->src_rect, - new_plane_state->fb->width, new_plane_state->fb->height, + DPU_DEBUG_PLANE(pdpu, "Got clip src:" DRM_RECT_FMT " dst: " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), DRM_RECT_ARG(&pipe_cfg->dst_rect)); + + /* Split wide rect into 2 rect */ + if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) || + _dpu_plane_calc_clk(mode, pipe_cfg) > max_mdp_clk_rate) { + + if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) { + DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth); + return -E2BIG; + } + + memcpy(r_pipe_cfg, pipe_cfg, sizeof(struct dpu_sw_pipe_cfg)); + pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1; + pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1; + r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2; + r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2; + DPU_DEBUG_PLANE(pdpu, "Split wide plane into:" + DRM_RECT_FMT " and " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), + DRM_RECT_ARG(&r_pipe_cfg->src_rect)); + } else { + memset(r_pipe_cfg, 0, sizeof(struct dpu_sw_pipe_cfg)); + } + + drm_rect_rotate_inv(&pipe_cfg->src_rect, + new_plane_state->fb->width, + new_plane_state->fb->height, new_plane_state->rotation); + if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) + drm_rect_rotate_inv(&r_pipe_cfg->src_rect, + new_plane_state->fb->width, + new_plane_state->fb->height, + new_plane_state->rotation); + } + return 0; } @@ -992,20 +1056,18 @@ static int dpu_plane_atomic_check_sspp(struct drm_plane *plane, drm_atomic_get_new_plane_state(state, plane); struct dpu_plane *pdpu = to_dpu_plane(plane); struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); - struct dpu_sw_pipe *pipe = &pstate->pipe[0]; - struct dpu_sw_pipe *r_pipe = &pstate->pipe[1]; - struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg[0]; - struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->pipe_cfg[1]; - int ret = 0; - ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg, - &crtc_state->adjusted_mode, - new_plane_state); - if (ret) - return ret; + struct dpu_sw_pipe *pipe; + struct dpu_sw_pipe_cfg *pipe_cfg; + int ret = 0, i; - if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) { - ret = dpu_plane_atomic_check_pipe(pdpu, r_pipe, r_pipe_cfg, + for (i = 0; i < PIPES_PER_PLANE; i++) { + pipe = &pstate->pipe[i]; + pipe_cfg = &pstate->pipe_cfg[i]; + if (!drm_rect_width(&pipe_cfg->src_rect)) + continue; + DPU_DEBUG_PLANE(pdpu, "pipe %d is in use, validate it\n", i); + ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg, &crtc_state->adjusted_mode, new_plane_state); if (ret) From b50dc1e54750a18265e7e465de38cd1c3c5ea543 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Tue, 24 Mar 2026 11:48:09 +0000 Subject: [PATCH 273/712] drm/mipi-dsi: add RGB101010 pixel format Add MIPI_DSI_FMT_RGB101010 for 30 bit (10,10,10 RGB) pixel format, corresponding to the packed 30 bit pixel stream defined in MIPI DSI v1.3 Section 8.8.17. Reviewed-by: Dmitry Baryshkov Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/713714/ Link: https://lore.kernel.org/r/20260324-dsi-rgb101010-support-v5-1-ff6afc904115@pm.me [Acked by Maxime to be merged through msm-next on IRC on dri-devel] [DB: moved RGB101010 to the end of enum mipi_dsi_pixel_format] Signed-off-by: Dmitry Baryshkov --- include/drm/drm_mipi_dsi.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 3aba7b380c8d..2ab651a36115 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -144,6 +144,7 @@ enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB666, MIPI_DSI_FMT_RGB666_PACKED, MIPI_DSI_FMT_RGB565, + MIPI_DSI_FMT_RGB101010, }; #define DSI_DEV_NAME_SIZE 20 @@ -235,6 +236,9 @@ extern const struct bus_type mipi_dsi_bus_type; static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt) { switch (fmt) { + case MIPI_DSI_FMT_RGB101010: + return 30; + case MIPI_DSI_FMT_RGB888: case MIPI_DSI_FMT_RGB666: return 24; From a780b7f6c8e52c3adb7dd63b1bbbbfae0f3e86c6 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Tue, 24 Mar 2026 11:48:17 +0000 Subject: [PATCH 274/712] drm/meson: use default case for unsupported DSI pixel formats Use default instead of listing unsupported formats explicitly, so the switch statements don't need updating each time a new pixel format is added. Reviewed-by: Neil Armstrong Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/713715/ Link: https://lore.kernel.org/r/20260324-dsi-rgb101010-support-v5-2-ff6afc904115@pm.me Acked-by: Neil Armstrong Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c index 66c73c512b0e..4412bd678108 100644 --- a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c @@ -119,8 +119,7 @@ static int dw_mipi_dsi_phy_init(void *priv_data) dpi_data_format = DPI_COLOR_18BIT_CFG_2; venc_data_width = VENC_IN_COLOR_18B; break; - case MIPI_DSI_FMT_RGB666_PACKED: - case MIPI_DSI_FMT_RGB565: + default: return -EINVAL; } @@ -232,8 +231,7 @@ static int meson_dw_mipi_dsi_host_attach(void *priv_data, break; case MIPI_DSI_FMT_RGB666: break; - case MIPI_DSI_FMT_RGB666_PACKED: - case MIPI_DSI_FMT_RGB565: + default: dev_err(mipi_dsi->dev, "invalid pixel format %d\n", device->format); return -EINVAL; } From 913a709dea0eff9c7b2e9470f8c8594b9a0114ab Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Tue, 24 Mar 2026 11:48:27 +0000 Subject: [PATCH 275/712] drm/msm/dsi: rename MSM8998 DSI version from V2_2_0 to V2_0_0 The MSM8998 DSI controller is v2.0.0 as stated in commit 7b8c9e203039 ("drm/msm/dsi: Add support for MSM8998 DSI controller"). The value was always correct just the name was wrong. Rename and reorder to maintain version sorting. Fixes: 7b8c9e203039 ("drm/msm/dsi: Add support for MSM8998 DSI controller") Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/713717/ Link: https://lore.kernel.org/r/20260324-dsi-rgb101010-support-v5-3-ff6afc904115@pm.me Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_cfg.c | 4 ++-- drivers/gpu/drm/msm/dsi/dsi_cfg.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c index bd3c51c350e7..da3fe6824495 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -317,10 +317,10 @@ static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { &msm8996_dsi_cfg, &msm_dsi_6g_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_4_2, &msm8976_dsi_cfg, &msm_dsi_6g_host_ops}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_0_0, + &msm8998_dsi_cfg, &msm_dsi_6g_v2_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_1_0, &sdm660_dsi_cfg, &msm_dsi_6g_v2_host_ops}, - {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_2_0, - &msm8998_dsi_cfg, &msm_dsi_6g_v2_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_2_1, &sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_3_0, diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h index 5dc812028bd5..ccf06679608e 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.h +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -19,8 +19,8 @@ #define MSM_DSI_6G_VER_MINOR_V1_3_1 0x10030001 #define MSM_DSI_6G_VER_MINOR_V1_4_1 0x10040001 #define MSM_DSI_6G_VER_MINOR_V1_4_2 0x10040002 +#define MSM_DSI_6G_VER_MINOR_V2_0_0 0x20000000 #define MSM_DSI_6G_VER_MINOR_V2_1_0 0x20010000 -#define MSM_DSI_6G_VER_MINOR_V2_2_0 0x20000000 #define MSM_DSI_6G_VER_MINOR_V2_2_1 0x20020001 #define MSM_DSI_6G_VER_MINOR_V2_3_0 0x20030000 #define MSM_DSI_6G_VER_MINOR_V2_3_1 0x20030001 From a65c4d30988e81e7291063f9c473f50ad1e715a1 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Tue, 24 Mar 2026 11:48:38 +0000 Subject: [PATCH 276/712] drm/msm/dsi: add DSI version >= comparison helper Add a helper for checking if the DSI hardware version is greater than or equal to a given version, for use in a future change. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/713719/ Link: https://lore.kernel.org/r/20260324-dsi-rgb101010-support-v5-4-ff6afc904115@pm.me Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 1c0841a1c101..b5a0b282b033 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -783,13 +783,21 @@ static void dsi_ctrl_disable(struct msm_dsi_host *msm_host) dsi_write(msm_host, REG_DSI_CTRL, 0); } +static bool msm_dsi_host_version_geq(struct msm_dsi_host *msm_host, + u32 major, u32 minor) +{ + return msm_host->cfg_hnd->major > major || + (msm_host->cfg_hnd->major == major && + msm_host->cfg_hnd->minor >= minor); +} + bool msm_dsi_host_is_wide_bus_enabled(struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); return msm_host->dsc && - (msm_host->cfg_hnd->major == MSM_DSI_VER_MAJOR_6G && - msm_host->cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V2_5_0); + msm_dsi_host_version_geq(msm_host, MSM_DSI_VER_MAJOR_6G, + MSM_DSI_6G_VER_MINOR_V2_5_0); } static void dsi_ctrl_enable(struct msm_dsi_host *msm_host, From cebf747abeebbde96a43ddd646d14b55a72673a7 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Tue, 24 Mar 2026 11:48:49 +0000 Subject: [PATCH 277/712] drm/msm/dsi: Add support for RGB101010 pixel format Add video and command mode destination format mappings for RGB101010, and extend the VID_CFG0 DST_FORMAT bitfield to 3 bits to accommodate the new format value. Make sure this is guarded behind MSM_DSI_6G_VER >= V2.1.0 as anything older does not support this. Required for 10 bit panels such as the BOE BF068MWM-TD0 found on the Nothing Phone (3a). Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/713721/ Link: https://lore.kernel.org/r/20260324-dsi-rgb101010-support-v5-5-ff6afc904115@pm.me Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 22 +++++++++++++++++++ drivers/gpu/drm/msm/registers/display/dsi.xml | 5 ++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index b5a0b282b033..565d425f88b8 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -758,6 +758,7 @@ static inline enum dsi_vid_dst_format dsi_get_vid_fmt(const enum mipi_dsi_pixel_format mipi_fmt) { switch (mipi_fmt) { + case MIPI_DSI_FMT_RGB101010: return VID_DST_FORMAT_RGB101010; case MIPI_DSI_FMT_RGB888: return VID_DST_FORMAT_RGB888; case MIPI_DSI_FMT_RGB666: return VID_DST_FORMAT_RGB666_LOOSE; case MIPI_DSI_FMT_RGB666_PACKED: return VID_DST_FORMAT_RGB666; @@ -770,6 +771,7 @@ static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt(const enum mipi_dsi_pixel_format mipi_fmt) { switch (mipi_fmt) { + case MIPI_DSI_FMT_RGB101010: return CMD_DST_FORMAT_RGB101010; case MIPI_DSI_FMT_RGB888: return CMD_DST_FORMAT_RGB888; case MIPI_DSI_FMT_RGB666_PACKED: case MIPI_DSI_FMT_RGB666: return CMD_DST_FORMAT_RGB666; @@ -1719,6 +1721,26 @@ static int dsi_host_attach(struct mipi_dsi_host *host, if (dsi->dsc) msm_host->dsc = dsi->dsc; + if (msm_host->format == MIPI_DSI_FMT_RGB101010) { + if (!msm_dsi_host_version_geq(msm_host, MSM_DSI_VER_MAJOR_6G, + MSM_DSI_6G_VER_MINOR_V2_1_0)) { + DRM_DEV_ERROR(&msm_host->pdev->dev, + "RGB101010 not supported on this DSI controller\n"); + return -EINVAL; + } + + /* + * Downstream overrides RGB101010 back to RGB888 when DSC is enabled + * but widebus is not. Using RGB101010 in this case may require some + * extra changes. + */ + if (msm_host->dsc && + !msm_dsi_host_is_wide_bus_enabled(&msm_host->base)) { + dev_warn(&msm_host->pdev->dev, + "RGB101010 with DSC but without widebus, may need extra changes\n"); + } + } + ret = dsi_dev_attach(msm_host->pdev); if (ret) return ret; diff --git a/drivers/gpu/drm/msm/registers/display/dsi.xml b/drivers/gpu/drm/msm/registers/display/dsi.xml index c7a7b633d747..e40125f75175 100644 --- a/drivers/gpu/drm/msm/registers/display/dsi.xml +++ b/drivers/gpu/drm/msm/registers/display/dsi.xml @@ -15,6 +15,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + @@ -39,6 +40,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + @@ -142,7 +144,8 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - + + From e398978ddf18fe5a2fc8299c77e6fe50e6c306c4 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 25 Mar 2026 13:34:18 +0100 Subject: [PATCH 278/712] workqueue: Better describe stall check Try to be more explicit why the workqueue watchdog does not take pool->lock by default. Spin locks are full memory barriers which delay anything. Obviously, they would primary delay operations on the related worker pools. Explain why it is enough to prevent the false positive by re-checking the timestamp under the pool->lock. Finally, make it clear what would be the alternative solution in __queue_work() which is a hotter path. Signed-off-by: Petr Mladek Acked-by: Song Liu Signed-off-by: Tejun Heo --- kernel/workqueue.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ff97b705f25e..eda756556341 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -7702,13 +7702,14 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) /* * Did we stall? * - * Do a lockless check first. On weakly ordered - * architectures, the lockless check can observe a - * reordering between worklist insert_work() and - * last_progress_ts update from __queue_work(). Since - * __queue_work() is a much hotter path than the timer - * function, we handle false positive here by reading - * last_progress_ts again with pool->lock held. + * Do a lockless check first to do not disturb the system. + * + * Prevent false positives by double checking the timestamp + * under pool->lock. The lock makes sure that the check reads + * an updated pool->last_progress_ts when this CPU saw + * an already updated pool->worklist above. It seems better + * than adding another barrier into __queue_work() which + * is a hotter path. */ if (time_after(now, ts + thresh)) { scoped_guard(raw_spinlock_irqsave, &pool->lock) { From 789b06f9f39cdc7e895bdab2c034e39c41c8f8d6 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 25 Mar 2026 01:46:02 +0300 Subject: [PATCH 279/712] wifi: virt_wifi: remove SET_NETDEV_DEV to avoid use-after-free Currently we execute `SET_NETDEV_DEV(dev, &priv->lowerdev->dev)` for the virt_wifi net devices. However, unregistering a virt_wifi device in netdev_run_todo() can happen together with the device referenced by SET_NETDEV_DEV(). It can result in use-after-free during the ethtool operations performed on a virt_wifi device that is currently being unregistered. Such a net device can have the `dev.parent` field pointing to the freed memory, but ethnl_ops_begin() calls `pm_runtime_get_sync(dev->dev.parent)`. Let's remove SET_NETDEV_DEV for virt_wifi to avoid bugs like this: ================================================================== BUG: KASAN: slab-use-after-free in __pm_runtime_resume+0xe2/0xf0 Read of size 2 at addr ffff88810cfc46f8 by task pm/606 Call Trace: dump_stack_lvl+0x4d/0x70 print_report+0x170/0x4f3 ? __pfx__raw_spin_lock_irqsave+0x10/0x10 kasan_report+0xda/0x110 ? __pm_runtime_resume+0xe2/0xf0 ? __pm_runtime_resume+0xe2/0xf0 __pm_runtime_resume+0xe2/0xf0 ethnl_ops_begin+0x49/0x270 ethnl_set_features+0x23c/0xab0 ? __pfx_ethnl_set_features+0x10/0x10 ? kvm_sched_clock_read+0x11/0x20 ? local_clock_noinstr+0xf/0xf0 ? local_clock+0x10/0x30 ? kasan_save_track+0x25/0x60 ? __kasan_kmalloc+0x7f/0x90 ? genl_family_rcv_msg_attrs_parse.isra.0+0x150/0x2c0 genl_family_rcv_msg_doit+0x1e7/0x2c0 ? __pfx_genl_family_rcv_msg_doit+0x10/0x10 ? __pfx_cred_has_capability.isra.0+0x10/0x10 ? stack_trace_save+0x8e/0xc0 genl_rcv_msg+0x411/0x660 ? __pfx_genl_rcv_msg+0x10/0x10 ? __pfx_ethnl_set_features+0x10/0x10 netlink_rcv_skb+0x121/0x380 ? __pfx_genl_rcv_msg+0x10/0x10 ? __pfx_netlink_rcv_skb+0x10/0x10 ? __pfx_down_read+0x10/0x10 genl_rcv+0x23/0x30 netlink_unicast+0x60f/0x830 ? __pfx_netlink_unicast+0x10/0x10 ? __pfx___alloc_skb+0x10/0x10 netlink_sendmsg+0x6ea/0xbc0 ? __pfx_netlink_sendmsg+0x10/0x10 ? __futex_queue+0x10b/0x1f0 ____sys_sendmsg+0x7a2/0x950 ? copy_msghdr_from_user+0x26b/0x430 ? __pfx_____sys_sendmsg+0x10/0x10 ? __pfx_copy_msghdr_from_user+0x10/0x10 ___sys_sendmsg+0xf8/0x180 ? __pfx____sys_sendmsg+0x10/0x10 ? __pfx_futex_wait+0x10/0x10 ? fdget+0x2e4/0x4a0 __sys_sendmsg+0x11f/0x1c0 ? __pfx___sys_sendmsg+0x10/0x10 do_syscall_64+0xe2/0x570 ? exc_page_fault+0x66/0xb0 entry_SYSCALL_64_after_hwframe+0x77/0x7f This fix may be combined with another one in the ethtool subsystem: https://lore.kernel.org/all/20260322075917.254874-1-alex.popov@linux.com/T/#u Fixes: d43c65b05b848e0b ("ethtool: runtime-resume netdev parent in ethnl_ops_begin") Cc: stable@vger.kernel.org Signed-off-by: Alexander Popov Acked-by: Greg Kroah-Hartman Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260324224607.374327-1-alex.popov@linux.com Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/virt_wifi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/virtual/virt_wifi.c b/drivers/net/wireless/virtual/virt_wifi.c index 885dc7243e8d..97bd39d89e98 100644 --- a/drivers/net/wireless/virtual/virt_wifi.c +++ b/drivers/net/wireless/virtual/virt_wifi.c @@ -557,7 +557,6 @@ static int virt_wifi_newlink(struct net_device *dev, eth_hw_addr_inherit(dev, priv->lowerdev); netif_stacked_transfer_operstate(priv->lowerdev, dev); - SET_NETDEV_DEV(dev, &priv->lowerdev->dev); dev->ieee80211_ptr = kzalloc_obj(*dev->ieee80211_ptr); if (!dev->ieee80211_ptr) { From 4c56a8ac6869855866de0bb368a4189739e1d24f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 25 Mar 2026 07:23:48 -1000 Subject: [PATCH 280/712] cgroup: Fix cgroup_drain_dying() testing the wrong condition cgroup_drain_dying() was using cgroup_is_populated() to test whether there are dying tasks to wait for. cgroup_is_populated() tests nr_populated_csets, nr_populated_domain_children and nr_populated_threaded_children, but cgroup_drain_dying() only needs to care about this cgroup's own tasks - whether there are children is cgroup_destroy_locked()'s concern. This caused hangs during shutdown. When systemd tried to rmdir a cgroup that had no direct tasks but had a populated child, cgroup_drain_dying() would enter its wait loop because cgroup_is_populated() was true from nr_populated_domain_children. The task iterator found nothing to wait for, yet the populated state never cleared because it was driven by live tasks in the child cgroup. Fix it by using cgroup_has_tasks() which only tests nr_populated_csets. v3: Fix cgroup_is_populated() -> cgroup_has_tasks() (Sebastian). v2: https://lore.kernel.org/r/20260323200205.1063629-1-tj@kernel.org Reported-by: Sebastian Andrzej Siewior Fixes: 1b164b876c36 ("cgroup: Wait for dying tasks to leave on rmdir") Signed-off-by: Tejun Heo Tested-by: Sebastian Andrzej Siewior --- kernel/cgroup/cgroup.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 2163054e1aa6..4ca3cb993da2 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -6229,20 +6229,22 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * cgroup_drain_dying - wait for dying tasks to leave before rmdir * @cgrp: the cgroup being removed * - * The PF_EXITING filter in css_task_iter_advance() hides exiting tasks from - * cgroup.procs so that userspace (e.g. systemd) doesn't see tasks that have - * already been reaped via waitpid(). However, the populated counter - * (nr_populated_csets) is only decremented when the task later passes through + * cgroup.procs and cgroup.threads use css_task_iter which filters out + * PF_EXITING tasks so that userspace doesn't see tasks that have already been + * reaped via waitpid(). However, cgroup_has_tasks() - which tests whether the + * cgroup has non-empty css_sets - is only updated when dying tasks pass through * cgroup_task_dead() in finish_task_switch(). This creates a window where - * cgroup.procs appears empty but cgroup_is_populated() is still true, causing - * rmdir to fail with -EBUSY. + * cgroup.procs reads empty but cgroup_has_tasks() is still true, making rmdir + * fail with -EBUSY from cgroup_destroy_locked() even though userspace sees no + * tasks. * - * This function bridges that gap. If the cgroup is populated but all remaining - * tasks have PF_EXITING set, we wait for cgroup_task_dead() to process them. - * Tasks are removed from the cgroup's css_set in cgroup_task_dead() called from - * finish_task_switch(). As the window between PF_EXITING and cgroup_task_dead() - * is short, the number of PF_EXITING tasks on the list is small and the wait - * is brief. + * This function aligns cgroup_has_tasks() with what userspace can observe. If + * cgroup_has_tasks() but the task iterator sees nothing (all remaining tasks are + * PF_EXITING), we wait for cgroup_task_dead() to finish processing them. As the + * window between PF_EXITING and cgroup_task_dead() is short, the wait is brief. + * + * This function only concerns itself with this cgroup's own dying tasks. + * Whether the cgroup has children is cgroup_destroy_locked()'s problem. * * Each cgroup_task_dead() kicks the waitqueue via cset->cgrp_links, and we * retry the full check from scratch. @@ -6258,7 +6260,7 @@ static int cgroup_drain_dying(struct cgroup *cgrp) lockdep_assert_held(&cgroup_mutex); retry: - if (!cgroup_is_populated(cgrp)) + if (!cgroup_has_tasks(cgrp)) return 0; /* Same iterator as cgroup.threads - if any task is visible, it's busy */ @@ -6273,15 +6275,15 @@ static int cgroup_drain_dying(struct cgroup *cgrp) * All remaining tasks are PF_EXITING and will pass through * cgroup_task_dead() shortly. Wait for a kick and retry. * - * cgroup_is_populated() can't transition from false to true while - * we're holding cgroup_mutex, but the true to false transition - * happens under css_set_lock (via cgroup_task_dead()). We must - * retest and prepare_to_wait() under css_set_lock. Otherwise, the - * transition can happen between our first test and - * prepare_to_wait(), and we sleep with no one to wake us. + * cgroup_has_tasks() can't transition from false to true while we're + * holding cgroup_mutex, but the true to false transition happens + * under css_set_lock (via cgroup_task_dead()). We must retest and + * prepare_to_wait() under css_set_lock. Otherwise, the transition + * can happen between our first test and prepare_to_wait(), and we + * sleep with no one to wake us. */ spin_lock_irq(&css_set_lock); - if (!cgroup_is_populated(cgrp)) { + if (!cgroup_has_tasks(cgrp)) { spin_unlock_irq(&css_set_lock); return 0; } From e64b9cc293ae710c815c2de1ec9dcaa0784a8017 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 16 Mar 2026 17:16:09 -0400 Subject: [PATCH 281/712] rust: drm: Add gem::impl_aref_for_gem_obj! In the future we're going to be introducing more GEM object types in rust then just gem::Object. Since all types of GEM objects have refcounting, let's introduce a macro that we can use in the gem crate in order to copy this boilerplate implementation for each type: impl_aref_for_gem_obj!(). Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida Reviewed-by: Janne Grunau Tested-by: Deborah Brouwer Link: https://patch.msgid.link/20260316211646.650074-2-lyude@redhat.com [ Resolve merge conflicts. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/drm/gem/mod.rs | 51 +++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index b4199945db37..48ffdd7400ca 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -26,6 +26,41 @@ ptr::NonNull, // }; +/// A macro for implementing [`AlwaysRefCounted`] for any GEM object type. +/// +/// Since all GEM objects use the same refcounting scheme. +#[macro_export] +macro_rules! impl_aref_for_gem_obj { + ( + impl $( <$( $tparam_id:ident ),+> )? for $type:ty + $( + where + $( $bind_param:path : $bind_trait:path ),+ + )? + ) => { + // SAFETY: All GEM objects are refcounted. + unsafe impl $( <$( $tparam_id ),+> )? $crate::types::AlwaysRefCounted for $type + where + Self: IntoGEMObject, + $( $( $bind_param : $bind_trait ),+ )? + { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the refcount is + // non-zero. + unsafe { bindings::drm_gem_object_get(self.as_raw()) }; + } + + unsafe fn dec_ref(obj: core::ptr::NonNull) { + // SAFETY: `obj` is a valid pointer to an `Object`. + let obj = unsafe { obj.as_ref() }.as_raw(); + + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::drm_gem_object_put(obj) }; + } + } + }; +} + /// A type alias for retrieving a [`Driver`]s [`DriverFile`] implementation from its /// [`DriverObject`] implementation. /// @@ -263,21 +298,7 @@ extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { } } -// SAFETY: Instances of `Object` are always reference-counted. -unsafe impl crate::sync::aref::AlwaysRefCounted for Object { - fn inc_ref(&self) { - // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. - unsafe { bindings::drm_gem_object_get(self.as_raw()) }; - } - - unsafe fn dec_ref(obj: NonNull) { - // SAFETY: `obj` is a valid pointer to an `Object`. - let obj = unsafe { obj.as_ref() }; - - // SAFETY: The safety requirements guarantee that the refcount is non-zero. - unsafe { bindings::drm_gem_object_put(obj.as_raw()) } - } -} +impl_aref_for_gem_obj!(impl for Object where T: DriverObject); impl super::private::Sealed for Object {} From 442ba16a5a51368f5bafd011609f40782aec6d65 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 16 Mar 2026 17:16:12 -0400 Subject: [PATCH 282/712] rust: gem: Introduce DriverObject::Args This is an associated type that may be used in order to specify a data-type to pass to gem objects when constructing them, allowing for drivers to more easily initialize their private-data for gem objects. Signed-off-by: Lyude Paul Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida Reviewed-by: Janne Grunau Tested-by: Deborah Brouwer Link: https://patch.msgid.link/20260316211646.650074-5-lyude@redhat.com [ Resolve merge conflicts in Tyr. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nova/gem.rs | 5 +++-- drivers/gpu/drm/tyr/gem.rs | 3 ++- rust/kernel/drm/gem/mod.rs | 13 ++++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs index 6ccfa5da5761..e073e174e257 100644 --- a/drivers/gpu/drm/nova/gem.rs +++ b/drivers/gpu/drm/nova/gem.rs @@ -19,8 +19,9 @@ pub(crate) struct NovaObject {} impl gem::DriverObject for NovaObject { type Driver = NovaDriver; + type Args = (); - fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit { + fn new(_dev: &NovaDevice, _size: usize, _args: Self::Args) -> impl PinInit { try_pin_init!(NovaObject {}) } } @@ -33,7 +34,7 @@ pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result impl PinInit { + fn new(_dev: &TyrDrmDevice, _size: usize, _args: ()) -> impl PinInit { try_pin_init!(TyrObject {}) } } diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 48ffdd7400ca..ed974bfdc861 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -73,8 +73,15 @@ pub trait DriverObject: Sync + Send + Sized { /// Parent `Driver` for this object. type Driver: drm::Driver; + /// The data type to use for passing arguments to [`DriverObject::new`]. + type Args; + /// Create a new driver data object for a GEM object of a given size. - fn new(dev: &drm::Device, size: usize) -> impl PinInit; + fn new( + dev: &drm::Device, + size: usize, + args: Self::Args, + ) -> impl PinInit; /// Open a new handle to an existing object, associated with a File. fn open(_obj: &::Object, _file: &DriverFile) -> Result { @@ -241,11 +248,11 @@ impl Object { }; /// Create a new GEM object. - pub fn new(dev: &drm::Device, size: usize) -> Result> { + pub fn new(dev: &drm::Device, size: usize, args: T::Args) -> Result> { let obj: Pin> = KBox::pin_init( try_pin_init!(Self { obj: Opaque::new(bindings::drm_gem_object::default()), - data <- T::new(dev, size), + data <- T::new(dev, size, args), }), GFP_KERNEL, )?; From 1de647abdfda9dc307503d0a85152161850ba52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Fri, 20 Mar 2026 10:04:03 +0200 Subject: [PATCH 283/712] drm/i915/psr: Fixes for Dell XPS DA14260 quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dell seems to be changing device ID even within same device model. Due to this we need to ignore device ID when applying quirk for Dell XPS 14 DA14260. Do this by adding DEVICE_ID_ANY and assign it to Dell XPS 14 DA14260 quirk. Also apply the quirk only for eDP Panel Replay. Fixes: 45c77d4bf8d4 ("drm/i915/psr: Disable Panel Replay on Dell XPS 14 DA14260 as a quirk") Cc: Mika Kahola Signed-off-by: Jouni Högander Reviewed-by: Mika Kahola Link: https://patch.msgid.link/20260320080403.1396926-1-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 3 ++- drivers/gpu/drm/i915/display/intel_quirks.c | 16 ++++++++++------ drivers/gpu/drm/i915/display/intel_quirks.h | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index b319e5bd6274..2f1b48cd8efd 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -610,7 +610,8 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp, struct intel_conn if (intel_dp->mst_detect == DRM_DP_MST) return; - if (intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY)) { + if (intel_dp_is_edp(intel_dp) && + intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY)) { drm_dbg_kms(display->drm, "Panel Replay support not currently available for this setup\n"); return; diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c index 8f1bf8f418ec..883f297d4b83 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.c +++ b/drivers/gpu/drm/i915/display/intel_quirks.c @@ -86,11 +86,11 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display *display) drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n"); } -static void quirk_disable_panel_replay(struct intel_dp *intel_dp) +static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY); + intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY); drm_info(display->drm, "Applying disable Panel Replay quirk\n"); } @@ -116,6 +116,8 @@ struct intel_dpcd_quirk { #define SINK_DEVICE_ID_ANY SINK_DEVICE_ID(0, 0, 0, 0, 0, 0) +#define DEVICE_ID_ANY 0 + /* For systems that don't have a meaningful PCI subdevice/subvendor ID */ struct intel_dmi_quirk { void (*hook)(struct intel_display *display); @@ -261,11 +263,11 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { }, /* Dell XPS 14 DA14260 */ { - .device = 0xb080, + .device = DEVICE_ID_ANY, .subsystem_vendor = 0x1028, .subsystem_device = 0x0db9, .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), - .hook = quirk_disable_panel_replay, + .hook = quirk_disable_edp_panel_replay, }, }; @@ -277,7 +279,8 @@ void intel_init_quirks(struct intel_display *display) for (i = 0; i < ARRAY_SIZE(intel_quirks); i++) { struct intel_quirk *q = &intel_quirks[i]; - if (d->device == q->device && + if ((d->device == q->device || + q->device == DEVICE_ID_ANY) && (d->subsystem_vendor == q->subsystem_vendor || q->subsystem_vendor == PCI_ANY_ID) && (d->subsystem_device == q->subsystem_device || @@ -300,7 +303,8 @@ void intel_init_dpcd_quirks(struct intel_dp *intel_dp, for (i = 0; i < ARRAY_SIZE(intel_dpcd_quirks); i++) { const struct intel_dpcd_quirk *q = &intel_dpcd_quirks[i]; - if (d->device == q->device && + if ((d->device == q->device || + q->device == DEVICE_ID_ANY) && (d->subsystem_vendor == q->subsystem_vendor || q->subsystem_vendor == PCI_ANY_ID) && (d->subsystem_device == q->subsystem_device || diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h index 77e490caed0d..83214eb94b0c 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.h +++ b/drivers/gpu/drm/i915/display/intel_quirks.h @@ -21,7 +21,7 @@ enum intel_quirk_id { QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK, QUIRK_FW_SYNC_LEN, QUIRK_EDP_LIMIT_RATE_HBR2, - QUIRK_DISABLE_PANEL_REPLAY, + QUIRK_DISABLE_EDP_PANEL_REPLAY, }; void intel_init_quirks(struct intel_display *display); From b525d0c5e9ec4e51b54b8853047303957e8afbc4 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:14 +0900 Subject: [PATCH 284/712] gpu: nova-core: introduce `bounded_enum` macro Introduce a powered-up version of our ad-hoc `impl_from_enum_to_u8` macro that allows the definition of an enum type associated to a `Bounded` of a given width, and provides the `From` and `TryFrom` implementations required to use that enum as a register field member. This allows us to generate the required conversion implementations for using the kernel register macro and skip some tedious boilerplate. Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-1-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/nova_core.rs | 1 + drivers/gpu/nova-core/num.rs | 80 ++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index ccd14b757b49..98675c69d2b7 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -21,6 +21,7 @@ mod gfw; mod gpu; mod gsp; +#[macro_use] mod num; mod regs; mod sbuffer; diff --git a/drivers/gpu/nova-core/num.rs b/drivers/gpu/nova-core/num.rs index c952a834e662..6c824b8d7b97 100644 --- a/drivers/gpu/nova-core/num.rs +++ b/drivers/gpu/nova-core/num.rs @@ -215,3 +215,83 @@ pub(crate) const fn [<$from _into_ $into>]() -> $into { impl_const_into!(u64 => { u8, u16, u32 }); impl_const_into!(u32 => { u8, u16 }); impl_const_into!(u16 => { u8 }); + +/// Creates an enum type associated to a [`Bounded`](kernel::num::Bounded), with a [`From`] +/// conversion to the associated `Bounded` and either a [`TryFrom`] or `From` conversion from the +/// associated `Bounded`. +// TODO[FPRI]: This is a temporary solution to be replaced with the corresponding derive macros +// once they land. +#[macro_export] +macro_rules! bounded_enum { + ( + $(#[$enum_meta:meta])* + $vis:vis enum $enum_type:ident with $from_impl:ident> { + $( $(#[doc = $variant_doc:expr])* $variant:ident = $value:expr),* $(,)* + } + ) => { + $(#[$enum_meta])* + $vis enum $enum_type { + $( + $(#[doc = $variant_doc])* + $variant = $value + ),* + } + + impl core::convert::From<$enum_type> for kernel::num::Bounded<$width, $length> { + fn from(value: $enum_type) -> Self { + match value { + $($enum_type::$variant => + kernel::num::Bounded::<$width, _>::new::<{ $value }>()),* + } + } + } + + bounded_enum!(@impl_from $enum_type with $from_impl> { + $($variant = $value),* + }); + }; + + // `TryFrom` implementation from associated `Bounded` to enum type. + (@impl_from $enum_type:ident with TryFrom> { + $($variant:ident = $value:expr),* $(,)* + }) => { + impl core::convert::TryFrom> for $enum_type { + type Error = kernel::error::Error; + + fn try_from( + value: kernel::num::Bounded<$width, $length> + ) -> kernel::error::Result { + match value.get() { + $( + $value => Ok($enum_type::$variant), + )* + _ => Err(kernel::error::code::EINVAL), + } + } + } + }; + + // `From` implementation from associated `Bounded` to enum type. Triggers a build-time error if + // all possible values of the `Bounded` are not covered by the enum type. + (@impl_from $enum_type:ident with From> { + $($variant:ident = $value:expr),* $(,)* + }) => { + impl core::convert::From> for $enum_type { + fn from(value: kernel::num::Bounded<$width, $length>) -> Self { + const MAX: $width = 1 << $length; + + // Makes the compiler optimizer aware of the possible range of values. + let value = value.get() & ((1 << $length) - 1); + match value { + $( + $value => $enum_type::$variant, + )* + // PANIC: we cannot reach this arm as all possible variants are handled by the + // match arms above. It is here to make the compiler complain if `$enum_type` + // does not cover all values of the `0..MAX` range. + MAX.. => unreachable!(), + } + } + } + } +} From 1b155edcab0832a887387dd77e209e37beb7b49c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:15 +0900 Subject: [PATCH 285/712] gpu: nova-core: convert PMC registers to kernel register macro Convert all PMC registers to use the kernel's register macro and update the code accordingly. Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-2-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 7 +++-- drivers/gpu/nova-core/gpu.rs | 53 ++++++++++----------------------- drivers/gpu/nova-core/regs.rs | 50 ++++++++++++++++++++----------- 3 files changed, 53 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 5bf8da8760bf..123de6c55b45 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -13,7 +13,10 @@ DmaAddress, DmaMask, // }, - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + Io, // + }, prelude::*, sync::aref::ARef, time::Delta, @@ -531,7 +534,7 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result { self.hal.reset_wait_mem_scrubbing(bar)?; regs::NV_PFALCON_FALCON_RM::default() - .set_value(regs::NV_PMC_BOOT_0::read(bar).into()) + .set_value(bar.read(regs::NV_PMC_BOOT_0).into()) .write(bar, &E::ID); Ok(()) diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 8579d632e717..0f6fe9a1b955 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -4,12 +4,15 @@ device, devres::Devres, fmt, + io::Io, + num::Bounded, pci, prelude::*, sync::Arc, // }; use crate::{ + bounded_enum, driver::Bar0, falcon::{ gsp::Gsp as GspFalcon, @@ -128,50 +131,26 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } -/// Enum representation of the GPU generation. -/// -/// TODO: remove the `Default` trait implementation, and the `#[default]` -/// attribute, once the register!() macro (which creates Architecture items) no -/// longer requires it for read-only fields. -#[derive(fmt::Debug, Default, Copy, Clone)] -#[repr(u8)] -pub(crate) enum Architecture { - #[default] - Turing = 0x16, - Ampere = 0x17, - Ada = 0x19, -} - -impl TryFrom for Architecture { - type Error = Error; - - fn try_from(value: u8) -> Result { - match value { - 0x16 => Ok(Self::Turing), - 0x17 => Ok(Self::Ampere), - 0x19 => Ok(Self::Ada), - _ => Err(ENODEV), - } - } -} - -impl From for u8 { - fn from(value: Architecture) -> Self { - // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless. - value as u8 +bounded_enum! { + /// Enum representation of the GPU generation. + #[derive(fmt::Debug, Copy, Clone)] + pub(crate) enum Architecture with TryFrom> { + Turing = 0x16, + Ampere = 0x17, + Ada = 0x19, } } pub(crate) struct Revision { - major: u8, - minor: u8, + major: Bounded, + minor: Bounded, } impl From for Revision { fn from(boot0: regs::NV_PMC_BOOT_42) -> Self { Self { - major: boot0.major_revision(), - minor: boot0.minor_revision(), + major: boot0.major_revision().cast(), + minor: boot0.minor_revision().cast(), } } } @@ -208,13 +187,13 @@ fn new(dev: &device::Device, bar: &Bar0) -> Result { // from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU. // Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs. - let boot0 = regs::NV_PMC_BOOT_0::read(bar); + let boot0 = bar.read(regs::NV_PMC_BOOT_0); if boot0.is_older_than_fermi() { return Err(ENODEV); } - let boot42 = regs::NV_PMC_BOOT_42::read(bar); + let boot42 = bar.read(regs::NV_PMC_BOOT_42); Spec::try_from(boot42).inspect_err(|_| { dev_err!(dev, "Unsupported chipset: {}\n", boot42); }) diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 53f412f0ca32..58fb807605dd 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -8,6 +8,7 @@ pub(crate) mod macros; use kernel::{ + io, prelude::*, time, // }; @@ -37,18 +38,38 @@ // PMC -register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU" { - 3:0 minor_revision as u8, "Minor revision of the chip"; - 7:4 major_revision as u8, "Major revision of the chip"; - 8:8 architecture_1 as u8, "MSB of the architecture"; - 23:20 implementation as u8, "Implementation version of the architecture"; - 28:24 architecture_0 as u8, "Lower bits of the architecture"; -}); +io::register! { + /// Basic revision information about the GPU. + pub(crate) NV_PMC_BOOT_0(u32) @ 0x00000000 { + /// Lower bits of the architecture. + 28:24 architecture_0; + /// Implementation version of the architecture. + 23:20 implementation; + /// MSB of the architecture. + 8:8 architecture_1; + /// Major revision of the chip. + 7:4 major_revision; + /// Minor revision of the chip. + 3:0 minor_revision; + } + + /// Extended architecture information. + pub(crate) NV_PMC_BOOT_42(u32) @ 0x00000a00 { + /// Architecture value. + 29:24 architecture ?=> Architecture; + /// Implementation version of the architecture. + 23:20 implementation; + /// Major revision of the chip. + 19:16 major_revision; + /// Minor revision of the chip. + 15:12 minor_revision; + } +} impl NV_PMC_BOOT_0 { pub(crate) fn is_older_than_fermi(self) -> bool { // From https://github.com/NVIDIA/open-gpu-doc/tree/master/manuals : - const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u8 = 0xc; + const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u32 = 0xc; // Older chips left arch1 zeroed out. That, combined with an arch0 value that is less than // GF100, means "older than Fermi". @@ -56,13 +77,6 @@ pub(crate) fn is_older_than_fermi(self) -> bool { } } -register!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture information" { - 15:12 minor_revision as u8, "Minor revision of the chip"; - 19:16 major_revision as u8, "Major revision of the chip"; - 23:20 implementation as u8, "Implementation version of the architecture"; - 29:24 architecture as u8 ?=> Architecture, "Architecture value"; -}); - impl NV_PMC_BOOT_42 { /// Combines `architecture` and `implementation` to obtain a code unique to the chipset. pub(crate) fn chipset(self) -> Result { @@ -76,8 +90,8 @@ pub(crate) fn chipset(self) -> Result { /// Returns the raw architecture value from the register. fn architecture_raw(self) -> u8 { - ((self.0 >> Self::ARCHITECTURE_RANGE.start()) & ((1 << Self::ARCHITECTURE_RANGE.len()) - 1)) - as u8 + ((self.into_raw() >> Self::ARCHITECTURE_RANGE.start()) + & ((1 << Self::ARCHITECTURE_RANGE.len()) - 1)) as u8 } } @@ -86,7 +100,7 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { write!( f, "boot42 = 0x{:08x} (architecture 0x{:x}, implementation 0x{:x})", - self.0, + self.inner, self.architecture_raw(), self.implementation() ) From 4e7588dcb0a7fef0e709f6907fc42bb7d7458038 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:16 +0900 Subject: [PATCH 286/712] gpu: nova-core: convert PBUS registers to kernel register macro Convert all PBUS registers to use the kernel's register macro and update the code accordingly. Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-3-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp/boot.rs | 5 ++++- drivers/gpu/nova-core/regs.rs | 12 +++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index e55210ebb6d1..3a0124818956 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -4,6 +4,7 @@ device, dma::Coherent, io::poll::read_poll_timeout, + io::Io, pci, prelude::*, time::Delta, // @@ -86,7 +87,9 @@ fn run_fwsec_frts( } // SCRATCH_E contains the error code for FWSEC-FRTS. - let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code(); + let frts_status = bar + .read(regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR) + .frts_err_code(); if frts_status != 0 { dev_err!( dev, diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 58fb807605dd..533d912659ba 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -109,12 +109,14 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { // PBUS -register!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {}); +io::register! { + pub(crate) NV_PBUS_SW_SCRATCH(u32)[64] @ 0x00001400 {} -register!(NV_PBUS_SW_SCRATCH_0E_FRTS_ERR => NV_PBUS_SW_SCRATCH[0xe], - "scratch register 0xe used as FRTS firmware error code" { - 31:16 frts_err_code as u16; -}); + /// Scratch register 0xe used as FRTS firmware error code. + pub(crate) NV_PBUS_SW_SCRATCH_0E_FRTS_ERR(u32) => NV_PBUS_SW_SCRATCH[0xe] { + 31:16 frts_err_code; + } +} // PFB From 797385890759d6a011ccd7a028eed6c43142450b Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:17 +0900 Subject: [PATCH 287/712] gpu: nova-core: convert PFB registers to kernel register macro Convert all PFB registers to use the kernel's register macro and update the code accordingly. NV_PGSP_QUEUE_HEAD was somehow caught in the PFB section, so move it to its own section and convert it as well. Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-4-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/fb/hal/ga100.rs | 34 ++++++++++------ drivers/gpu/nova-core/fb/hal/tu102.rs | 14 ++++--- drivers/gpu/nova-core/gsp/boot.rs | 6 +-- drivers/gpu/nova-core/gsp/cmdq.rs | 9 +++-- drivers/gpu/nova-core/regs.rs | 57 ++++++++++++++++----------- 5 files changed, 70 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/nova-core/fb/hal/ga100.rs b/drivers/gpu/nova-core/fb/hal/ga100.rs index e0acc41aa7cd..629588c75778 100644 --- a/drivers/gpu/nova-core/fb/hal/ga100.rs +++ b/drivers/gpu/nova-core/fb/hal/ga100.rs @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 -use kernel::prelude::*; +use kernel::{ + io::Io, + num::Bounded, + prelude::*, // +}; use crate::{ driver::Bar0, @@ -13,22 +17,26 @@ struct Ga100; pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) -> u64 { - u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT - | u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::read(bar).adr_63_40()) + u64::from(bar.read(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT + | u64::from(bar.read(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI).adr_63_40()) << FLUSH_SYSMEM_ADDR_SHIFT_HI } pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) { - regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::default() - // CAST: `as u32` is used on purpose since the remaining bits are guaranteed to fit within - // a `u32`. - .set_adr_63_40((addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI) as u32) - .write(bar); - regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default() - // CAST: `as u32` is used on purpose since we want to strip the upper bits that have been - // written to `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`. - .set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32) - .write(bar); + bar.write_reg( + regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr_63_40( + Bounded::::from(addr) + .shr::() + .cast(), + ), + ); + + bar.write_reg( + regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::zeroed() + // CAST: `as u32` is used on purpose since we want to strip the upper bits that have + // been written to `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`. + .with_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32), + ); } pub(super) fn display_enabled_ga100(bar: &Bar0) -> bool { diff --git a/drivers/gpu/nova-core/fb/hal/tu102.rs b/drivers/gpu/nova-core/fb/hal/tu102.rs index eec984f4e816..515d50872224 100644 --- a/drivers/gpu/nova-core/fb/hal/tu102.rs +++ b/drivers/gpu/nova-core/fb/hal/tu102.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 -use kernel::prelude::*; +use kernel::{ + io::Io, + prelude::*, // +}; use crate::{ driver::Bar0, @@ -13,7 +16,7 @@ pub(super) const FLUSH_SYSMEM_ADDR_SHIFT: u32 = 8; pub(super) fn read_sysmem_flush_page_gm107(bar: &Bar0) -> u64 { - u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT + u64::from(bar.read(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT } pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result { @@ -21,9 +24,7 @@ pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result { u32::try_from(addr >> FLUSH_SYSMEM_ADDR_SHIFT) .map_err(|_| EINVAL) .map(|addr| { - regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default() - .set_adr_39_08(addr) - .write(bar) + bar.write_reg(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::zeroed().with_adr_39_08(addr)) }) } @@ -32,7 +33,8 @@ pub(super) fn display_enabled_gm107(bar: &Bar0) -> bool { } pub(super) fn vidmem_size_gp102(bar: &Bar0) -> u64 { - regs::NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE::read(bar).usable_fb_size() + bar.read(regs::NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE) + .usable_fb_size() } struct Tu102; diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 3a0124818956..6f707b3d1a54 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -57,7 +57,7 @@ fn run_fwsec_frts( ) -> Result<()> { // Check that the WPR2 region does not already exists - if it does, we cannot run // FWSEC-FRTS until the GPU is reset. - if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 { + if bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound() != 0 { dev_err!( dev, "WPR2 region already exists - GPU needs to be reset to proceed\n" @@ -102,8 +102,8 @@ fn run_fwsec_frts( // Check that the WPR2 region has been created as we requested. let (wpr2_lo, wpr2_hi) = ( - regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(), - regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(), + bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO).lower_bound(), + bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound(), ); match (wpr2_lo, wpr2_hi) { diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index c853be23e3a5..2224896ccc89 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -11,7 +11,10 @@ DmaAddress, // }, dma_write, - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + Io, // + }, new_mutex, prelude::*, sync::{ @@ -509,9 +512,7 @@ fn calculate_checksum>(it: T) -> u32 { /// Notifies the GSP that we have updated the command queue pointers. fn notify_gsp(bar: &Bar0) { - regs::NV_PGSP_QUEUE_HEAD::default() - .set_address(0) - .write(bar); + bar.write_reg(regs::NV_PGSP_QUEUE_HEAD::zeroed().with_address(0u32)); } /// Sends `command` to the GSP and waits for the reply. diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 533d912659ba..4f5cd64c2fce 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -120,26 +120,35 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { // PFB -// The following two registers together hold the physical system memory address that is used by the -// GPU to perform sysmembar operations (see `fb::SysmemFlush`). +io::register! { + /// Low bits of the physical system memory address used by the GPU to perform sysmembar + /// operations (see [`crate::fb::SysmemFlush`]). + pub(crate) NV_PFB_NISO_FLUSH_SYSMEM_ADDR(u32) @ 0x00100c10 { + 31:0 adr_39_08; + } -register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR @ 0x00100c10 { - 31:0 adr_39_08 as u32; -}); + /// High bits of the physical system memory address used by the GPU to perform sysmembar + /// operations (see [`crate::fb::SysmemFlush`]). + pub(crate) NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI(u32) @ 0x00100c40 { + 23:0 adr_63_40; + } -register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI @ 0x00100c40 { - 23:0 adr_63_40 as u32; -}); + pub(crate) NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE(u32) @ 0x00100ce0 { + 30:30 ecc_mode_enabled => bool; + 9:4 lower_mag; + 3:0 lower_scale; + } -register!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 { - 3:0 lower_scale as u8; - 9:4 lower_mag as u8; - 30:30 ecc_mode_enabled as bool; -}); + pub(crate) NV_PFB_PRI_MMU_WPR2_ADDR_LO(u32) @ 0x001fa824 { + /// Bits 12..40 of the lower (inclusive) bound of the WPR2 region. + 31:4 lo_val; + } -register!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 { - 31:0 address as u32; -}); + pub(crate) NV_PFB_PRI_MMU_WPR2_ADDR_HI(u32) @ 0x001fa828 { + /// Bits 12..40 of the higher (exclusive) bound of the WPR2 region. + 31:4 hi_val; + } +} impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE { /// Returns the usable framebuffer size, in bytes. @@ -156,10 +165,6 @@ pub(crate) fn usable_fb_size(self) -> u64 { } } -register!(NV_PFB_PRI_MMU_WPR2_ADDR_LO@0x001fa824 { - 31:4 lo_val as u32, "Bits 12..40 of the lower (inclusive) bound of the WPR2 region"; -}); - impl NV_PFB_PRI_MMU_WPR2_ADDR_LO { /// Returns the lower (inclusive) bound of the WPR2 region. pub(crate) fn lower_bound(self) -> u64 { @@ -167,10 +172,6 @@ pub(crate) fn lower_bound(self) -> u64 { } } -register!(NV_PFB_PRI_MMU_WPR2_ADDR_HI@0x001fa828 { - 31:4 hi_val as u32, "Bits 12..40 of the higher (exclusive) bound of the WPR2 region"; -}); - impl NV_PFB_PRI_MMU_WPR2_ADDR_HI { /// Returns the higher (exclusive) bound of the WPR2 region. /// @@ -180,6 +181,14 @@ pub(crate) fn higher_bound(self) -> u64 { } } +// PGSP + +io::register! { + pub(crate) NV_PGSP_QUEUE_HEAD(u32) @ 0x00110c00 { + 31:0 address; + } +} + // PGC6 register space. // // `GC6` is a GPU low-power state where VRAM is in self-refresh and the GPU is powered down (except From ffabad08e46e425781a5d3a7f9e6a64c12e36de2 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:18 +0900 Subject: [PATCH 288/712] gpu: nova-core: convert GC6 registers to kernel register macro Convert all GC6 registers to use the kernel's register macro and update the code accordingly. Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-5-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon/gsp.rs | 7 ++- drivers/gpu/nova-core/fb/hal/ga102.rs | 7 ++- drivers/gpu/nova-core/gfw.rs | 11 +++-- drivers/gpu/nova-core/regs.rs | 67 ++++++++++++++------------- 4 files changed, 52 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs index 67edef3636c1..e52f57abc223 100644 --- a/drivers/gpu/nova-core/falcon/gsp.rs +++ b/drivers/gpu/nova-core/falcon/gsp.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 use kernel::{ - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + Io, // + }, prelude::*, time::Delta, // }; @@ -47,7 +50,7 @@ pub(crate) fn clear_swgen0_intr(&self, bar: &Bar0) { /// Checks if GSP reload/resume has completed during the boot process. pub(crate) fn check_reload_completed(&self, bar: &Bar0, timeout: Delta) -> Result { read_poll_timeout( - || Ok(regs::NV_PGC6_BSI_SECURE_SCRATCH_14::read(bar)), + || Ok(bar.read(regs::NV_PGC6_BSI_SECURE_SCRATCH_14)), |val| val.boot_stage_3_handoff(), Delta::ZERO, timeout, diff --git a/drivers/gpu/nova-core/fb/hal/ga102.rs b/drivers/gpu/nova-core/fb/hal/ga102.rs index 734605905031..4b9f0f74d0e7 100644 --- a/drivers/gpu/nova-core/fb/hal/ga102.rs +++ b/drivers/gpu/nova-core/fb/hal/ga102.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 -use kernel::prelude::*; +use kernel::{ + io::Io, + prelude::*, // +}; use crate::{ driver::Bar0, @@ -9,7 +12,7 @@ }; fn vidmem_size_ga102(bar: &Bar0) -> u64 { - regs::NV_USABLE_FB_SIZE_IN_MB::read(bar).usable_fb_size() + bar.read(regs::NV_USABLE_FB_SIZE_IN_MB).usable_fb_size() } struct Ga102; diff --git a/drivers/gpu/nova-core/gfw.rs b/drivers/gpu/nova-core/gfw.rs index 9121f400046d..fb75dd10a172 100644 --- a/drivers/gpu/nova-core/gfw.rs +++ b/drivers/gpu/nova-core/gfw.rs @@ -19,7 +19,10 @@ //! Note that the devinit sequence also needs to run during suspend/resume. use kernel::{ - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + Io, // + }, prelude::*, time::Delta, // }; @@ -58,9 +61,11 @@ pub(crate) fn wait_gfw_boot_completion(bar: &Bar0) -> Result { Ok( // Check that FWSEC has lowered its protection level before reading the GFW_BOOT // status. - regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK::read(bar) + bar.read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK) .read_protection_level0() - && regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT::read(bar).completed(), + && bar + .read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT) + .completed(), ) }, |&gfw_booted| gfw_booted, diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 4f5cd64c2fce..6f49467e78ec 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -198,29 +198,41 @@ pub(crate) fn higher_bound(self) -> u64 { // These scratch registers remain powered on even in a low-power state and have a designated group // number. -// Boot Sequence Interface (BSI) register used to determine -// if GSP reload/resume has completed during the boot process. -register!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 { - 26:26 boot_stage_3_handoff as bool; -}); - -// Privilege level mask register. It dictates whether the host CPU has privilege to access the -// `PGC6_AON_SECURE_SCRATCH_GROUP_05` register (which it needs to read GFW_BOOT). -register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128, - "Privilege level mask register" { - 0:0 read_protection_level0 as bool, "Set after FWSEC lowers its protection level"; -}); - -// OpenRM defines this as a register array, but doesn't specify its size and only uses its first -// element. Be conservative until we know the actual size or need to use more registers. -register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234[1] {}); - -register!( - NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT => NV_PGC6_AON_SECURE_SCRATCH_GROUP_05[0], - "Scratch group 05 register 0 used as GFW boot progress indicator" { - 7:0 progress as u8, "Progress of GFW boot (0xff means completed)"; +io::register! { + /// Boot Sequence Interface (BSI) register used to determine + /// if GSP reload/resume has completed during the boot process. + pub(crate) NV_PGC6_BSI_SECURE_SCRATCH_14(u32) @ 0x001180f8 { + 26:26 boot_stage_3_handoff => bool; } -); + + /// Privilege level mask register. It dictates whether the host CPU has privilege to access the + /// `PGC6_AON_SECURE_SCRATCH_GROUP_05` register (which it needs to read GFW_BOOT). + pub(crate) NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK(u32) @ 0x00118128 { + /// Set after FWSEC lowers its protection level. + 0:0 read_protection_level0 => bool; + } + + /// OpenRM defines this as a register array, but doesn't specify its size and only uses its + /// first element. Be conservative until we know the actual size or need to use more registers. + pub(crate) NV_PGC6_AON_SECURE_SCRATCH_GROUP_05(u32)[1] @ 0x00118234 {} + + /// Scratch group 05 register 0 used as GFW boot progress indicator. + pub(crate) NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT(u32) + => NV_PGC6_AON_SECURE_SCRATCH_GROUP_05[0] { + /// Progress of GFW boot (0xff means completed). + 7:0 progress; + } + + pub(crate) NV_PGC6_AON_SECURE_SCRATCH_GROUP_42(u32) @ 0x001183a4 { + 31:0 value; + } + + /// Scratch group 42 register used as framebuffer size. + pub(crate) NV_USABLE_FB_SIZE_IN_MB(u32) => NV_PGC6_AON_SECURE_SCRATCH_GROUP_42 { + /// Usable framebuffer size, in megabytes. + 31:0 value; + } +} impl NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT { /// Returns `true` if GFW boot is completed. @@ -229,17 +241,6 @@ pub(crate) fn completed(self) -> bool { } } -register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_42 @ 0x001183a4 { - 31:0 value as u32; -}); - -register!( - NV_USABLE_FB_SIZE_IN_MB => NV_PGC6_AON_SECURE_SCRATCH_GROUP_42, - "Scratch group 42 register used as framebuffer size" { - 31:0 value as u32, "Usable framebuffer size, in megabytes"; - } -); - impl NV_USABLE_FB_SIZE_IN_MB { /// Returns the usable framebuffer size, in bytes. pub(crate) fn usable_fb_size(self) -> u64 { From 1a8f58c5e125d61c597d420237750d2dcea32ce8 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:19 +0900 Subject: [PATCH 289/712] gpu: nova-core: convert FUSE registers to kernel register macro Convert all FUSE registers to use the kernel's register macro and update the code accordingly. Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-6-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon/hal/ga102.rs | 17 +++++++--- drivers/gpu/nova-core/fb/hal/ga100.rs | 3 +- drivers/gpu/nova-core/fb/hal/tu102.rs | 3 +- drivers/gpu/nova-core/regs.rs | 40 ++++++++++++++--------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index 8f62df10da0a..cbdf36bad633 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -4,7 +4,11 @@ use kernel::{ device, - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + register::Array, + Io, // + }, prelude::*, time::Delta, // }; @@ -60,12 +64,15 @@ fn signature_reg_fuse_version_ga102( // `ucode_idx` is guaranteed to be in the range [0..15], making the `read` calls provable valid // at build-time. - let reg_fuse_version = if engine_id_mask & 0x0001 != 0 { - regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::read(bar, ucode_idx).data() + let reg_fuse_version: u16 = if engine_id_mask & 0x0001 != 0 { + bar.read(regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::at(ucode_idx)) + .data() } else if engine_id_mask & 0x0004 != 0 { - regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::read(bar, ucode_idx).data() + bar.read(regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::at(ucode_idx)) + .data() } else if engine_id_mask & 0x0400 != 0 { - regs::NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION::read(bar, ucode_idx).data() + bar.read(regs::NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION::at(ucode_idx)) + .data() } else { dev_err!(dev, "unexpected engine_id_mask {:#x}\n", engine_id_mask); return Err(EINVAL); diff --git a/drivers/gpu/nova-core/fb/hal/ga100.rs b/drivers/gpu/nova-core/fb/hal/ga100.rs index 629588c75778..1c03783cddef 100644 --- a/drivers/gpu/nova-core/fb/hal/ga100.rs +++ b/drivers/gpu/nova-core/fb/hal/ga100.rs @@ -40,7 +40,8 @@ pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) { } pub(super) fn display_enabled_ga100(bar: &Bar0) -> bool { - !regs::ga100::NV_FUSE_STATUS_OPT_DISPLAY::read(bar).display_disabled() + !bar.read(regs::ga100::NV_FUSE_STATUS_OPT_DISPLAY) + .display_disabled() } /// Shift applied to the sysmem address before it is written into diff --git a/drivers/gpu/nova-core/fb/hal/tu102.rs b/drivers/gpu/nova-core/fb/hal/tu102.rs index 515d50872224..281bb796e198 100644 --- a/drivers/gpu/nova-core/fb/hal/tu102.rs +++ b/drivers/gpu/nova-core/fb/hal/tu102.rs @@ -29,7 +29,8 @@ pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result { } pub(super) fn display_enabled_gm107(bar: &Bar0) -> bool { - !regs::gm107::NV_FUSE_STATUS_OPT_DISPLAY::read(bar).display_disabled() + !bar.read(regs::gm107::NV_FUSE_STATUS_OPT_DISPLAY) + .display_disabled() } pub(super) fn vidmem_size_gp102(bar: &Bar0) -> u64 { diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 6f49467e78ec..61a8dba22d88 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -270,17 +270,19 @@ pub(crate) fn vga_workspace_addr(self) -> Option { pub(crate) const NV_FUSE_OPT_FPF_SIZE: usize = 16; -register!(NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION @ 0x00824100[NV_FUSE_OPT_FPF_SIZE] { - 15:0 data as u16; -}); +io::register! { + pub(crate) NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION(u32)[NV_FUSE_OPT_FPF_SIZE] @ 0x00824100 { + 15:0 data => u16; + } -register!(NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION @ 0x00824140[NV_FUSE_OPT_FPF_SIZE] { - 15:0 data as u16; -}); + pub(crate) NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION(u32)[NV_FUSE_OPT_FPF_SIZE] @ 0x00824140 { + 15:0 data => u16; + } -register!(NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION @ 0x008241c0[NV_FUSE_OPT_FPF_SIZE] { - 15:0 data as u16; -}); + pub(crate) NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION(u32)[NV_FUSE_OPT_FPF_SIZE] @ 0x008241c0 { + 15:0 data => u16; + } +} // PFALCON @@ -491,17 +493,25 @@ pub(crate) fn reset_engine(bar: &Bar0) { // only be used in HAL modules. pub(crate) mod gm107 { + use kernel::io; + // FUSE - register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 { - 0:0 display_disabled as bool; - }); + io::register! { + pub(crate) NV_FUSE_STATUS_OPT_DISPLAY(u32) @ 0x00021c04 { + 0:0 display_disabled => bool; + } + } } pub(crate) mod ga100 { + use kernel::io; + // FUSE - register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 { - 0:0 display_disabled as bool; - }); + io::register! { + pub(crate) NV_FUSE_STATUS_OPT_DISPLAY(u32) @ 0x00820c04 { + 0:0 display_disabled => bool; + } + } } From 02ade2557eba91143f56837593ed821da4144e82 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:20 +0900 Subject: [PATCH 290/712] gpu: nova-core: convert PDISP registers to kernel register macro Convert all PDISP registers to use the kernel's register macro and update the code accordingly. Reviewed-by: Eliot Courtney Reviewed-by: Joel Fernandes Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-7-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/fb.rs | 6 +++++- drivers/gpu/nova-core/regs.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index 6536d0035cb1..62fc90fa6a84 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -8,6 +8,7 @@ use kernel::{ device, fmt, + io::Io, prelude::*, ptr::{ Alignable, @@ -189,7 +190,10 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result< let base = fb.end - NV_PRAMIN_SIZE; if hal.supports_display(bar) { - match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { + match bar + .read(regs::NV_PDISP_VGA_WORKSPACE_BASE) + .vga_workspace_addr() + { Some(addr) => { if addr < base { const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K); diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 61a8dba22d88..b051d5568cd8 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -250,10 +250,14 @@ pub(crate) fn usable_fb_size(self) -> u64 { // PDISP -register!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 { - 3:3 status_valid as bool, "Set if the `addr` field is valid"; - 31:8 addr as u32, "VGA workspace base address divided by 0x10000"; -}); +io::register! { + pub(crate) NV_PDISP_VGA_WORKSPACE_BASE(u32) @ 0x00625f04 { + /// VGA workspace base address divided by 0x10000. + 31:8 addr; + /// Set if the `addr` field is valid. + 3:3 status_valid => bool; + } +} impl NV_PDISP_VGA_WORKSPACE_BASE { /// Returns the base address of the VGA workspace, or `None` if none exists. From 38f7e5450ebfc6f2e046a249a3f629ea7bec8c31 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:21 +0900 Subject: [PATCH 291/712] gpu: nova-core: convert falcon registers to kernel register macro Convert all PFALCON, PFALCON2 and PRISCV registers to use the kernel's register macro and update the code accordingly. Because they rely on the same types to implement relative registers, they need to be updated in lockstep. nova-core's local register macro is now unused, so remove it. Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-8-bdf172f0f6ca@nvidia.com [acourbot@nvidia.com: remove unused import.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 520 +++++------- drivers/gpu/nova-core/falcon/gsp.rs | 22 +- drivers/gpu/nova-core/falcon/hal/ga102.rs | 55 +- drivers/gpu/nova-core/falcon/hal/tu102.rs | 12 +- drivers/gpu/nova-core/falcon/sec2.rs | 17 +- .../nova-core/firmware/fwsec/bootloader.rs | 19 +- drivers/gpu/nova-core/regs.rs | 353 +++++---- drivers/gpu/nova-core/regs/macros.rs | 739 ------------------ 8 files changed, 456 insertions(+), 1281 deletions(-) delete mode 100644 drivers/gpu/nova-core/regs/macros.rs diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 123de6c55b45..c49ec6ded909 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -15,7 +15,11 @@ }, io::{ poll::read_poll_timeout, - Io, // + register::{ + RegisterBase, + WithBase, // + }, + Io, }, prelude::*, sync::aref::ARef, @@ -23,6 +27,7 @@ }; use crate::{ + bounded_enum, dma::DmaObject, driver::Bar0, falcon::hal::LoadMethod, @@ -32,7 +37,6 @@ FromSafeCast, // }, regs, - regs::macros::RegisterBase, // }; pub(crate) mod gsp; @@ -42,208 +46,91 @@ /// Alignment (in bytes) of falcon memory blocks. pub(crate) const MEM_BLOCK_ALIGNMENT: usize = 256; -// TODO[FPRI]: Replace with `ToPrimitive`. -macro_rules! impl_from_enum_to_u8 { - ($enum_type:ty) => { - impl From<$enum_type> for u8 { - fn from(value: $enum_type) -> Self { - value as u8 - } - } - }; -} - -/// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] -/// register. -#[repr(u8)] -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) enum FalconCoreRev { - #[default] - Rev1 = 1, - Rev2 = 2, - Rev3 = 3, - Rev4 = 4, - Rev5 = 5, - Rev6 = 6, - Rev7 = 7, -} -impl_from_enum_to_u8!(FalconCoreRev); - -// TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconCoreRev { - type Error = Error; - - fn try_from(value: u8) -> Result { - use FalconCoreRev::*; - - let rev = match value { - 1 => Rev1, - 2 => Rev2, - 3 => Rev3, - 4 => Rev4, - 5 => Rev5, - 6 => Rev6, - 7 => Rev7, - _ => return Err(EINVAL), - }; - - Ok(rev) +bounded_enum! { + /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] + /// register. + #[derive(Debug, Copy, Clone)] + pub(crate) enum FalconCoreRev with TryFrom> { + Rev1 = 1, + Rev2 = 2, + Rev3 = 3, + Rev4 = 4, + Rev5 = 5, + Rev6 = 6, + Rev7 = 7, } } -/// Revision subversion number of a falcon core, used in the -/// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register. -#[repr(u8)] -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) enum FalconCoreRevSubversion { - #[default] - Subversion0 = 0, - Subversion1 = 1, - Subversion2 = 2, - Subversion3 = 3, -} -impl_from_enum_to_u8!(FalconCoreRevSubversion); - -// TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconCoreRevSubversion { - type Error = Error; - - fn try_from(value: u8) -> Result { - use FalconCoreRevSubversion::*; - - let sub_version = match value & 0b11 { - 0 => Subversion0, - 1 => Subversion1, - 2 => Subversion2, - 3 => Subversion3, - _ => return Err(EINVAL), - }; - - Ok(sub_version) +bounded_enum! { + /// Revision subversion number of a falcon core, used in the + /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register. + #[derive(Debug, Copy, Clone)] + pub(crate) enum FalconCoreRevSubversion with From> { + Subversion0 = 0, + Subversion1 = 1, + Subversion2 = 2, + Subversion3 = 3, } } -/// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] -/// register. -#[repr(u8)] -#[derive(Debug, Default, Copy, Clone)] -/// Security mode of the Falcon microprocessor. -/// -/// See `falcon.rst` for more details. -pub(crate) enum FalconSecurityModel { - /// Non-Secure: runs unsigned code without privileges. - #[default] - None = 0, - /// Light-Secured (LS): Runs signed code with some privileges. - /// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the code's - /// signature. +bounded_enum! { + /// Security mode of the Falcon microprocessor. /// - /// Also known as Low-Secure, Privilege Level 2 or PL2. - Light = 2, - /// Heavy-Secured (HS): Runs signed code with full privileges. - /// The code's signature is verified by the Falcon Boot ROM (BROM). - /// - /// Also known as High-Secure, Privilege Level 3 or PL3. - Heavy = 3, -} -impl_from_enum_to_u8!(FalconSecurityModel); - -// TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconSecurityModel { - type Error = Error; - - fn try_from(value: u8) -> Result { - use FalconSecurityModel::*; - - let sec_model = match value { - 0 => None, - 2 => Light, - 3 => Heavy, - _ => return Err(EINVAL), - }; - - Ok(sec_model) + /// See `falcon.rst` for more details. + #[derive(Debug, Copy, Clone)] + pub(crate) enum FalconSecurityModel with TryFrom> { + /// Non-Secure: runs unsigned code without privileges. + None = 0, + /// Light-Secured (LS): Runs signed code with some privileges. + /// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the + /// code's signature. + /// + /// Also known as Low-Secure, Privilege Level 2 or PL2. + Light = 2, + /// Heavy-Secured (HS): Runs signed code with full privileges. + /// The code's signature is verified by the Falcon Boot ROM (BROM). + /// + /// Also known as High-Secure, Privilege Level 3 or PL3. + Heavy = 3, } } -/// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`] -/// register. It is passed to the Falcon Boot ROM (BROM) as a parameter. -#[repr(u8)] -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] -pub(crate) enum FalconModSelAlgo { - /// AES. - #[expect(dead_code)] - Aes = 0, - /// RSA3K. - #[default] - Rsa3k = 1, -} -impl_from_enum_to_u8!(FalconModSelAlgo); - -// TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconModSelAlgo { - type Error = Error; - - fn try_from(value: u8) -> Result { - match value { - 1 => Ok(FalconModSelAlgo::Rsa3k), - _ => Err(EINVAL), - } +bounded_enum! { + /// Signing algorithm for a given firmware, used in the + /// [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`] register. It is passed to the Falcon Boot ROM + /// (BROM) as a parameter. + #[derive(Debug, Copy, Clone)] + pub(crate) enum FalconModSelAlgo with TryFrom> { + /// AES. + Aes = 0, + /// RSA3K. + Rsa3k = 1, } } -/// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register. -#[repr(u8)] -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] -pub(crate) enum DmaTrfCmdSize { - /// 256 bytes transfer. - #[default] - Size256B = 0x6, -} -impl_from_enum_to_u8!(DmaTrfCmdSize); - -// TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for DmaTrfCmdSize { - type Error = Error; - - fn try_from(value: u8) -> Result { - match value { - 0x6 => Ok(Self::Size256B), - _ => Err(EINVAL), - } +bounded_enum! { + /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] + /// register. + #[derive(Debug, Copy, Clone)] + pub(crate) enum DmaTrfCmdSize with TryFrom> { + /// 256 bytes transfer. + Size256B = 0x6, } } -/// Currently active core on a dual falcon/riscv (Peregrine) controller. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub(crate) enum PeregrineCoreSelect { - /// Falcon core is active. - #[default] - Falcon = 0, - /// RISC-V core is active. - Riscv = 1, -} - -impl From for PeregrineCoreSelect { - fn from(value: bool) -> Self { - match value { - false => PeregrineCoreSelect::Falcon, - true => PeregrineCoreSelect::Riscv, - } - } -} - -impl From for bool { - fn from(value: PeregrineCoreSelect) -> Self { - match value { - PeregrineCoreSelect::Falcon => false, - PeregrineCoreSelect::Riscv => true, - } +bounded_enum! { + /// Currently active core on a dual falcon/riscv (Peregrine) controller. + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub(crate) enum PeregrineCoreSelect with From> { + /// Falcon core is active. + Falcon = 0, + /// RISC-V core is active. + Riscv = 1, } } /// Different types of memory present in a falcon core. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) enum FalconMem { /// Secure Instruction Memory. ImemSecure, @@ -254,64 +141,29 @@ pub(crate) enum FalconMem { Dmem, } -/// Defines the Framebuffer Interface (FBIF) aperture type. -/// This determines the memory type for external memory access during a DMA transfer, which is -/// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details. -#[derive(Debug, Clone, Default)] -pub(crate) enum FalconFbifTarget { - /// VRAM. - #[default] - /// Local Framebuffer (GPU's VRAM memory). - LocalFb = 0, - /// Coherent system memory (System DRAM). - CoherentSysmem = 1, - /// Non-coherent system memory (System DRAM). - NoncoherentSysmem = 2, -} -impl_from_enum_to_u8!(FalconFbifTarget); - -// TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconFbifTarget { - type Error = Error; - - fn try_from(value: u8) -> Result { - let res = match value { - 0 => Self::LocalFb, - 1 => Self::CoherentSysmem, - 2 => Self::NoncoherentSysmem, - _ => return Err(EINVAL), - }; - - Ok(res) +bounded_enum! { + /// Defines the Framebuffer Interface (FBIF) aperture type. + /// This determines the memory type for external memory access during a DMA transfer, which is + /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details. + #[derive(Debug, Copy, Clone)] + pub(crate) enum FalconFbifTarget with TryFrom> { + /// Local Framebuffer (GPU's VRAM memory). + LocalFb = 0, + /// Coherent system memory (System DRAM). + CoherentSysmem = 1, + /// Non-coherent system memory (System DRAM). + NoncoherentSysmem = 2, } } -/// Type of memory addresses to use. -#[derive(Debug, Clone, Default)] -pub(crate) enum FalconFbifMemType { - /// Virtual memory addresses. - #[default] - Virtual = 0, - /// Physical memory addresses. - Physical = 1, -} - -/// Conversion from a single-bit register field. -impl From for FalconFbifMemType { - fn from(value: bool) -> Self { - match value { - false => Self::Virtual, - true => Self::Physical, - } - } -} - -impl From for bool { - fn from(value: FalconFbifMemType) -> Self { - match value { - FalconFbifMemType::Virtual => false, - FalconFbifMemType::Physical => true, - } +bounded_enum! { + /// Type of memory addresses to use. + #[derive(Debug, Copy, Clone)] + pub(crate) enum FalconFbifMemType with From> { + /// Virtual memory addresses. + Virtual = 0, + /// Physical memory addresses. + Physical = 1, } } @@ -323,13 +175,10 @@ fn from(value: FalconFbifMemType) -> Self { /// Trait defining the parameters of a given Falcon engine. /// -/// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The `ID` constant is used -/// to identify a given Falcon instance with register I/O methods. +/// Each engine provides one base for `PFALCON` and `PFALCON2` registers. pub(crate) trait FalconEngine: Send + Sync + RegisterBase + RegisterBase + Sized { - /// Singleton of the engine, used to identify it with register I/O methods. - const ID: Self; } /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM) @@ -523,8 +372,14 @@ pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result { /// Resets DMA-related registers. pub(crate) fn dma_reset(&self, bar: &Bar0) { - regs::NV_PFALCON_FBIF_CTL::update(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true)); - regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); + bar.update(regs::NV_PFALCON_FBIF_CTL::of::(), |v| { + v.with_allow_phys_no_ctx(true) + }); + + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_DMACTL::zeroed(), + ); } /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete. @@ -533,9 +388,10 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result { self.hal.select_core(self, bar)?; self.hal.reset_wait_mem_scrubbing(bar)?; - regs::NV_PFALCON_FALCON_RM::default() - .set_value(bar.read(regs::NV_PMC_BOOT_0).into()) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_RM::from(bar.read(regs::NV_PMC_BOOT_0).into_raw()), + ); Ok(()) } @@ -553,25 +409,27 @@ fn pio_wr_imem_slice(&self, bar: &Bar0, load_offsets: FalconPioImemLoadTarget<'_ return Err(EINVAL); } - regs::NV_PFALCON_FALCON_IMEMC::default() - .set_secure(load_offsets.secure) - .set_aincw(true) - .set_offs(load_offsets.dst_start) - .write(bar, &E::ID, Self::PIO_PORT); + bar.write( + WithBase::of::().at(Self::PIO_PORT), + regs::NV_PFALCON_FALCON_IMEMC::zeroed() + .with_secure(load_offsets.secure) + .with_aincw(true) + .with_offs(load_offsets.dst_start), + ); for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() { let n = u16::try_from(n)?; let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?; - regs::NV_PFALCON_FALCON_IMEMT::default().set_tag(tag).write( - bar, - &E::ID, - Self::PIO_PORT, + bar.write( + WithBase::of::().at(Self::PIO_PORT), + regs::NV_PFALCON_FALCON_IMEMT::zeroed().with_tag(tag), ); for word in block.chunks_exact(4) { let w = [word[0], word[1], word[2], word[3]]; - regs::NV_PFALCON_FALCON_IMEMD::default() - .set_data(u32::from_le_bytes(w)) - .write(bar, &E::ID, Self::PIO_PORT); + bar.write( + WithBase::of::().at(Self::PIO_PORT), + regs::NV_PFALCON_FALCON_IMEMD::zeroed().with_data(u32::from_le_bytes(w)), + ); } } @@ -588,16 +446,19 @@ fn pio_wr_dmem_slice(&self, bar: &Bar0, load_offsets: FalconPioDmemLoadTarget<'_ return Err(EINVAL); } - regs::NV_PFALCON_FALCON_DMEMC::default() - .set_aincw(true) - .set_offs(load_offsets.dst_start) - .write(bar, &E::ID, Self::PIO_PORT); + bar.write( + WithBase::of::().at(Self::PIO_PORT), + regs::NV_PFALCON_FALCON_DMEMC::zeroed() + .with_aincw(true) + .with_offs(load_offsets.dst_start), + ); for word in load_offsets.data.chunks_exact(4) { let w = [word[0], word[1], word[2], word[3]]; - regs::NV_PFALCON_FALCON_DMEMD::default() - .set_data(u32::from_le_bytes(w)) - .write(bar, &E::ID, Self::PIO_PORT); + bar.write( + WithBase::of::().at(Self::PIO_PORT), + regs::NV_PFALCON_FALCON_DMEMD::zeroed().with_data(u32::from_le_bytes(w)), + ); } Ok(()) @@ -609,11 +470,14 @@ pub(crate) fn pio_load + FalconPioLoadable>( bar: &Bar0, fw: &F, ) -> Result { - regs::NV_PFALCON_FBIF_CTL::read(bar, &E::ID) - .set_allow_phys_no_ctx(true) - .write(bar, &E::ID); + bar.update(regs::NV_PFALCON_FBIF_CTL::of::(), |v| { + v.with_allow_phys_no_ctx(true) + }); - regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_DMACTL::zeroed(), + ); if let Some(imem_ns) = fw.imem_ns_load_params() { self.pio_wr_imem_slice(bar, imem_ns)?; @@ -625,9 +489,10 @@ pub(crate) fn pio_load + FalconPioLoadable>( self.hal.program_brom(self, bar, &fw.brom_params())?; - regs::NV_PFALCON_FALCON_BOOTVEC::default() - .set_value(fw.boot_addr()) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()), + ); Ok(()) } @@ -696,36 +561,42 @@ fn dma_wr( // Set up the base source DMA address. - regs::NV_PFALCON_FALCON_DMATRFBASE::default() - // CAST: `as u32` is used on purpose since we do want to strip the upper bits, which - // will be written to `NV_PFALCON_FALCON_DMATRFBASE1`. - .set_base((dma_start >> 8) as u32) - .write(bar, &E::ID); - regs::NV_PFALCON_FALCON_DMATRFBASE1::default() - // CAST: `as u16` is used on purpose since the remaining bits are guaranteed to fit - // within a `u16`. - .set_base((dma_start >> 40) as u16) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base( + // CAST: `as u32` is used on purpose since we do want to strip the upper bits, + // which will be written to `NV_PFALCON_FALCON_DMATRFBASE1`. + (dma_start >> 8) as u32, + ), + ); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_DMATRFBASE1::zeroed().try_with_base(dma_start >> 40)?, + ); - let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() - .set_size(DmaTrfCmdSize::Size256B) + let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::zeroed() + .with_size(DmaTrfCmdSize::Size256B) .with_falcon_mem(target_mem); for pos in (0..num_transfers).map(|i| i * DMA_LEN) { // Perform a transfer of size `DMA_LEN`. - regs::NV_PFALCON_FALCON_DMATRFMOFFS::default() - .set_offs(load_offsets.dst_start + pos) - .write(bar, &E::ID); - regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default() - .set_offs(src_start + pos) - .write(bar, &E::ID); - cmd.write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed() + .try_with_offs(load_offsets.dst_start + pos)?, + ); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_start + pos), + ); + + bar.write(WithBase::of::(), cmd); // Wait for the transfer to complete. // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories // should ever take that long. read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID)), + || Ok(bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::())), |r| r.idle(), Delta::ZERO, Delta::from_secs(2), @@ -746,9 +617,9 @@ fn dma_load + FalconDmaLoadable>( let dma_obj = DmaObject::from_data(dev, fw.as_slice())?; self.dma_reset(bar); - regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| { - v.set_target(FalconFbifTarget::CoherentSysmem) - .set_mem_type(FalconFbifMemType::Physical) + bar.update(regs::NV_PFALCON_FBIF_TRANSCFG::of::().at(0), |v| { + v.with_target(FalconFbifTarget::CoherentSysmem) + .with_mem_type(FalconFbifMemType::Physical) }); self.dma_wr( @@ -762,9 +633,10 @@ fn dma_load + FalconDmaLoadable>( self.hal.program_brom(self, bar, &fw.brom_params())?; // Set `BootVec` to start of non-secure code. - regs::NV_PFALCON_FALCON_BOOTVEC::default() - .set_value(fw.boot_addr()) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()), + ); Ok(()) } @@ -773,7 +645,7 @@ fn dma_load + FalconDmaLoadable>( pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> { // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)), + || Ok(bar.read(regs::NV_PFALCON_FALCON_CPUCTL::of::())), |r| r.halted(), Delta::ZERO, Delta::from_secs(2), @@ -784,13 +656,18 @@ pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> { /// Start the falcon CPU. pub(crate) fn start(&self, bar: &Bar0) -> Result<()> { - match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() { - true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default() - .set_startcpu(true) - .write(bar, &E::ID), - false => regs::NV_PFALCON_FALCON_CPUCTL::default() - .set_startcpu(true) - .write(bar, &E::ID), + match bar + .read(regs::NV_PFALCON_FALCON_CPUCTL::of::()) + .alias_en() + { + true => bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::zeroed().with_startcpu(true), + ), + false => bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_CPUCTL::zeroed().with_startcpu(true), + ), } Ok(()) @@ -799,26 +676,30 @@ pub(crate) fn start(&self, bar: &Bar0) -> Result<()> { /// Writes values to the mailbox registers if provided. pub(crate) fn write_mailboxes(&self, bar: &Bar0, mbox0: Option, mbox1: Option) { if let Some(mbox0) = mbox0 { - regs::NV_PFALCON_FALCON_MAILBOX0::default() - .set_value(mbox0) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_MAILBOX0::zeroed().with_value(mbox0), + ); } if let Some(mbox1) = mbox1 { - regs::NV_PFALCON_FALCON_MAILBOX1::default() - .set_value(mbox1) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_MAILBOX1::zeroed().with_value(mbox1), + ); } } /// Reads the value from `mbox0` register. pub(crate) fn read_mailbox0(&self, bar: &Bar0) -> u32 { - regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value() + bar.read(regs::NV_PFALCON_FALCON_MAILBOX0::of::()) + .value() } /// Reads the value from `mbox1` register. pub(crate) fn read_mailbox1(&self, bar: &Bar0) -> u32 { - regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value() + bar.read(regs::NV_PFALCON_FALCON_MAILBOX1::of::()) + .value() } /// Reads values from both mailbox registers. @@ -883,8 +764,9 @@ pub(crate) fn load + FalconDmaLoadable>( /// Write the application version to the OS register. pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) { - regs::NV_PFALCON_FALCON_OS::default() - .set_value(app_version) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_OS::zeroed().with_value(app_version), + ); } } diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs index e52f57abc223..df6d5a382c7a 100644 --- a/drivers/gpu/nova-core/falcon/gsp.rs +++ b/drivers/gpu/nova-core/falcon/gsp.rs @@ -3,7 +3,11 @@ use kernel::{ io::{ poll::read_poll_timeout, - Io, // + register::{ + RegisterBase, + WithBase, // + }, + Io, }, prelude::*, time::Delta, // @@ -17,10 +21,7 @@ PFalcon2Base, PFalconBase, // }, - regs::{ - self, - macros::RegisterBase, // - }, + regs, }; /// Type specifying the `Gsp` falcon engine. Cannot be instantiated. @@ -34,17 +35,16 @@ impl RegisterBase for Gsp { const BASE: usize = 0x00111000; } -impl FalconEngine for Gsp { - const ID: Self = Gsp(()); -} +impl FalconEngine for Gsp {} impl Falcon { /// Clears the SWGEN0 bit in the Falcon's IRQ status clear register to /// allow GSP to signal CPU for processing new messages in message queue. pub(crate) fn clear_swgen0_intr(&self, bar: &Bar0) { - regs::NV_PFALCON_FALCON_IRQSCLR::default() - .set_swgen0(true) - .write(bar, &Gsp::ID); + bar.write( + WithBase::of::(), + regs::NV_PFALCON_FALCON_IRQSCLR::zeroed().with_swgen0(true), + ); } /// Checks if GSP reload/resume has completed during the boot process. diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index cbdf36bad633..8368a61ddeef 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -6,7 +6,10 @@ device, io::{ poll::read_poll_timeout, - register::Array, + register::{ + Array, + WithBase, // + }, Io, // }, prelude::*, @@ -29,15 +32,16 @@ use super::FalconHal; fn select_core_ga102(bar: &Bar0) -> Result { - let bcr_ctrl = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID); + let bcr_ctrl = bar.read(regs::NV_PRISCV_RISCV_BCR_CTRL::of::()); if bcr_ctrl.core_select() != PeregrineCoreSelect::Falcon { - regs::NV_PRISCV_RISCV_BCR_CTRL::default() - .set_core_select(PeregrineCoreSelect::Falcon) - .write(bar, &E::ID); + bar.write( + WithBase::of::(), + regs::NV_PRISCV_RISCV_BCR_CTRL::zeroed().with_core_select(PeregrineCoreSelect::Falcon), + ); // TIMEOUT: falcon core should take less than 10ms to report being enabled. read_poll_timeout( - || Ok(regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID)), + || Ok(bar.read(regs::NV_PRISCV_RISCV_BCR_CTRL::of::())), |r| r.valid(), Delta::ZERO, Delta::from_millis(10), @@ -83,18 +87,23 @@ fn signature_reg_fuse_version_ga102( } fn program_brom_ga102(bar: &Bar0, params: &FalconBromParams) -> Result { - regs::NV_PFALCON2_FALCON_BROM_PARAADDR::default() - .set_value(params.pkc_data_offset) - .write(bar, &E::ID, 0); - regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::default() - .set_value(u32::from(params.engine_id_mask)) - .write(bar, &E::ID); - regs::NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID::default() - .set_ucode_id(params.ucode_id) - .write(bar, &E::ID); - regs::NV_PFALCON2_FALCON_MOD_SEL::default() - .set_algo(FalconModSelAlgo::Rsa3k) - .write(bar, &E::ID); + bar.write( + WithBase::of::().at(0), + regs::NV_PFALCON2_FALCON_BROM_PARAADDR::zeroed().with_value(params.pkc_data_offset), + ); + bar.write( + WithBase::of::(), + regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::zeroed() + .with_value(u32::from(params.engine_id_mask)), + ); + bar.write( + WithBase::of::(), + regs::NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID::zeroed().with_ucode_id(params.ucode_id), + ); + bar.write( + WithBase::of::(), + regs::NV_PFALCON2_FALCON_MOD_SEL::zeroed().with_algo(FalconModSelAlgo::Rsa3k), + ); Ok(()) } @@ -127,14 +136,14 @@ fn program_brom(&self, _falcon: &Falcon, bar: &Bar0, params: &FalconBromParam } fn is_riscv_active(&self, bar: &Bar0) -> bool { - let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID); - cpuctl.active_stat() + bar.read(regs::NV_PRISCV_RISCV_CPUCTL::of::()) + .active_stat() } fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { // TIMEOUT: memory scrubbing should complete in less than 20ms. read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), + || Ok(bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::())), |r| r.mem_scrubbing_done(), Delta::ZERO, Delta::from_millis(20), @@ -143,12 +152,12 @@ fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { } fn reset_eng(&self, bar: &Bar0) -> Result { - let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); + let _ = bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::()); // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set // RESET_READY so a non-failing timeout is used. let _ = read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), + || Ok(bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::())), |r| r.reset_ready(), Delta::ZERO, Delta::from_micros(150), diff --git a/drivers/gpu/nova-core/falcon/hal/tu102.rs b/drivers/gpu/nova-core/falcon/hal/tu102.rs index 7de6f24cc0a0..c7a90266cb44 100644 --- a/drivers/gpu/nova-core/falcon/hal/tu102.rs +++ b/drivers/gpu/nova-core/falcon/hal/tu102.rs @@ -3,7 +3,11 @@ use core::marker::PhantomData; use kernel::{ - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + register::WithBase, + Io, // + }, prelude::*, time::Delta, // }; @@ -49,14 +53,14 @@ fn program_brom(&self, _falcon: &Falcon, _bar: &Bar0, _params: &FalconBromPar } fn is_riscv_active(&self, bar: &Bar0) -> bool { - let cpuctl = regs::NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS::read(bar, &E::ID); - cpuctl.active_stat() + bar.read(regs::NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS::of::()) + .active_stat() } fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { // TIMEOUT: memory scrubbing should complete in less than 10ms. read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_DMACTL::read(bar, &E::ID)), + || Ok(bar.read(regs::NV_PFALCON_FALCON_DMACTL::of::())), |r| r.mem_scrubbing_done(), Delta::ZERO, Delta::from_millis(10), diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs index b57d362e576a..91ec7d49c1f5 100644 --- a/drivers/gpu/nova-core/falcon/sec2.rs +++ b/drivers/gpu/nova-core/falcon/sec2.rs @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 -use crate::{ - falcon::{ - FalconEngine, - PFalcon2Base, - PFalconBase, // - }, - regs::macros::RegisterBase, +use kernel::io::register::RegisterBase; + +use crate::falcon::{ + FalconEngine, + PFalcon2Base, + PFalconBase, // }; /// Type specifying the `Sec2` falcon engine. Cannot be instantiated. @@ -20,6 +19,4 @@ impl RegisterBase for Sec2 { const BASE: usize = 0x00841000; } -impl FalconEngine for Sec2 { - const ID: Self = Sec2(()); -} +impl FalconEngine for Sec2 {} diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs index 342dba59b2f9..3b12d90d9412 100644 --- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs +++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs @@ -12,6 +12,10 @@ self, Device, // }, + io::{ + register::WithBase, // + Io, + }, prelude::*, ptr::{ Alignable, @@ -33,7 +37,6 @@ Falcon, FalconBromParams, FalconDmaLoadable, - FalconEngine, FalconFbifMemType, FalconFbifTarget, FalconFirmware, @@ -288,15 +291,15 @@ pub(crate) fn run( .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; // Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory. - regs::NV_PFALCON_FBIF_TRANSCFG::try_update( - bar, - &Gsp::ID, - usize::from_safe_cast(self.dmem_desc.ctx_dma), + bar.update( + regs::NV_PFALCON_FBIF_TRANSCFG::of::() + .try_at(usize::from_safe_cast(self.dmem_desc.ctx_dma)) + .ok_or(EINVAL)?, |v| { - v.set_target(FalconFbifTarget::CoherentSysmem) - .set_mem_type(FalconFbifMemType::Physical) + v.with_target(FalconFbifTarget::CoherentSysmem) + .with_mem_type(FalconFbifMemType::Physical) }, - )?; + ); let (mbox0, _) = falcon .boot(bar, Some(0), None) diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index b051d5568cd8..87c2977ba6e4 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -1,14 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 -// Required to retain the original register names used by OpenRM, which are all capital snake case -// but are mapped to types. -#![allow(non_camel_case_types)] - -#[macro_use] -pub(crate) mod macros; - use kernel::{ - io, + io::{ + self, + register::WithBase, + Io, // + }, prelude::*, time, // }; @@ -290,60 +287,147 @@ pub(crate) fn vga_workspace_addr(self) -> Option { // PFALCON -register!(NV_PFALCON_FALCON_IRQSCLR @ PFalconBase[0x00000004] { - 4:4 halt as bool; - 6:6 swgen0 as bool; -}); +io::register! { + pub(crate) NV_PFALCON_FALCON_IRQSCLR(u32) @ PFalconBase + 0x00000004 { + 6:6 swgen0 => bool; + 4:4 halt => bool; + } -register!(NV_PFALCON_FALCON_MAILBOX0 @ PFalconBase[0x00000040] { - 31:0 value as u32; -}); + pub(crate) NV_PFALCON_FALCON_MAILBOX0(u32) @ PFalconBase + 0x00000040 { + 31:0 value => u32; + } -register!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] { - 31:0 value as u32; -}); + pub(crate) NV_PFALCON_FALCON_MAILBOX1(u32) @ PFalconBase + 0x00000044 { + 31:0 value => u32; + } -// Used to store version information about the firmware running -// on the Falcon processor. -register!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] { - 31:0 value as u32; -}); + /// Used to store version information about the firmware running + /// on the Falcon processor. + pub(crate) NV_PFALCON_FALCON_OS(u32) @ PFalconBase + 0x00000080 { + 31:0 value => u32; + } -register!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] { - 31:0 value as u32; -}); + pub(crate) NV_PFALCON_FALCON_RM(u32) @ PFalconBase + 0x00000084 { + 31:0 value => u32; + } -register!(NV_PFALCON_FALCON_HWCFG2 @ PFalconBase[0x000000f4] { - 10:10 riscv as bool; - 12:12 mem_scrubbing as bool, "Set to 0 after memory scrubbing is completed"; - 31:31 reset_ready as bool, "Signal indicating that reset is completed (GA102+)"; -}); + pub(crate) NV_PFALCON_FALCON_HWCFG2(u32) @ PFalconBase + 0x000000f4 { + /// Signal indicating that reset is completed (GA102+). + 31:31 reset_ready => bool; + /// Set to 0 after memory scrubbing is completed. + 12:12 mem_scrubbing => bool; + 10:10 riscv => bool; + } -impl NV_PFALCON_FALCON_HWCFG2 { - /// Returns `true` if memory scrubbing is completed. - pub(crate) fn mem_scrubbing_done(self) -> bool { - !self.mem_scrubbing() + pub(crate) NV_PFALCON_FALCON_CPUCTL(u32) @ PFalconBase + 0x00000100 { + 6:6 alias_en => bool; + 4:4 halted => bool; + 1:1 startcpu => bool; + } + + pub(crate) NV_PFALCON_FALCON_BOOTVEC(u32) @ PFalconBase + 0x00000104 { + 31:0 value => u32; + } + + pub(crate) NV_PFALCON_FALCON_DMACTL(u32) @ PFalconBase + 0x0000010c { + 7:7 secure_stat => bool; + 6:3 dmaq_num; + 2:2 imem_scrubbing => bool; + 1:1 dmem_scrubbing => bool; + 0:0 require_ctx => bool; + } + + pub(crate) NV_PFALCON_FALCON_DMATRFBASE(u32) @ PFalconBase + 0x00000110 { + 31:0 base => u32; + } + + pub(crate) NV_PFALCON_FALCON_DMATRFMOFFS(u32) @ PFalconBase + 0x00000114 { + 23:0 offs; + } + + pub(crate) NV_PFALCON_FALCON_DMATRFCMD(u32) @ PFalconBase + 0x00000118 { + 16:16 set_dmtag; + 14:12 ctxdma; + 10:8 size ?=> DmaTrfCmdSize; + 5:5 is_write => bool; + 4:4 imem => bool; + 3:2 sec; + 1:1 idle => bool; + 0:0 full => bool; + } + + pub(crate) NV_PFALCON_FALCON_DMATRFFBOFFS(u32) @ PFalconBase + 0x0000011c { + 31:0 offs => u32; + } + + pub(crate) NV_PFALCON_FALCON_DMATRFBASE1(u32) @ PFalconBase + 0x00000128 { + 8:0 base; + } + + pub(crate) NV_PFALCON_FALCON_HWCFG1(u32) @ PFalconBase + 0x0000012c { + /// Core revision subversion. + 7:6 core_rev_subversion => FalconCoreRevSubversion; + /// Security model. + 5:4 security_model ?=> FalconSecurityModel; + /// Core revision. + 3:0 core_rev ?=> FalconCoreRev; + } + + pub(crate) NV_PFALCON_FALCON_CPUCTL_ALIAS(u32) @ PFalconBase + 0x00000130 { + 1:1 startcpu => bool; + } + + /// IMEM access control register. Up to 4 ports are available for IMEM access. + pub(crate) NV_PFALCON_FALCON_IMEMC(u32)[4, stride = 16] @ PFalconBase + 0x00000180 { + /// Access secure IMEM. + 28:28 secure => bool; + /// Auto-increment on write. + 24:24 aincw => bool; + /// IMEM block and word offset. + 15:0 offs; + } + + /// IMEM data register. Reading/writing this register accesses IMEM at the address + /// specified by the corresponding IMEMC register. + pub(crate) NV_PFALCON_FALCON_IMEMD(u32)[4, stride = 16] @ PFalconBase + 0x00000184 { + 31:0 data; + } + + /// IMEM tag register. Used to set the tag for the current IMEM block. + pub(crate) NV_PFALCON_FALCON_IMEMT(u32)[4, stride = 16] @ PFalconBase + 0x00000188 { + 15:0 tag; + } + + /// DMEM access control register. Up to 8 ports are available for DMEM access. + pub(crate) NV_PFALCON_FALCON_DMEMC(u32)[8, stride = 8] @ PFalconBase + 0x000001c0 { + /// Auto-increment on write. + 24:24 aincw => bool; + /// DMEM block and word offset. + 15:0 offs; + } + + /// DMEM data register. Reading/writing this register accesses DMEM at the address + /// specified by the corresponding DMEMC register. + pub(crate) NV_PFALCON_FALCON_DMEMD(u32)[8, stride = 8] @ PFalconBase + 0x000001c4 { + 31:0 data; + } + + /// Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE` depending on the + /// falcon instance. + pub(crate) NV_PFALCON_FALCON_ENGINE(u32) @ PFalconBase + 0x000003c0 { + 0:0 reset => bool; + } + + pub(crate) NV_PFALCON_FBIF_TRANSCFG(u32)[8] @ PFalconBase + 0x00000600 { + 2:2 mem_type => FalconFbifMemType; + 1:0 target ?=> FalconFbifTarget; + } + + pub(crate) NV_PFALCON_FBIF_CTL(u32) @ PFalconBase + 0x00000624 { + 7:7 allow_phys_no_ctx => bool; } } -register!(NV_PFALCON_FALCON_CPUCTL @ PFalconBase[0x00000100] { - 1:1 startcpu as bool; - 4:4 halted as bool; - 6:6 alias_en as bool; -}); - -register!(NV_PFALCON_FALCON_BOOTVEC @ PFalconBase[0x00000104] { - 31:0 value as u32; -}); - -register!(NV_PFALCON_FALCON_DMACTL @ PFalconBase[0x0000010c] { - 0:0 require_ctx as bool; - 1:1 dmem_scrubbing as bool; - 2:2 imem_scrubbing as bool; - 6:3 dmaq_num as u8; - 7:7 secure_stat as bool; -}); - impl NV_PFALCON_FALCON_DMACTL { /// Returns `true` if memory scrubbing is completed. pub(crate) fn mem_scrubbing_done(self) -> bool { @@ -351,147 +435,82 @@ pub(crate) fn mem_scrubbing_done(self) -> bool { } } -register!(NV_PFALCON_FALCON_DMATRFBASE @ PFalconBase[0x00000110] { - 31:0 base as u32; -}); - -register!(NV_PFALCON_FALCON_DMATRFMOFFS @ PFalconBase[0x00000114] { - 23:0 offs as u32; -}); - -register!(NV_PFALCON_FALCON_DMATRFCMD @ PFalconBase[0x00000118] { - 0:0 full as bool; - 1:1 idle as bool; - 3:2 sec as u8; - 4:4 imem as bool; - 5:5 is_write as bool; - 10:8 size as u8 ?=> DmaTrfCmdSize; - 14:12 ctxdma as u8; - 16:16 set_dmtag as u8; -}); - impl NV_PFALCON_FALCON_DMATRFCMD { /// Programs the `imem` and `sec` fields for the given FalconMem pub(crate) fn with_falcon_mem(self, mem: FalconMem) -> Self { - self.set_imem(mem != FalconMem::Dmem) - .set_sec(if mem == FalconMem::ImemSecure { 1 } else { 0 }) + let this = self.with_imem(mem != FalconMem::Dmem); + + match mem { + FalconMem::ImemSecure => this.with_const_sec::<1>(), + _ => this.with_const_sec::<0>(), + } } } -register!(NV_PFALCON_FALCON_DMATRFFBOFFS @ PFalconBase[0x0000011c] { - 31:0 offs as u32; -}); - -register!(NV_PFALCON_FALCON_DMATRFBASE1 @ PFalconBase[0x00000128] { - 8:0 base as u16; -}); - -register!(NV_PFALCON_FALCON_HWCFG1 @ PFalconBase[0x0000012c] { - 3:0 core_rev as u8 ?=> FalconCoreRev, "Core revision"; - 5:4 security_model as u8 ?=> FalconSecurityModel, "Security model"; - 7:6 core_rev_subversion as u8 ?=> FalconCoreRevSubversion, "Core revision subversion"; -}); - -register!(NV_PFALCON_FALCON_CPUCTL_ALIAS @ PFalconBase[0x00000130] { - 1:1 startcpu as bool; -}); - -// IMEM access control register. Up to 4 ports are available for IMEM access. -register!(NV_PFALCON_FALCON_IMEMC @ PFalconBase[0x00000180[4; 16]] { - 15:0 offs as u16, "IMEM block and word offset"; - 24:24 aincw as bool, "Auto-increment on write"; - 28:28 secure as bool, "Access secure IMEM"; -}); - -// IMEM data register. Reading/writing this register accesses IMEM at the address -// specified by the corresponding IMEMC register. -register!(NV_PFALCON_FALCON_IMEMD @ PFalconBase[0x00000184[4; 16]] { - 31:0 data as u32; -}); - -// IMEM tag register. Used to set the tag for the current IMEM block. -register!(NV_PFALCON_FALCON_IMEMT @ PFalconBase[0x00000188[4; 16]] { - 15:0 tag as u16; -}); - -// DMEM access control register. Up to 8 ports are available for DMEM access. -register!(NV_PFALCON_FALCON_DMEMC @ PFalconBase[0x000001c0[8; 8]] { - 15:0 offs as u16, "DMEM block and word offset"; - 24:24 aincw as bool, "Auto-increment on write"; -}); - -// DMEM data register. Reading/writing this register accesses DMEM at the address -// specified by the corresponding DMEMC register. -register!(NV_PFALCON_FALCON_DMEMD @ PFalconBase[0x000001c4[8; 8]] { - 31:0 data as u32; -}); - -// Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE` depending on the falcon -// instance. -register!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] { - 0:0 reset as bool; -}); - impl NV_PFALCON_FALCON_ENGINE { /// Resets the falcon pub(crate) fn reset_engine(bar: &Bar0) { - Self::read(bar, &E::ID).set_reset(true).write(bar, &E::ID); + bar.update(Self::of::(), |r| r.with_reset(true)); // TIMEOUT: falcon engine should not take more than 10us to reset. time::delay::fsleep(time::Delta::from_micros(10)); - Self::read(bar, &E::ID).set_reset(false).write(bar, &E::ID); + bar.update(Self::of::(), |r| r.with_reset(false)); } } -register!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600[8]] { - 1:0 target as u8 ?=> FalconFbifTarget; - 2:2 mem_type as bool => FalconFbifMemType; -}); - -register!(NV_PFALCON_FBIF_CTL @ PFalconBase[0x00000624] { - 7:7 allow_phys_no_ctx as bool; -}); +impl NV_PFALCON_FALCON_HWCFG2 { + /// Returns `true` if memory scrubbing is completed. + pub(crate) fn mem_scrubbing_done(self) -> bool { + !self.mem_scrubbing() + } +} /* PFALCON2 */ -register!(NV_PFALCON2_FALCON_MOD_SEL @ PFalcon2Base[0x00000180] { - 7:0 algo as u8 ?=> FalconModSelAlgo; -}); +io::register! { + pub(crate) NV_PFALCON2_FALCON_MOD_SEL(u32) @ PFalcon2Base + 0x00000180 { + 7:0 algo ?=> FalconModSelAlgo; + } -register!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalcon2Base[0x00000198] { - 7:0 ucode_id as u8; -}); + pub(crate) NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID(u32) @ PFalcon2Base + 0x00000198 { + 7:0 ucode_id => u8; + } -register!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalcon2Base[0x0000019c] { - 31:0 value as u32; -}); + pub(crate) NV_PFALCON2_FALCON_BROM_ENGIDMASK(u32) @ PFalcon2Base + 0x0000019c { + 31:0 value => u32; + } -// OpenRM defines this as a register array, but doesn't specify its size and only uses its first -// element. Be conservative until we know the actual size or need to use more registers. -register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] { - 31:0 value as u32; -}); + /// OpenRM defines this as a register array, but doesn't specify its size and only uses its + /// first element. Be conservative until we know the actual size or need to use more registers. + pub(crate) NV_PFALCON2_FALCON_BROM_PARAADDR(u32)[1] @ PFalcon2Base + 0x00000210 { + 31:0 value => u32; + } +} // PRISCV -// RISC-V status register for debug (Turing and GA100 only). -// Reflects current RISC-V core status. -register!(NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS @ PFalcon2Base[0x00000240] { - 0:0 active_stat as bool, "RISC-V core active/inactive status"; -}); +io::register! { + /// RISC-V status register for debug (Turing and GA100 only). + /// Reflects current RISC-V core status. + pub(crate) NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS(u32) @ PFalcon2Base + 0x00000240 { + /// RISC-V core active/inactive status. + 0:0 active_stat => bool; + } -// GA102 and later -register!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] { - 0:0 halted as bool; - 7:7 active_stat as bool; -}); + /// GA102 and later. + pub(crate) NV_PRISCV_RISCV_CPUCTL(u32) @ PFalcon2Base + 0x00000388 { + 7:7 active_stat => bool; + 0:0 halted => bool; + } -register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalcon2Base[0x00000668] { - 0:0 valid as bool; - 4:4 core_select as bool => PeregrineCoreSelect; - 8:8 br_fetch as bool; -}); + /// GA102 and later. + pub(crate) NV_PRISCV_RISCV_BCR_CTRL(u32) @ PFalcon2Base + 0x00000668 { + 8:8 br_fetch => bool; + 4:4 core_select => PeregrineCoreSelect; + 0:0 valid => bool; + } +} // The modules below provide registers that are not identical on all supported chips. They should // only be used in HAL modules. diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs deleted file mode 100644 index ed624be1f39b..000000000000 --- a/drivers/gpu/nova-core/regs/macros.rs +++ /dev/null @@ -1,739 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! `register!` macro to define register layout and accessors. -//! -//! A single register typically includes several fields, which are accessed through a combination -//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because -//! not all possible field values are necessarily valid. -//! -//! The `register!` macro in this module provides an intuitive and readable syntax for defining a -//! dedicated type for each register. Each such type comes with its own field accessors that can -//! return an error if a field's value is invalid. Please look at the [`bitfield`] macro for the -//! complete syntax of fields definitions. - -/// Trait providing a base address to be added to the offset of a relative register to obtain -/// its actual offset. -/// -/// The `T` generic argument is used to distinguish which base to use, in case a type provides -/// several bases. It is given to the `register!` macro to restrict the use of the register to -/// implementors of this particular variant. -pub(crate) trait RegisterBase { - const BASE: usize; -} - -/// Defines a dedicated type for a register with an absolute offset, including getter and setter -/// methods for its fields and methods to read and write it from an `Io` region. -/// -/// Example: -/// -/// ```no_run -/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" { -/// 3:0 minor_revision as u8, "Minor revision of the chip"; -/// 7:4 major_revision as u8, "Major revision of the chip"; -/// 28:20 chipset as u32 ?=> Chipset, "Chipset model"; -/// }); -/// ``` -/// -/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io` -/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least -/// significant bits of the register. Each field can be accessed and modified using accessor -/// methods: -/// -/// ```no_run -/// // Read from the register's defined offset (0x100). -/// let boot0 = BOOT_0::read(&bar); -/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision()); -/// -/// // `Chipset::try_from` is called with the value of the `chipset` field and returns an -/// // error if it is invalid. -/// let chipset = boot0.chipset()?; -/// -/// // Update some fields and write the value back. -/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar); -/// -/// // Or, just read and update the register in a single step: -/// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10)); -/// ``` -/// -/// The documentation strings are optional. If present, they will be added to the type's -/// definition, or the field getter and setter methods they are attached to. -/// -/// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful -/// for cases where a register's interpretation depends on the context: -/// -/// ```no_run -/// register!(SCRATCH @ 0x00000200, "Scratch register" { -/// 31:0 value as u32, "Raw value"; -/// }); -/// -/// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" { -/// 0:0 completed as bool, "Whether the firmware has completed booting"; -/// }); -/// ``` -/// -/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also -/// providing its own `completed` field. -/// -/// ## Relative registers -/// -/// A register can be defined as being accessible from a fixed offset of a provided base. For -/// instance, imagine the following I/O space: -/// -/// ```text -/// +-----------------------------+ -/// | ... | -/// | | -/// 0x100--->+------------CPU0-------------+ -/// | | -/// 0x110--->+-----------------------------+ -/// | CPU_CTL | -/// +-----------------------------+ -/// | ... | -/// | | -/// | | -/// 0x200--->+------------CPU1-------------+ -/// | | -/// 0x210--->+-----------------------------+ -/// | CPU_CTL | -/// +-----------------------------+ -/// | ... | -/// +-----------------------------+ -/// ``` -/// -/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O -/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define -/// them twice and would prefer a way to select which one to use from a single definition -/// -/// This can be done using the `Base[Offset]` syntax when specifying the register's address. -/// -/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the -/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for -/// this register needs to implement `RegisterBase`. Here is the above example translated -/// into code: -/// -/// ```no_run -/// // Type used to identify the base. -/// pub(crate) struct CpuCtlBase; -/// -/// // ZST describing `CPU0`. -/// struct Cpu0; -/// impl RegisterBase for Cpu0 { -/// const BASE: usize = 0x100; -/// } -/// // Singleton of `CPU0` used to identify it. -/// const CPU0: Cpu0 = Cpu0; -/// -/// // ZST describing `CPU1`. -/// struct Cpu1; -/// impl RegisterBase for Cpu1 { -/// const BASE: usize = 0x200; -/// } -/// // Singleton of `CPU1` used to identify it. -/// const CPU1: Cpu1 = Cpu1; -/// -/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase`. -/// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" { -/// 0:0 start as bool, "Start the CPU core"; -/// }); -/// -/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument -/// // that is used to resolve its final address by adding its `BASE` to the offset of the -/// // register. -/// -/// // Start `CPU0`. -/// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true)); -/// -/// // Start `CPU1`. -/// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true)); -/// -/// // Aliases can also be defined for relative register. -/// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" { -/// 1:1 alias_start as bool, "Start the aliased CPU core"; -/// }); -/// -/// // Start the aliased `CPU0`. -/// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true)); -/// ``` -/// -/// ## Arrays of registers -/// -/// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas -/// can be defined as an array of identical registers, allowing them to be accessed by index with -/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add -/// an `idx` parameter to their `read`, `write` and `update` methods: -/// -/// ```no_run -/// # fn no_run() -> Result<(), Error> { -/// # fn get_scratch_idx() -> usize { -/// # 0x15 -/// # } -/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`. -/// register!(SCRATCH @ 0x00000080[64], "Scratch registers" { -/// 31:0 value as u32; -/// }); -/// -/// // Read scratch register 0, i.e. I/O address `0x80`. -/// let scratch_0 = SCRATCH::read(bar, 0).value(); -/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`. -/// let scratch_15 = SCRATCH::read(bar, 15).value(); -/// -/// // This is out of bounds and won't build. -/// // let scratch_128 = SCRATCH::read(bar, 128).value(); -/// -/// // Runtime-obtained array index. -/// let scratch_idx = get_scratch_idx(); -/// // Access on a runtime index returns an error if it is out-of-bounds. -/// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value(); -/// -/// // Alias to a particular register in an array. -/// // Here `SCRATCH[8]` is used to convey the firmware exit code. -/// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status code" { -/// 7:0 status as u8; -/// }); -/// -/// let status = FIRMWARE_STATUS::read(bar).status(); -/// -/// // Non-contiguous register arrays can be defined by adding a stride parameter. -/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the -/// // registers of the two declarations below are interleaved. -/// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registers bank 0" { -/// 31:0 value as u32; -/// }); -/// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registers bank 1" { -/// 31:0 value as u32; -/// }); -/// # Ok(()) -/// # } -/// ``` -/// -/// ## Relative arrays of registers -/// -/// Combining the two features described in the sections above, arrays of registers accessible from -/// a base can also be defined: -/// -/// ```no_run -/// # fn no_run() -> Result<(), Error> { -/// # fn get_scratch_idx() -> usize { -/// # 0x15 -/// # } -/// // Type used as parameter of `RegisterBase` to specify the base. -/// pub(crate) struct CpuCtlBase; -/// -/// // ZST describing `CPU0`. -/// struct Cpu0; -/// impl RegisterBase for Cpu0 { -/// const BASE: usize = 0x100; -/// } -/// // Singleton of `CPU0` used to identify it. -/// const CPU0: Cpu0 = Cpu0; -/// -/// // ZST describing `CPU1`. -/// struct Cpu1; -/// impl RegisterBase for Cpu1 { -/// const BASE: usize = 0x200; -/// } -/// // Singleton of `CPU1` used to identify it. -/// const CPU1: Cpu1 = Cpu1; -/// -/// // 64 per-cpu scratch registers, arranged as an contiguous array. -/// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" { -/// 31:0 value as u32; -/// }); -/// -/// let cpu0_scratch_0 = CPU_SCRATCH::read(bar, &Cpu0, 0).value(); -/// let cpu1_scratch_15 = CPU_SCRATCH::read(bar, &Cpu1, 15).value(); -/// -/// // This won't build. -/// // let cpu0_scratch_128 = CPU_SCRATCH::read(bar, &Cpu0, 128).value(); -/// -/// // Runtime-obtained array index. -/// let scratch_idx = get_scratch_idx(); -/// // Access on a runtime value returns an error if it is out-of-bounds. -/// let cpu0_some_scratch = CPU_SCRATCH::try_read(bar, &Cpu0, scratch_idx)?.value(); -/// -/// // `SCRATCH[8]` is used to convey the firmware exit code. -/// register!(CPU_FIRMWARE_STATUS => CpuCtlBase[CPU_SCRATCH[8]], -/// "Per-CPU firmware exit status code" { -/// 7:0 status as u8; -/// }); -/// -/// let cpu0_status = CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status(); -/// -/// // Non-contiguous register arrays can be defined by adding a stride parameter. -/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the -/// // registers of the two declarations below are interleaved. -/// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]], -/// "Scratch registers bank 0" { -/// 31:0 value as u32; -/// }); -/// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]], -/// "Scratch registers bank 1" { -/// 31:0 value as u32; -/// }); -/// # Ok(()) -/// # } -/// ``` -macro_rules! register { - // Creates a register at a fixed offset of the MMIO space. - ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_fixed $name @ $offset); - }; - - // Creates an alias register of fixed offset register `alias` with its own fields. - ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_fixed $name @ $alias::OFFSET); - }; - - // Creates a register at a relative offset from a base address provider. - ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_relative $name @ $base [ $offset ]); - }; - - // Creates an alias register of relative offset register `alias` with its own fields. - ($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_relative $name @ $base [ $alias::OFFSET ]); - }; - - // Creates an array of registers at a fixed offset of the MMIO space. - ( - $name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $comment:literal)? { - $($fields:tt)* - } - ) => { - static_assert!(::core::mem::size_of::() <= $stride); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_array $name @ $offset [ $size ; $stride ]); - }; - - // Shortcut for contiguous array of registers (stride == size of element). - ( - $name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? { - $($fields:tt)* - } - ) => { - register!($name @ $offset [ $size ; ::core::mem::size_of::() ] $(, $comment)? { - $($fields)* - } ); - }; - - // Creates an array of registers at a relative offset from a base address provider. - ( - $name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ] - $(, $comment:literal)? { $($fields:tt)* } - ) => { - static_assert!(::core::mem::size_of::() <= $stride); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]); - }; - - // Shortcut for contiguous array of relative registers (stride == size of element). - ( - $name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $comment:literal)? { - $($fields:tt)* - } - ) => { - register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::() ] ] - $(, $comment)? { $($fields)* } ); - }; - - // Creates an alias of register `idx` of relative array of registers `alias` with its own - // fields. - ( - $name:ident => $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comment:literal)? { - $($fields:tt)* - } - ) => { - static_assert!($idx < $alias::SIZE); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] ); - }; - - // Creates an alias of register `idx` of array of registers `alias` with its own fields. - // This rule belongs to the (non-relative) register arrays set, but needs to be put last - // to avoid it being interpreted in place of the relative register array alias rule. - ($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => { - static_assert!($idx < $alias::SIZE); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); - register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE ); - }; - - // Generates the IO accessors for a fixed offset register. - (@io_fixed $name:ident @ $offset:expr) => { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize = $offset; - - /// Read the register from its address in `io`. - #[inline(always)] - pub(crate) fn read(io: &T) -> Self where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - { - Self(io.read32($offset)) - } - - /// Write the value contained in `self` to the register address in `io`. - #[inline(always)] - pub(crate) fn write(self, io: &T) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - { - io.write32(self.0, $offset) - } - - /// Read the register from its address in `io` and run `f` on its value to obtain a new - /// value to write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - f: F, - ) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg = f(Self::read(io)); - reg.write(io); - } - } - }; - - // Generates the IO accessors for a relative offset register. - (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize = $offset; - - /// Read the register from `io`, using the base address provided by `base` and adding - /// the register's offset to it. - #[inline(always)] - pub(crate) fn read( - io: &T, - #[allow(unused_variables)] - base: &B, - ) -> Self where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - { - const OFFSET: usize = $name::OFFSET; - - let value = io.read32( - >::BASE + OFFSET - ); - - Self(value) - } - - /// Write the value contained in `self` to `io`, using the base address provided by - /// `base` and adding the register's offset to it. - #[inline(always)] - pub(crate) fn write( - self, - io: &T, - #[allow(unused_variables)] - base: &B, - ) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - { - const OFFSET: usize = $name::OFFSET; - - io.write32( - self.0, - >::BASE + OFFSET - ); - } - - /// Read the register from `io`, using the base address provided by `base` and adding - /// the register's offset to it, then run `f` on its value to obtain a new value to - /// write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - base: &B, - f: F, - ) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg = f(Self::read(io, base)); - reg.write(io, base); - } - } - }; - - // Generates the IO accessors for an array of registers. - (@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize = $offset; - pub(crate) const SIZE: usize = $size; - pub(crate) const STRIDE: usize = $stride; - - /// Read the array register at index `idx` from its address in `io`. - #[inline(always)] - pub(crate) fn read( - io: &T, - idx: usize, - ) -> Self where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - { - build_assert!(idx < Self::SIZE); - - let offset = Self::OFFSET + (idx * Self::STRIDE); - let value = io.read32(offset); - - Self(value) - } - - /// Write the value contained in `self` to the array register with index `idx` in `io`. - #[inline(always)] - pub(crate) fn write( - self, - io: &T, - idx: usize - ) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - { - build_assert!(idx < Self::SIZE); - - let offset = Self::OFFSET + (idx * Self::STRIDE); - - io.write32(self.0, offset); - } - - /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a - /// new value to write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - idx: usize, - f: F, - ) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg = f(Self::read(io, idx)); - reg.write(io, idx); - } - - /// Read the array register at index `idx` from its address in `io`. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_read( - io: &T, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - { - if idx < Self::SIZE { - Ok(Self::read(io, idx)) - } else { - Err(EINVAL) - } - } - - /// Write the value contained in `self` to the array register with index `idx` in `io`. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_write( - self, - io: &T, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - { - if idx < Self::SIZE { - Ok(self.write(io, idx)) - } else { - Err(EINVAL) - } - } - - /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a - /// new value to write back. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_update( - io: &T, - idx: usize, - f: F, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - F: ::core::ops::FnOnce(Self) -> Self, - { - if idx < Self::SIZE { - Ok(Self::update(io, idx, f)) - } else { - Err(EINVAL) - } - } - } - }; - - // Generates the IO accessors for an array of relative registers. - ( - @io_relative_array $name:ident @ $base:ty - [ $offset:literal [ $size:expr ; $stride:expr ] ] - ) => { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize = $offset; - pub(crate) const SIZE: usize = $size; - pub(crate) const STRIDE: usize = $stride; - - /// Read the array register at index `idx` from `io`, using the base address provided - /// by `base` and adding the register's offset to it. - #[inline(always)] - pub(crate) fn read( - io: &T, - #[allow(unused_variables)] - base: &B, - idx: usize, - ) -> Self where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - { - build_assert!(idx < Self::SIZE); - - let offset = >::BASE + - Self::OFFSET + (idx * Self::STRIDE); - let value = io.read32(offset); - - Self(value) - } - - /// Write the value contained in `self` to `io`, using the base address provided by - /// `base` and adding the offset of array register `idx` to it. - #[inline(always)] - pub(crate) fn write( - self, - io: &T, - #[allow(unused_variables)] - base: &B, - idx: usize - ) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - { - build_assert!(idx < Self::SIZE); - - let offset = >::BASE + - Self::OFFSET + (idx * Self::STRIDE); - - io.write32(self.0, offset); - } - - /// Read the array register at index `idx` from `io`, using the base address provided - /// by `base` and adding the register's offset to it, then run `f` on its value to - /// obtain a new value to write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - base: &B, - idx: usize, - f: F, - ) where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg = f(Self::read(io, base, idx)); - reg.write(io, base, idx); - } - - /// Read the array register at index `idx` from `io`, using the base address provided - /// by `base` and adding the register's offset to it. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_read( - io: &T, - base: &B, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - { - if idx < Self::SIZE { - Ok(Self::read(io, base, idx)) - } else { - Err(EINVAL) - } - } - - /// Write the value contained in `self` to `io`, using the base address provided by - /// `base` and adding the offset of array register `idx` to it. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_write( - self, - io: &T, - base: &B, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - { - if idx < Self::SIZE { - Ok(self.write(io, base, idx)) - } else { - Err(EINVAL) - } - } - - /// Read the array register at index `idx` from `io`, using the base address provided - /// by `base` and adding the register's offset to it, then run `f` on its value to - /// obtain a new value to write back. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_update( - io: &T, - base: &B, - idx: usize, - f: F, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref, - I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable, - B: crate::regs::macros::RegisterBase<$base>, - F: ::core::ops::FnOnce(Self) -> Self, - { - if idx < Self::SIZE { - Ok(Self::update(io, base, idx, f)) - } else { - Err(EINVAL) - } - } - } - }; -} From 2278f97bb3e121504fe7f6ecbcfc11e8b6a3dc6e Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:22 +0900 Subject: [PATCH 292/712] gpu: nova-core: remove `io::` qualifier to register macro invocations The kernel's `register` macro would clash with nova-core's own version if it was imported directly, so it was accessed through its `io` module during the conversion phase. Now that nova-core's `register` macro doesn't exist anymore, we can import and use it directly without risk of name collision. Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-9-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/regs.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 87c2977ba6e4..2f171a4ff9ba 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -2,7 +2,7 @@ use kernel::{ io::{ - self, + register, register::WithBase, Io, // }, @@ -35,7 +35,7 @@ // PMC -io::register! { +register! { /// Basic revision information about the GPU. pub(crate) NV_PMC_BOOT_0(u32) @ 0x00000000 { /// Lower bits of the architecture. @@ -106,7 +106,7 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { // PBUS -io::register! { +register! { pub(crate) NV_PBUS_SW_SCRATCH(u32)[64] @ 0x00001400 {} /// Scratch register 0xe used as FRTS firmware error code. @@ -117,7 +117,7 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { // PFB -io::register! { +register! { /// Low bits of the physical system memory address used by the GPU to perform sysmembar /// operations (see [`crate::fb::SysmemFlush`]). pub(crate) NV_PFB_NISO_FLUSH_SYSMEM_ADDR(u32) @ 0x00100c10 { @@ -180,7 +180,7 @@ pub(crate) fn higher_bound(self) -> u64 { // PGSP -io::register! { +register! { pub(crate) NV_PGSP_QUEUE_HEAD(u32) @ 0x00110c00 { 31:0 address; } @@ -195,7 +195,7 @@ pub(crate) fn higher_bound(self) -> u64 { // These scratch registers remain powered on even in a low-power state and have a designated group // number. -io::register! { +register! { /// Boot Sequence Interface (BSI) register used to determine /// if GSP reload/resume has completed during the boot process. pub(crate) NV_PGC6_BSI_SECURE_SCRATCH_14(u32) @ 0x001180f8 { @@ -247,7 +247,7 @@ pub(crate) fn usable_fb_size(self) -> u64 { // PDISP -io::register! { +register! { pub(crate) NV_PDISP_VGA_WORKSPACE_BASE(u32) @ 0x00625f04 { /// VGA workspace base address divided by 0x10000. 31:8 addr; @@ -271,7 +271,7 @@ pub(crate) fn vga_workspace_addr(self) -> Option { pub(crate) const NV_FUSE_OPT_FPF_SIZE: usize = 16; -io::register! { +register! { pub(crate) NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION(u32)[NV_FUSE_OPT_FPF_SIZE] @ 0x00824100 { 15:0 data => u16; } @@ -287,7 +287,7 @@ pub(crate) fn vga_workspace_addr(self) -> Option { // PFALCON -io::register! { +register! { pub(crate) NV_PFALCON_FALCON_IRQSCLR(u32) @ PFalconBase + 0x00000004 { 6:6 swgen0 => bool; 4:4 halt => bool; @@ -468,7 +468,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool { /* PFALCON2 */ -io::register! { +register! { pub(crate) NV_PFALCON2_FALCON_MOD_SEL(u32) @ PFalcon2Base + 0x00000180 { 7:0 algo ?=> FalconModSelAlgo; } @@ -490,7 +490,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool { // PRISCV -io::register! { +register! { /// RISC-V status register for debug (Turing and GA100 only). /// Reflects current RISC-V core status. pub(crate) NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS(u32) @ PFalcon2Base + 0x00000240 { @@ -516,11 +516,11 @@ pub(crate) fn mem_scrubbing_done(self) -> bool { // only be used in HAL modules. pub(crate) mod gm107 { - use kernel::io; + use kernel::io::register; // FUSE - io::register! { + register! { pub(crate) NV_FUSE_STATUS_OPT_DISPLAY(u32) @ 0x00021c04 { 0:0 display_disabled => bool; } @@ -528,11 +528,11 @@ pub(crate) mod gm107 { } pub(crate) mod ga100 { - use kernel::io; + use kernel::io::register; // FUSE - io::register! { + register! { pub(crate) NV_FUSE_STATUS_OPT_DISPLAY(u32) @ 0x00820c04 { 0:0 display_disabled => bool; } From 1998e6be8239cd37817e05bdc21439de17b904df Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 25 Mar 2026 11:46:23 +0900 Subject: [PATCH 293/712] Documentation: nova: remove register abstraction task The `register!` macro has been implemented and all nova-core code converted to use it. Remove the corresponding task in todo.rst. Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260325-b4-nova-register-v4-10-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot --- Documentation/gpu/nova/core/todo.rst | 76 ---------------------------- 1 file changed, 76 deletions(-) diff --git a/Documentation/gpu/nova/core/todo.rst b/Documentation/gpu/nova/core/todo.rst index d1964eb645e2..d5130b2b08fb 100644 --- a/Documentation/gpu/nova/core/todo.rst +++ b/Documentation/gpu/nova/core/todo.rst @@ -51,82 +51,6 @@ There also have been considerations of ToPrimitive [2]. | Link: https://lore.kernel.org/all/cover.1750689857.git.y.j3ms.n@gmail.com/ [1] | Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Implement.20.60FromPrimitive.60.20trait.20.2B.20derive.20macro.20for.20nova-core/with/541971854 [2] -Generic register abstraction [REGA] ------------------------------------ - -Work out how register constants and structures can be automatically generated -through generalized macros. - -Example: - -.. code-block:: rust - - register!(BOOT0, 0x0, u32, pci::Bar, Fields [ - MINOR_REVISION(3:0, RO), - MAJOR_REVISION(7:4, RO), - REVISION(7:0, RO), // Virtual register combining major and minor rev. - ]) - -This could expand to something like: - -.. code-block:: rust - - const BOOT0_OFFSET: usize = 0x00000000; - const BOOT0_MINOR_REVISION_SHIFT: u8 = 0; - const BOOT0_MINOR_REVISION_MASK: u32 = 0x0000000f; - const BOOT0_MAJOR_REVISION_SHIFT: u8 = 4; - const BOOT0_MAJOR_REVISION_MASK: u32 = 0x000000f0; - const BOOT0_REVISION_SHIFT: u8 = BOOT0_MINOR_REVISION_SHIFT; - const BOOT0_REVISION_MASK: u32 = BOOT0_MINOR_REVISION_MASK | BOOT0_MAJOR_REVISION_MASK; - - struct Boot0(u32); - - impl Boot0 { - #[inline] - fn read(bar: &RevocableGuard<'_, pci::Bar>) -> Self { - Self(bar.readl(BOOT0_OFFSET)) - } - - #[inline] - fn minor_revision(&self) -> u32 { - (self.0 & BOOT0_MINOR_REVISION_MASK) >> BOOT0_MINOR_REVISION_SHIFT - } - - #[inline] - fn major_revision(&self) -> u32 { - (self.0 & BOOT0_MAJOR_REVISION_MASK) >> BOOT0_MAJOR_REVISION_SHIFT - } - - #[inline] - fn revision(&self) -> u32 { - (self.0 & BOOT0_REVISION_MASK) >> BOOT0_REVISION_SHIFT - } - } - -Usage: - -.. code-block:: rust - - let bar = bar.try_access().ok_or(ENXIO)?; - - let boot0 = Boot0::read(&bar); - pr_info!("Revision: {}\n", boot0.revision()); - -A work-in-progress implementation currently resides in -`drivers/gpu/nova-core/regs/macros.rs` and is used in nova-core. It would be -nice to improve it (possibly using proc macros) and move it to the `kernel` -crate so it can be used by other components as well. - -Features desired before this happens: - -* Make I/O optional I/O (for field values that are not registers), -* Support other sizes than `u32`, -* Allow visibility control for registers and individual fields, -* Use Rust slice syntax to express fields ranges. - -| Complexity: Advanced -| Contact: Alexandre Courbot - Numerical operations [NUMM] --------------------------- From 059b7b5b79009af10eeeb24fb942de6fb6bea677 Mon Sep 17 00:00:00 2001 From: Christoph Manszewski Date: Wed, 25 Mar 2026 22:23:42 +0100 Subject: [PATCH 294/712] mailmap: update email address for Christoph Manszewski Since I am moving from intel, map the intel mail to my personal Gmail address. Signed-off-by: Christoph Manszewski Signed-off-by: Joonas Lahtinen Link: https://patch.msgid.link/20260325212342.4388-1-c.manszewski@gmail.com --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index e1cf6bb85d33..9dbd3a2b4961 100644 --- a/.mailmap +++ b/.mailmap @@ -196,6 +196,7 @@ Christophe Leroy Christophe Ricard Christopher Obbard Christoph Hellwig +Christoph Manszewski Chuck Lever Chuck Lever Chuck Lever From d0b224cf9ab12e86a4d1ca55c760dfaa5c19cbe7 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Wed, 25 Mar 2026 12:32:16 +0100 Subject: [PATCH 295/712] iio: light: veml6070: fix veml6070_read() return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit veml6070_read() computes the sensor value in ret but returns 0 instead of the actual result. This causes veml6070_read_raw() to always report 0. Return the computed value instead of 0. Running make W=1 returns no errors. I was unable to test the patch because I do not have the hardware. Found by code inspection. Fixes: fc38525135dd ("iio: light: veml6070: use guard to handle mutex") Signed-off-by: Aldo Conte Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/veml6070.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c index 6d4483c85f30..74d7246e5225 100644 --- a/drivers/iio/light/veml6070.c +++ b/drivers/iio/light/veml6070.c @@ -134,9 +134,7 @@ static int veml6070_read(struct veml6070_data *data) if (ret < 0) return ret; - ret = (msb << 8) | lsb; - - return 0; + return (msb << 8) | lsb; } static const struct iio_chan_spec veml6070_channels[] = { From c720fb57d56274213d027b3c5ab99080cf62a306 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Tue, 24 Mar 2026 14:21:29 -0500 Subject: [PATCH 296/712] gpio: mxc: map Both Edge pad wakeup to Rising Edge Suspend may fail on i.MX8QM when Falling Edge is used as a pad wakeup trigger due to a hardware bug in the detection logic. Since the hardware does not support Both Edge wakeup, remap requests for Both Edge to Rising Edge by default to avoid hitting this issue. A warning is emitted when Falling Edge is selected on i.MX8QM. Fixes: f60c9eac54af ("gpio: mxc: enable pad wakeup on i.MX8x platforms") cc: stable@vger.kernel.org Reviewed-by: Peng Fan Signed-off-by: Shenwei Wang Link: https://patch.msgid.link/20260324192129.2797237-1-shenwei.wang@nxp.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index d7666fe9dbf8..647b6f4861b7 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -584,12 +584,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable) unsigned long config; bool ret = false; int i, type; + bool is_imx8qm = of_device_is_compatible(port->dev->of_node, "fsl,imx8qm-gpio"); static const u32 pad_type_map[] = { IMX_SCU_WAKEUP_OFF, /* 0 */ IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_RISING */ IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_FALLING */ - IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_BOTH */ + IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_BOTH */ IMX_SCU_WAKEUP_HIGH_LVL, /* IRQ_TYPE_LEVEL_HIGH */ IMX_SCU_WAKEUP_OFF, /* 5 */ IMX_SCU_WAKEUP_OFF, /* 6 */ @@ -604,6 +605,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable) config = pad_type_map[type]; else config = IMX_SCU_WAKEUP_OFF; + + if (is_imx8qm && config == IMX_SCU_WAKEUP_FALL_EDGE) { + dev_warn_once(port->dev, + "No falling-edge support for wakeup on i.MX8QM\n"); + config = IMX_SCU_WAKEUP_OFF; + } + ret |= mxc_gpio_generic_config(port, i, config); } } From 4b56770d345524fc2acc143a2b85539cf7d74bc1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 16 Mar 2026 13:21:19 -0700 Subject: [PATCH 297/712] crypto: tegra - Add missing CRYPTO_ALG_ASYNC The tegra crypto driver failed to set the CRYPTO_ALG_ASYNC on its asynchronous algorithms, causing the crypto API to select them for users that request only synchronous algorithms. This causes crashes (at least). Fix this by adding the flag like what the other drivers do. Also remove the unnecessary CRYPTO_ALG_TYPE_* flags, since those just get ignored and overridden by the registration function anyway. Reported-by: Zorro Lang Closes: https://lore.kernel.org/r/20260314080937.pghb4aa7d4je3mhh@dell-per750-06-vm-08.rhts.eng.pek2.redhat.com Fixes: 0880bb3b00c8 ("crypto: tegra - Add Tegra Security Engine driver") Cc: stable@vger.kernel.org Cc: Akhil R Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu --- drivers/crypto/tegra/tegra-se-aes.c | 11 ++++++---- drivers/crypto/tegra/tegra-se-hash.c | 30 ++++++++++++++++------------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 0e07d0523291..9210cceb4b7b 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -529,7 +529,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "cbc(aes)", .cra_driver_name = "cbc-aes-tegra", .cra_priority = 500, - .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = 0xf, @@ -550,7 +550,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "ecb(aes)", .cra_driver_name = "ecb-aes-tegra", .cra_priority = 500, - .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = 0xf, @@ -572,7 +572,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "ctr(aes)", .cra_driver_name = "ctr-aes-tegra", .cra_priority = 500, - .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = 0xf, @@ -594,6 +594,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "xts(aes)", .cra_driver_name = "xts-aes-tegra", .cra_priority = 500, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = (__alignof__(u64) - 1), @@ -1922,6 +1923,7 @@ static struct tegra_se_alg tegra_aead_algs[] = { .cra_name = "gcm(aes)", .cra_driver_name = "gcm-aes-tegra", .cra_priority = 500, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct tegra_aead_ctx), .cra_alignmask = 0xf, @@ -1944,6 +1946,7 @@ static struct tegra_se_alg tegra_aead_algs[] = { .cra_name = "ccm(aes)", .cra_driver_name = "ccm-aes-tegra", .cra_priority = 500, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct tegra_aead_ctx), .cra_alignmask = 0xf, @@ -1971,7 +1974,7 @@ static struct tegra_se_alg tegra_cmac_algs[] = { .cra_name = "cmac(aes)", .cra_driver_name = "tegra-se-cmac", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_cmac_ctx), .cra_alignmask = 0, diff --git a/drivers/crypto/tegra/tegra-se-hash.c b/drivers/crypto/tegra/tegra-se-hash.c index 4a298ace6e9f..06bb5bf0fa33 100644 --- a/drivers/crypto/tegra/tegra-se-hash.c +++ b/drivers/crypto/tegra/tegra-se-hash.c @@ -761,7 +761,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha1", .cra_driver_name = "tegra-se-sha1", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -786,7 +786,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha224", .cra_driver_name = "tegra-se-sha224", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -811,7 +811,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha256", .cra_driver_name = "tegra-se-sha256", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -836,7 +836,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha384", .cra_driver_name = "tegra-se-sha384", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -861,7 +861,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha512", .cra_driver_name = "tegra-se-sha512", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -886,7 +886,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-224", .cra_driver_name = "tegra-se-sha3-224", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -911,7 +911,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-256", .cra_driver_name = "tegra-se-sha3-256", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -936,7 +936,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-384", .cra_driver_name = "tegra-se-sha3-384", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -961,7 +961,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-512", .cra_driver_name = "tegra-se-sha3-512", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -988,7 +988,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha224)", .cra_driver_name = "tegra-se-hmac-sha224", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -1015,7 +1016,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha256)", .cra_driver_name = "tegra-se-hmac-sha256", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -1042,7 +1044,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha384)", .cra_driver_name = "tegra-se-hmac-sha384", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -1069,7 +1072,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha512)", .cra_driver_name = "tegra-se-hmac-sha512", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, From 5ddfdcbe10dc5f97afc4e46ca22be2be717e8caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Horia=20Geant=C4=83?= Date: Tue, 17 Mar 2026 12:25:13 +0200 Subject: [PATCH 298/712] crypto: caam - fix DMA corruption on long hmac keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a key longer than block size is supplied, it is copied and then hashed into the real key. The memory allocated for the copy needs to be rounded to DMA cache alignment, as otherwise the hashed key may corrupt neighbouring memory. The rounding was performed, but never actually used for the allocation. Fix this by replacing kmemdup with kmalloc for a larger buffer, followed by memcpy. Fixes: 199354d7fb6e ("crypto: caam - Remove GFP_DMA and add DMA alignment padding") Reported-by: Paul Bunyan Signed-off-by: Horia Geantă Signed-off-by: Herbert Xu --- drivers/crypto/caam/caamhash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 628c43a7efc4..44122208f70c 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -441,9 +441,10 @@ static int ahash_setkey(struct crypto_ahash *ahash, if (aligned_len < keylen) return -EOVERFLOW; - hashed_key = kmemdup(key, keylen, GFP_KERNEL); + hashed_key = kmalloc(aligned_len, GFP_KERNEL); if (!hashed_key) return -ENOMEM; + memcpy(hashed_key, key, keylen); ret = hash_digest_key(ctx, &keylen, hashed_key, digestsize); if (ret) goto bad_free_key; From 80688afb9c35b3934ce2d6be9973758915e2e0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Horia=20Geant=C4=83?= Date: Tue, 17 Mar 2026 12:25:14 +0200 Subject: [PATCH 299/712] crypto: caam - fix overflow on long hmac keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a key longer than block size is supplied, it is copied and then hashed into the real key. The memory allocated for the copy needs to be rounded to DMA cache alignment, as otherwise the hashed key may corrupt neighbouring memory. The copying is performed using kmemdup, however this leads to an overflow: reading more bytes (aligned_len - keylen) from the keylen source buffer. Fix this by replacing kmemdup with kmalloc, followed by memcpy. Fixes: 199354d7fb6e ("crypto: caam - Remove GFP_DMA and add DMA alignment padding") Signed-off-by: Horia Geantă Signed-off-by: Herbert Xu --- drivers/crypto/caam/caamalg_qi2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c index 167372936ca7..78964e1712e5 100644 --- a/drivers/crypto/caam/caamalg_qi2.c +++ b/drivers/crypto/caam/caamalg_qi2.c @@ -3326,9 +3326,10 @@ static int ahash_setkey(struct crypto_ahash *ahash, const u8 *key, if (aligned_len < keylen) return -EOVERFLOW; - hashed_key = kmemdup(key, aligned_len, GFP_KERNEL); + hashed_key = kmalloc(aligned_len, GFP_KERNEL); if (!hashed_key) return -ENOMEM; + memcpy(hashed_key, key, keylen); ret = hash_digest_key(ctx, &keylen, hashed_key, digestsize); if (ret) goto bad_free_key; From 6d89f743e57cb34e233a8217b394c7ee09abf225 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 25 Mar 2026 16:31:38 +0100 Subject: [PATCH 300/712] crypto: deflate - fix spurious -ENOSPC The code in deflate_decompress_one may erroneously return -ENOSPC even if it didn't run out of output space. The error happens under this condition: - Suppose that there are two input pages, the compressed data fits into the first page and the zlib checksum is placed in the second page. - The code iterates over the first page, decompresses the data and fully fills the destination buffer, zlib_inflate returns Z_OK becuse zlib hasn't seen the checksum yet. - The outer do-while loop is iterated again, acomp_walk_next_src sets the input parameters to the second page containing the checksum. - We go into the inner do-while loop, execute "dcur = acomp_walk_next_dst(&walk);". "dcur" is zero, so we break out of the loop and return -ENOSPC, despite the fact that the decompressed data fit into the destination buffer. In order to fix this bug, this commit changes the logic when to report the -ENOSPC error. We report the error if the destination buffer is empty *and* if zlib_inflate didn't make any progress consuming the input buffer. If zlib_inflate consumes the trailing checksum, we see that it made progress and we will not return -ENOSPC. Fixes: 08cabc7d3c86 ("crypto: deflate - Convert to acomp") Signed-off-by: Mikulas Patocka Signed-off-by: Herbert Xu --- crypto/deflate.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crypto/deflate.c b/crypto/deflate.c index 46fc7def8d4c..710ebba7ce85 100644 --- a/crypto/deflate.c +++ b/crypto/deflate.c @@ -164,18 +164,21 @@ static int deflate_decompress_one(struct acomp_req *req, do { unsigned int dcur; + unsigned long avail_in; dcur = acomp_walk_next_dst(&walk); - if (!dcur) { - out_of_space = true; - break; - } stream->avail_out = dcur; stream->next_out = walk.dst.virt.addr; + avail_in = stream->avail_in; ret = zlib_inflate(stream, Z_NO_FLUSH); + if (!dcur && avail_in == stream->avail_in) { + out_of_space = true; + break; + } + dcur -= stream->avail_out; acomp_walk_done_dst(&walk, dcur); } while (ret == Z_OK && stream->avail_in); From 62397b493e14107ae82d8b80938f293d95425bcb Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Wed, 25 Mar 2026 18:26:13 +0100 Subject: [PATCH 301/712] crypto: af-alg - fix NULL pointer dereference in scatterwalk The AF_ALG interface fails to unmark the end of a Scatter/Gather List (SGL) when chaining a new af_alg_tsgl structure. If a sendmsg() fills an SGL exactly to MAX_SGL_ENTS, the last entry is marked as the end. A subsequent sendmsg() allocates a new SGL and chains it, but fails to clear the end marker on the previous SGL's last data entry. This causes the crypto scatterwalk to hit a premature end, returning NULL on sg_next() and leading to a kernel panic during dereference. Fix this by explicitly unmarking the end of the previous SGL when performing sg_chain() in af_alg_alloc_tsgl(). Fixes: 8ff590903d5f ("crypto: algif_skcipher - User-space interface for skcipher operations") Signed-off-by: Norbert Szetei Signed-off-by: Herbert Xu --- crypto/af_alg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 0bb609fbec7d..c2fd9cd86c5e 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -623,8 +623,10 @@ static int af_alg_alloc_tsgl(struct sock *sk) sg_init_table(sgl->sg, MAX_SGL_ENTS + 1); sgl->cur = 0; - if (sg) + if (sg) { + sg_unmark_end(sg + MAX_SGL_ENTS - 1); sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg); + } list_add_tail(&sgl->list, &ctx->tsgl_list); } From f078634c184a9b5ccaa056e8b8d6cd32f7bff1b6 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Wed, 18 Mar 2026 13:26:05 +0800 Subject: [PATCH 302/712] drm/bridge: Fix refcount shown via debugfs for encoder_bridges_show() A typical bridge refcount value is 3 after a bridge chain is formed: - devm_drm_bridge_alloc() initializes the refcount value to be 1. - drm_bridge_add() gets an additional reference hence 2. - drm_bridge_attach() gets the third reference hence 3. This typical refcount value aligns with allbridges_show()'s behaviour. However, since encoder_bridges_show() uses drm_for_each_bridge_in_chain_scoped() to automatically get/put the bridge reference while iterating, a bogus reference is accidentally got when showing the wrong typical refcount value as 4 to users via debugfs. Fix this by caching the refcount value returned from kref_read() while iterating and explicitly decreasing the cached refcount value by 1 before showing it to users. Fixes: bd57048e4576 ("drm/bridge: use drm_for_each_bridge_in_chain_scoped()") Signed-off-by: Liu Ying Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli Link: https://patch.msgid.link/20260318-drm-misc-next-2026-03-05-fix-encoder-bridges-refcount-v3-1-147fea581279@nxp.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/drm_bridge.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index d6f11c68bb6a..1987789b258a 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1569,11 +1569,17 @@ EXPORT_SYMBOL(devm_drm_put_bridge); static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx, - bool lingering) + bool lingering, + bool scoped) { + unsigned int refcount = kref_read(&bridge->refcount); + + if (scoped) + refcount--; + drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); - drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount), + drm_printf(p, "\trefcount: %u%s\n", refcount, lingering ? " [lingering]" : ""); drm_printf(p, "\ttype: [%d] %s\n", @@ -1607,10 +1613,10 @@ static int allbridges_show(struct seq_file *m, void *data) mutex_lock(&bridge_lock); list_for_each_entry(bridge, &bridge_list, list) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, false); list_for_each_entry(bridge, &bridge_lingering_list, list) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true, false); mutex_unlock(&bridge_lock); @@ -1625,7 +1631,7 @@ static int encoder_bridges_show(struct seq_file *m, void *data) unsigned int idx = 0; drm_for_each_bridge_in_chain_scoped(encoder, bridge) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, true); return 0; } From f5e841e4966c1873b9bb2c69d07947a54284e5eb Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 23 Mar 2026 20:26:59 -0300 Subject: [PATCH 303/712] rust: workqueue: add support for ARef Add support for the ARef smart pointer. This allows an instance of ARef to handle deferred work directly, which can be convenient or even necessary at times, depending on the specifics of the driver or subsystem. The implementation is similar to that of Arc, and a subsequent patch will implement support for drm::Device as the first user. This is notably important for work items that need access to the drm device, as it was not possible to enqueue work on a ARef> previously without failing the orphan rule. Reviewed-by: Alice Ryhl Acked-by: Danilo Krummrich Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20260323-aref-workitem-v3-1-f59729b812aa@collabora.com Signed-off-by: Alice Ryhl --- rust/kernel/workqueue.rs | 85 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 706e833e9702..6ae7f3fb3496 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -192,9 +192,9 @@ sync::Arc, sync::LockClassKey, time::Jiffies, - types::Opaque, + types::{ARef, AlwaysRefCounted, Opaque}, }; -use core::marker::PhantomData; +use core::{marker::PhantomData, ptr::NonNull}; /// Creates a [`Work`] initialiser with the given name and a newly-created lock class. #[macro_export] @@ -425,10 +425,11 @@ pub unsafe trait RawDelayedWorkItem: RawWorkItem {} /// Defines the method that should be called directly when a work item is executed. /// -/// This trait is implemented by `Pin>` and [`Arc`], and is mainly intended to be -/// implemented for smart pointer types. For your own structs, you would implement [`WorkItem`] -/// instead. The [`run`] method on this trait will usually just perform the appropriate -/// `container_of` translation and then call into the [`run`][WorkItem::run] method from the +/// This trait is implemented by `Pin>`, [`Arc`] and [`ARef`], and +/// is mainly intended to be implemented for smart pointer types. For your own +/// structs, you would implement [`WorkItem`] instead. The [`run`] method on +/// this trait will usually just perform the appropriate `container_of` +/// translation and then call into the [`run`][WorkItem::run] method from the /// [`WorkItem`] trait. /// /// This trait is used when the `work_struct` field is defined using the [`Work`] helper. @@ -934,6 +935,78 @@ unsafe impl RawDelayedWorkItem for Pin> { } +// SAFETY: Like the `Arc` implementation, the `__enqueue` implementation for +// `ARef` obtains a `work_struct` from the `Work` field using +// `T::raw_get_work`, so the same safety reasoning applies: +// +// - `__enqueue` gets the `work_struct` from the `Work` field, using `T::raw_get_work`. +// - The only safe way to create a `Work` object is through `Work::new`. +// - `Work::new` makes sure that `T::Pointer::run` is passed to `init_work_with_key`. +// - Finally `Work` and `RawWorkItem` guarantee that the correct `Work` field +// will be used because of the ID const generic bound. This makes sure that `T::raw_get_work` +// uses the correct offset for the `Work` field, and `Work::new` picks the correct +// implementation of `WorkItemPointer` for `ARef`. +unsafe impl WorkItemPointer for ARef +where + T: AlwaysRefCounted, + T: WorkItem, + T: HasWork, +{ + unsafe extern "C" fn run(ptr: *mut bindings::work_struct) { + // The `__enqueue` method always uses a `work_struct` stored in a `Work`. + let ptr = ptr.cast::>(); + + // SAFETY: This computes the pointer that `__enqueue` got from + // `ARef::into_raw`. + let ptr = unsafe { T::work_container_of(ptr) }; + + // SAFETY: The safety contract of `work_container_of` ensures that it + // returns a valid non-null pointer. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + + // SAFETY: This pointer comes from `ARef::into_raw` and we've been given + // back ownership. + let aref = unsafe { ARef::from_raw(ptr) }; + + T::run(aref) + } +} + +// SAFETY: The `work_struct` raw pointer is guaranteed to be valid for the duration of the call to +// the closure because we get it from an `ARef`, which means that the ref count will be at least 1, +// and we don't drop the `ARef` ourselves. If `queue_work_on` returns true, it is further guaranteed +// to be valid until a call to the function pointer in `work_struct` because we leak the memory it +// points to, and only reclaim it if the closure returns false, or in `WorkItemPointer::run`, which +// is what the function pointer in the `work_struct` must be pointing to, according to the safety +// requirements of `WorkItemPointer`. +unsafe impl RawWorkItem for ARef +where + T: AlwaysRefCounted, + T: WorkItem, + T: HasWork, +{ + type EnqueueOutput = Result<(), Self>; + + unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput + where + F: FnOnce(*mut bindings::work_struct) -> bool, + { + let ptr = ARef::into_raw(self); + + // SAFETY: Pointers from ARef::into_raw are valid and non-null. + let work_ptr = unsafe { T::raw_get_work(ptr.as_ptr()) }; + // SAFETY: `raw_get_work` returns a pointer to a valid value. + let work_ptr = unsafe { Work::raw_get(work_ptr) }; + + if queue_work_on(work_ptr) { + Ok(()) + } else { + // SAFETY: The work queue has not taken ownership of the pointer. + Err(unsafe { ARef::from_raw(ptr) }) + } + } +} + /// Returns the system work queue (`system_wq`). /// /// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU multi-threaded. There are From 72a723df8decf70e04f799a6defda8bb62d41848 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 23 Mar 2026 20:27:00 -0300 Subject: [PATCH 304/712] rust: drm: dispatch work items to the private data This implementation dispatches any work enqueued on ARef> to its driver-provided handler. It does so by building upon the newly-added ARef support in workqueue.rs in order to call into the driver implementations for work_container_of and raw_get_work. This is notably important for work items that need access to the drm device, as it was not possible to enqueue work on a ARef> previously without failing the orphan rule. The current implementation needs T::Data to live inline with drm::Device in order for work_container_of to function. This restriction is already captured by the trait bounds. Drivers that need to share their ownership of T::Data may trivially get around this: // Lives inline in drm::Device struct DataWrapper { work: ..., // Heap-allocated, shared ownership. data: Arc, } Reviewed-by: Alice Ryhl Acked-by: Danilo Krummrich Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20260323-aref-workitem-v3-2-f59729b812aa@collabora.com Signed-off-by: Alice Ryhl --- rust/kernel/drm/device.rs | 56 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 629ef0bd1188..5db5c7e3bb7a 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -6,8 +6,7 @@ use crate::{ alloc::allocator::Kmalloc, - bindings, - device, + bindings, device, drm::{ self, driver::AllocImpl, // @@ -18,7 +17,12 @@ ARef, AlwaysRefCounted, // }, - types::Opaque, // + types::Opaque, + workqueue::{ + HasWork, + Work, + WorkItem, // + }, }; use core::{ alloc::Layout, @@ -241,3 +245,49 @@ unsafe impl Send for Device {} // SAFETY: A `drm::Device` can be shared among threads because all immutable methods are protected // by the synchronization in `struct drm_device`. unsafe impl Sync for Device {} + +impl WorkItem for Device +where + T: drm::Driver, + T::Data: WorkItem>>, + T::Data: HasWork, ID>, +{ + type Pointer = ARef>; + + fn run(ptr: ARef>) { + T::Data::run(ptr); + } +} + +// SAFETY: +// +// - `raw_get_work` and `work_container_of` return valid pointers by relying on +// `T::Data::raw_get_work` and `container_of`. In particular, `T::Data` is +// stored inline in `drm::Device`, so the `container_of` call is valid. +// +// - The two methods are true inverses of each other: given `ptr: *mut +// Device`, `raw_get_work` will return a `*mut Work, ID>` through +// `T::Data::raw_get_work` and given a `ptr: *mut Work, ID>`, +// `work_container_of` will return a `*mut Device` through `container_of`. +unsafe impl HasWork, ID> for Device +where + T: drm::Driver, + T::Data: HasWork, ID>, +{ + unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work, ID> { + // SAFETY: The caller promises that `ptr` points to a valid `Device`. + let data_ptr = unsafe { &raw mut (*ptr).data }; + + // SAFETY: `data_ptr` is a valid pointer to `T::Data`. + unsafe { T::Data::raw_get_work(data_ptr) } + } + + unsafe fn work_container_of(ptr: *mut Work, ID>) -> *mut Self { + // SAFETY: The caller promises that `ptr` points at a `Work` field in + // `T::Data`. + let data_ptr = unsafe { T::Data::work_container_of(ptr) }; + + // SAFETY: `T::Data` is stored as the `data` field in `Device`. + unsafe { crate::container_of!(data_ptr, Self, data) } + } +} From 332666484f06cd85ad382329f5b3165aa627e9f8 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 23 Mar 2026 20:27:01 -0300 Subject: [PATCH 305/712] rust: workqueue: add delayed work support for ARef The preceding patches added support for ARef work items. By the same token, add support for delayed work items too. The rationale is the same: it may be convenient or even necessary at times to implement HasDelayedWork directly on ARef. A follow up patch will also implement support for drm::Device as the first user. Reviewed-by: Alice Ryhl Acked-by: Danilo Krummrich Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20260323-aref-workitem-v3-3-f59729b812aa@collabora.com Signed-off-by: Alice Ryhl --- rust/kernel/workqueue.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 6ae7f3fb3496..4ee4ff567197 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -1007,6 +1007,17 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput } } +// SAFETY: By the safety requirements of `HasDelayedWork`, the `work_struct` returned by methods in +// `HasWork` provides a `work_struct` that is the `work` field of a `delayed_work`, and the rest of +// the `delayed_work` has the same access rules as its `work` field. +unsafe impl RawDelayedWorkItem for ARef +where + T: WorkItem, + T: HasDelayedWork, + T: AlwaysRefCounted, +{ +} + /// Returns the system work queue (`system_wq`). /// /// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU multi-threaded. There are From 206bada308d0d715d62ac841784af34a2ca22ff6 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 23 Mar 2026 20:27:02 -0300 Subject: [PATCH 306/712] rust: drm: dispatch delayed work items to the private data Much like the patch that dispatched (regular) work items, we also need to dispatch delayed work items in order not to trigger the orphan rule. This allows a drm::Device to dispatch the delayed work to T::Data. Reviewed-by: Alice Ryhl Acked-by: Danilo Krummrich Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20260323-aref-workitem-v3-4-f59729b812aa@collabora.com Signed-off-by: Alice Ryhl --- rust/kernel/drm/device.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 5db5c7e3bb7a..adbafe8db54d 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -19,6 +19,7 @@ }, types::Opaque, workqueue::{ + HasDelayedWork, HasWork, Work, WorkItem, // @@ -291,3 +292,15 @@ unsafe fn work_container_of(ptr: *mut Work, ID>) -> *mut Self { unsafe { crate::container_of!(data_ptr, Self, data) } } } + +// SAFETY: Our `HasWork` implementation returns a `work_struct` that is +// stored in the `work` field of a `delayed_work` with the same access rules as +// the `work_struct` owing to the bound on `T::Data: HasDelayedWork, +// ID>`, which requires that `T::Data::raw_get_work` return a `work_struct` that +// is inside a `delayed_work`. +unsafe impl HasDelayedWork, ID> for Device +where + T: drm::Driver, + T::Data: HasDelayedWork, ID>, +{ +} From 45ebe43ea00d6b9f5b3e0db9c35b8ca2a96b7e70 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 26 Mar 2026 08:07:29 +0100 Subject: [PATCH 307/712] Revert "drm: Fix use-after-free on framebuffers and property blobs when calling drm_dev_unplug" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6bee098b91417654703e17eb5c1822c6dfd0c01d. Den 2026-03-25 kl. 22:11, skrev Simona Vetter: > On Wed, Mar 25, 2026 at 10:26:40AM -0700, Guenter Roeck wrote: >> Hi, >> >> On Fri, Mar 13, 2026 at 04:17:27PM +0100, Maarten Lankhorst wrote: >>> When trying to do a rather aggressive test of igt's "xe_module_load >>> --r reload" with a full desktop environment and game running I noticed >>> a few OOPSes when dereferencing freed pointers, related to >>> framebuffers and property blobs after the compositor exits. >>> >>> Solve this by guarding the freeing in drm_file with drm_dev_enter/exit, >>> and immediately put the references from struct drm_file objects during >>> drm_dev_unplug(). >>> >> >> With this patch in v6.18.20, I get the warning backtraces below. >> The backtraces are gone with the patch reverted. > > Yeah, this needs to be reverted, reasoning below. Maarten, can you please > take care of that and feed the revert through the usual channels? I don't > think it's critical enough that we need to fast-track this into drm.git > directly. > > Quoting the patch here again: > >> drivers/gpu/drm/drm_file.c | 5 ++++- >> drivers/gpu/drm/drm_mode_config.c | 9 ++++++--- >> 2 files changed, 10 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c >> index ec820686b3021..f52141f842a1f 100644 >> --- a/drivers/gpu/drm/drm_file.c >> +++ b/drivers/gpu/drm/drm_file.c >> @@ -233,6 +233,7 @@ static void drm_events_release(struct drm_file *file_priv) >> void drm_file_free(struct drm_file *file) >> { >> struct drm_device *dev; >> + int idx; >> >> if (!file) >> return; >> @@ -249,9 +250,11 @@ void drm_file_free(struct drm_file *file) >> >> drm_events_release(file); >> >> - if (drm_core_check_feature(dev, DRIVER_MODESET)) { >> + if (drm_core_check_feature(dev, DRIVER_MODESET) && >> + drm_dev_enter(dev, &idx)) { > > This is misplaced for two reasons: > > - Even if we'd want to guarantee that we hold a drm_dev_enter/exit > reference during framebuffer teardown, we'd need to do this > _consistently over all callsites. Not ad-hoc in just one place that a > testcase hits. This also means kerneldoc updates of the relevant hooks > and at least a bunch of acks from other driver people to document the > consensus. > > - More importantly, this is driver responsibilities in general unless we > have extremely good reasons to the contrary. Which means this must be > placed in xe. > >> drm_fb_release(file); >> drm_property_destroy_user_blobs(dev, file); >> + drm_dev_exit(idx); >> } >> >> if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) >> diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c >> index 84ae8a23a3678..e349418978f79 100644 >> --- a/drivers/gpu/drm/drm_mode_config.c >> +++ b/drivers/gpu/drm/drm_mode_config.c >> @@ -583,10 +583,13 @@ void drm_mode_config_cleanup(struct drm_device *dev) >> */ >> WARN_ON(!list_empty(&dev->mode_config.fb_list)); >> list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { >> - struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); >> + if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) { >> + struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); > > This is also wrong: > > - Firstly, it's a completely independent bug, we do not smash two bugfixes > into one patch. > > - Secondly, it's again a driver bug: drm_mode_cleanup must be called when > the last drm_device reference disappears (hence the existence of > drmm_mode_config_init), not when the driver gets unbound. The fact that > this shows up in a callchain from a devres cleanup means the intel > driver gets this wrong (like almost everyone else because historically > we didn't know better). > > If we don't follow this rule, then we get races with this code here > running concurrently with drm_file fb cleanups, which just does not > work. Review pointed that out, but then shrugged it off with a confused > explanation: > > https://lore.kernel.org/all/e61e64c796ccfb17ae673331a3df4b877bf42d82.camel@linux.intel.com/ > > Yes this also means a lot of the other drm_device teardown that drivers > do happens way too early. There is a massive can of worms here of a > magnitude that most likely is much, much bigger than what you can > backport to stable kernels. Hotunplug is _hard_. Back to the drawing board, and fixing it in the intel display driver instead. Cc: Thomas Hellström Fixes: 6bee098b9141 ("drm: Fix use-after-free on framebuffers and property blobs when calling drm_dev_unplug") Reported-by: Guenter Roeck Tested-by: Guenter Roeck Acked-by: Simona Vetter Signed-off-by: Maarten Lankhorst Link: https://patch.msgid.link/20260326082217.39941-2-dev@lankhorst.se --- drivers/gpu/drm/drm_file.c | 5 +---- drivers/gpu/drm/drm_mode_config.c | 9 +++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index f52141f842a1..ec820686b302 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -233,7 +233,6 @@ static void drm_events_release(struct drm_file *file_priv) void drm_file_free(struct drm_file *file) { struct drm_device *dev; - int idx; if (!file) return; @@ -250,11 +249,9 @@ void drm_file_free(struct drm_file *file) drm_events_release(file); - if (drm_core_check_feature(dev, DRIVER_MODESET) && - drm_dev_enter(dev, &idx)) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) { drm_fb_release(file); drm_property_destroy_user_blobs(dev, file); - drm_dev_exit(idx); } if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 802bc4608abf..d12db9b0bab8 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -577,13 +577,10 @@ void drm_mode_config_cleanup(struct drm_device *dev) */ WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { - if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) { - struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); + struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); - drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); - drm_framebuffer_print_info(&p, 1, fb); - } - list_del_init(&fb->filp_head); + drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); + drm_framebuffer_print_info(&p, 1, fb); drm_framebuffer_free(&fb->base.refcount); } From d4cf576672fbfee061d6f4f70c74b3b3d163447c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 26 Mar 2026 15:25:36 +0000 Subject: [PATCH 308/712] rust: workqueue: use new sync::aref path for imports ARef and AlwaysRefCounted are being moved to sync::aref, and the re-exports under types are planned to be removed. Thus, update imports to the new path. Acked-by: Danilo Krummrich Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260326-drm-rust-next-fix-aref-v1-1-7f6f58d2828a@google.com Signed-off-by: Alice Ryhl --- rust/kernel/workqueue.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 4ee4ff567197..7e253b6f299c 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -189,10 +189,16 @@ alloc::{AllocError, Flags}, container_of, prelude::*, - sync::Arc, - sync::LockClassKey, + sync::{ + aref::{ + ARef, + AlwaysRefCounted, // + }, + Arc, + LockClassKey, // + }, time::Jiffies, - types::{ARef, AlwaysRefCounted, Opaque}, + types::Opaque, }; use core::{marker::PhantomData, ptr::NonNull}; From bdf6b22fd52954f5ac88689eeaf960ac9687b78c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 26 Mar 2026 15:25:37 +0000 Subject: [PATCH 309/712] rust: drm: use new sync::aref path for imports ARef and AlwaysRefCounted are being moved to sync::aref, and the re-exports under types are planned to be removed. Thus, update imports to the new path. Acked-by: Danilo Krummrich Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260326-drm-rust-next-fix-aref-v1-2-7f6f58d2828a@google.com Signed-off-by: Alice Ryhl --- rust/kernel/drm/gem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index ed974bfdc861..6cc441ee5b63 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -39,7 +39,7 @@ impl $( <$( $tparam_id:ident ),+> )? for $type:ty )? ) => { // SAFETY: All GEM objects are refcounted. - unsafe impl $( <$( $tparam_id ),+> )? $crate::types::AlwaysRefCounted for $type + unsafe impl $( <$( $tparam_id ),+> )? $crate::sync::aref::AlwaysRefCounted for $type where Self: IntoGEMObject, $( $( $bind_param : $bind_trait ),+ )? From 629ec78ef8608d955ce217880cdc3e1873af3a15 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 24 Mar 2026 00:25:57 +0100 Subject: [PATCH 310/712] mpls: add seqcount to protect the platform_label{,s} pair The RCU-protected codepaths (mpls_forward, mpls_dump_routes) can have an inconsistent view of platform_labels vs platform_label in case of a concurrent resize (resize_platform_label_table, under platform_mutex). This can lead to OOB accesses. This patch adds a seqcount, so that we get a consistent snapshot. Note that mpls_label_ok is also susceptible to this, so the check against RTA_DST in rtm_to_route_config, done outside platform_mutex, is not sufficient. This value gets passed to mpls_label_ok once more in both mpls_route_add and mpls_route_del, so there is no issue, but that additional check must not be removed. Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Fixes: 7720c01f3f590 ("mpls: Add a sysctl to control the size of the mpls label table") Fixes: dde1b38e873c ("mpls: Convert mpls_dump_routes() to RCU.") Signed-off-by: Sabrina Dubroca Link: https://patch.msgid.link/cd8fca15e3eb7e212b094064cd83652e20fd9d31.1774284088.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski --- include/net/netns/mpls.h | 1 + net/mpls/af_mpls.c | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/net/netns/mpls.h b/include/net/netns/mpls.h index 6682e51513ef..2073cbac2afb 100644 --- a/include/net/netns/mpls.h +++ b/include/net/netns/mpls.h @@ -17,6 +17,7 @@ struct netns_mpls { size_t platform_labels; struct mpls_route __rcu * __rcu *platform_label; struct mutex platform_mutex; + seqcount_mutex_t platform_label_seq; struct ctl_table_header *ctl; }; diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index d5417688f69e..18d3da8ab384 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -83,14 +83,30 @@ static struct mpls_route *mpls_route_input(struct net *net, unsigned int index) return mpls_dereference(net, platform_label[index]); } +static struct mpls_route __rcu **mpls_platform_label_rcu(struct net *net, size_t *platform_labels) +{ + struct mpls_route __rcu **platform_label; + unsigned int sequence; + + do { + sequence = read_seqcount_begin(&net->mpls.platform_label_seq); + platform_label = rcu_dereference(net->mpls.platform_label); + *platform_labels = net->mpls.platform_labels; + } while (read_seqcount_retry(&net->mpls.platform_label_seq, sequence)); + + return platform_label; +} + static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned int index) { struct mpls_route __rcu **platform_label; + size_t platform_labels; - if (index >= net->mpls.platform_labels) + platform_label = mpls_platform_label_rcu(net, &platform_labels); + + if (index >= platform_labels) return NULL; - platform_label = rcu_dereference(net->mpls.platform_label); return rcu_dereference(platform_label[index]); } @@ -2240,8 +2256,7 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) if (index < MPLS_LABEL_FIRST_UNRESERVED) index = MPLS_LABEL_FIRST_UNRESERVED; - platform_label = rcu_dereference(net->mpls.platform_label); - platform_labels = net->mpls.platform_labels; + platform_label = mpls_platform_label_rcu(net, &platform_labels); if (filter.filter_set) flags |= NLM_F_DUMP_FILTERED; @@ -2645,8 +2660,12 @@ static int resize_platform_label_table(struct net *net, size_t limit) } /* Update the global pointers */ + local_bh_disable(); + write_seqcount_begin(&net->mpls.platform_label_seq); net->mpls.platform_labels = limit; rcu_assign_pointer(net->mpls.platform_label, labels); + write_seqcount_end(&net->mpls.platform_label_seq); + local_bh_enable(); mutex_unlock(&net->mpls.platform_mutex); @@ -2728,6 +2747,8 @@ static __net_init int mpls_net_init(struct net *net) int i; mutex_init(&net->mpls.platform_mutex); + seqcount_mutex_init(&net->mpls.platform_label_seq, &net->mpls.platform_mutex); + net->mpls.platform_labels = 0; net->mpls.platform_label = NULL; net->mpls.ip_ttl_propagate = 1; From f73896b4197ed53cf0894657c899265ef7c86b7a Mon Sep 17 00:00:00 2001 From: Dipayaan Roy Date: Tue, 24 Mar 2026 11:14:28 -0700 Subject: [PATCH 311/712] net: mana: Fix RX skb truesize accounting MANA passes rxq->alloc_size to napi_build_skb() for all RX buffers. It is correct for fragment-backed RX buffers, where alloc_size matches the actual backing allocation used for each packet buffer. However, in the non-fragment RX path mana allocates a full page, or a higher-order page, per RX buffer. In that case alloc_size only reflects the usable packet area and not the actual backing memory. This causes napi_build_skb() to underestimate the skb backing allocation in the single-buffer RX path, so skb->truesize is derived from a value smaller than the real RX buffer allocation. Fix this by updating alloc_size in the non-fragment RX path to the actual backing allocation size before it is passed to napi_build_skb(). Fixes: 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.") Signed-off-by: Dipayaan Roy Reviewed-by: Haiyang Zhang Link: https://patch.msgid.link/acLUhLpLum6qrD/N@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microsoft/mana/mana_en.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index dca62fb9a3a9..09a53c977545 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -766,6 +766,13 @@ static void mana_get_rxbuf_cfg(struct mana_port_context *apc, } *frag_count = 1; + + /* In the single-buffer path, napi_build_skb() must see the + * actual backing allocation size so skb->truesize reflects + * the full page (or higher-order page), not just the usable + * packet area. + */ + *alloc_size = PAGE_SIZE << get_order(*alloc_size); return; } From 976ff48c2ac6e6b25b01428c9d7997bcd0fb2949 Mon Sep 17 00:00:00 2001 From: "Sven Eckelmann (Plasma Cloud)" Date: Tue, 24 Mar 2026 09:36:01 +0100 Subject: [PATCH 312/712] net: ethernet: mtk_ppe: avoid NULL deref when gmac0 is disabled If the gmac0 is disabled, the precheck for a valid ingress device will cause a NULL pointer deref and crash the system. This happens because eth->netdev[0] will be NULL but the code will directly try to access netdev_ops. Instead of just checking for the first net_device, it must be checked if any of the mtk_eth net_devices is matching the netdev_ops of the ingress device. Cc: stable@vger.kernel.org Fixes: 73cfd947dbdb ("net: ethernet: mtk_eth_soc: ppe: prevent ppe update for non-mtk devices") Signed-off-by: Sven Eckelmann (Plasma Cloud) Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324-wed-crash-gmac0-disabled-v1-1-3bc388aee565@simonwunderlich.de Signed-off-by: Jakub Kicinski --- .../net/ethernet/mediatek/mtk_ppe_offload.c | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c index cb30108f2bf6..cc8c4ef8038f 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c @@ -244,6 +244,25 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, return 0; } +static bool +mtk_flow_is_valid_idev(const struct mtk_eth *eth, const struct net_device *idev) +{ + size_t i; + + if (!idev) + return false; + + for (i = 0; i < ARRAY_SIZE(eth->netdev); i++) { + if (!eth->netdev[i]) + continue; + + if (idev->netdev_ops == eth->netdev[i]->netdev_ops) + return true; + } + + return false; +} + static int mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f, int ppe_index) @@ -270,7 +289,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f, flow_rule_match_meta(rule, &match); if (mtk_is_netsys_v2_or_greater(eth)) { idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex); - if (idev && idev->netdev_ops == eth->netdev[0]->netdev_ops) { + if (mtk_flow_is_valid_idev(eth, idev)) { struct mtk_mac *mac = netdev_priv(idev); if (WARN_ON(mac->ppe_idx >= eth->soc->ppe_num)) From 57a04a13aac1f247d171c3f3aef93efc69e6979e Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Tue, 24 Mar 2026 22:08:56 +0800 Subject: [PATCH 313/712] netdevsim: fix build if SKB_EXTENSIONS=n __skb_ext_put() is not declared if SKB_EXTENSIONS is not enabled, which causes a build error: drivers/net/netdevsim/netdev.c: In function 'nsim_forward_skb': drivers/net/netdevsim/netdev.c:114:25: error: implicit declaration of function '__skb_ext_put'; did you mean 'skb_ext_put'? [-Werror=implicit-function-declaration] 114 | __skb_ext_put(psp_ext); | ^~~~~~~~~~~~~ | skb_ext_put cc1: some warnings being treated as errors Add a stub to fix the build. Fixes: 7d9351435ebb ("netdevsim: drop PSP ext ref on forward failure") Signed-off-by: Qingfang Deng Link: https://patch.msgid.link/20260324140857.783-1-dqfext@gmail.com Signed-off-by: Jakub Kicinski --- include/linux/skbuff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index daa4e4944ce3..2f278ce376b7 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -5097,6 +5097,7 @@ static inline bool skb_has_extensions(struct sk_buff *skb) return unlikely(skb->active_extensions); } #else +static inline void __skb_ext_put(struct skb_ext *ext) {} static inline void skb_ext_put(struct sk_buff *skb) {} static inline void skb_ext_reset(struct sk_buff *skb) {} static inline void skb_ext_del(struct sk_buff *skb, int unused) {} From e8e44c98f789dee45cfd24ffb9d4936e0606d7c6 Mon Sep 17 00:00:00 2001 From: Buday Csaba Date: Tue, 24 Mar 2026 14:32:30 +0100 Subject: [PATCH 314/712] net: fec: fix the PTP periodic output sysfs interface When the PPS channel configuration was implemented, the channel index for the periodic outputs was configured as the hardware channel number. The sysfs interface uses a logical channel index, and rejects numbers greater than `n_per_out` (see period_store() in ptp_sysfs.c). That property was left at 1, since the driver implements channel selection, not simultaneous operation of multiple PTP hardware timer channels. A second check in fec_ptp_enable() returns -EOPNOTSUPP when the two channel numbers disagree, making channels 1..3 unusable from sysfs. Fix by removing this redundant check in the FEC PTP driver. Fixes: 566c2d83887f ("net: fec: make PPS channel configurable") Signed-off-by: Buday Csaba Link: https://patch.msgid.link/8ec2afe88423c2231f9cf8044d212ce57846670e.1774359059.git.buday.csaba@prolan.hu Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/fec_ptp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 4b7bad9a485d..56801c2009d5 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -545,9 +545,6 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, if (rq->perout.flags) return -EOPNOTSUPP; - if (rq->perout.index != fep->pps_channel) - return -EOPNOTSUPP; - period.tv_sec = rq->perout.period.sec; period.tv_nsec = rq->perout.period.nsec; period_ns = timespec64_to_ns(&period); From 0239fd701d33475a39428daa3dc627407cd417a6 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Tue, 24 Mar 2026 14:21:19 +0800 Subject: [PATCH 315/712] net: enetc: reset PIR and CIR if they are not equal when initializing TX ring Currently the driver does not reset the producer index register (PIR) and consumer index register (CIR) when initializing a TX BD ring. The driver only reads the PIR and CIR and initializes the software indexes. If the TX BD ring is reinitialized when it still contains unsent frames, its PIR and CIR will not be equal after the reinitialization. However, the BDs between CIR and PIR have been freed and become invalid and this can lead to a hardware malfunction, causing the TX BD ring will not work properly. For ENETC v4, it supports software to set the PIR and CIR, so the driver can reset these two registers if they are not equal when reinitializing the TX BD ring. Therefore, add this solution for ENETC v4. Note that this patch does not work for ENETC v1 because it does not support software to set the PIR and CIR. Fixes: 99100d0d9922 ("net: enetc: add preliminary support for i.MX95 ENETC PF") Signed-off-by: Wei Fang Reviewed-by: Claudiu Manoil Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324062121.2745033-2-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index a146ceaf2ed6..aa8a87124b10 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -2578,6 +2578,7 @@ EXPORT_SYMBOL_GPL(enetc_free_si_resources); static void enetc_setup_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring) { + struct enetc_si *si = container_of(hw, struct enetc_si, hw); int idx = tx_ring->index; u32 tbmr; @@ -2591,10 +2592,20 @@ static void enetc_setup_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring) enetc_txbdr_wr(hw, idx, ENETC_TBLENR, ENETC_RTBLENR_LEN(tx_ring->bd_count)); - /* clearing PI/CI registers for Tx not supported, adjust sw indexes */ + /* For ENETC v1, clearing PI/CI registers for Tx not supported, + * adjust sw indexes + */ tx_ring->next_to_use = enetc_txbdr_rd(hw, idx, ENETC_TBPIR); tx_ring->next_to_clean = enetc_txbdr_rd(hw, idx, ENETC_TBCIR); + if (tx_ring->next_to_use != tx_ring->next_to_clean && + !is_enetc_rev1(si)) { + tx_ring->next_to_use = 0; + tx_ring->next_to_clean = 0; + enetc_txbdr_wr(hw, idx, ENETC_TBPIR, 0); + enetc_txbdr_wr(hw, idx, ENETC_TBCIR, 0); + } + /* enable Tx ints by setting pkt thr to 1 */ enetc_txbdr_wr(hw, idx, ENETC_TBICR0, ENETC_TBICR0_ICEN | 0x1); From 2725d84efe2582c0a4b907e74a689d26b2dbd382 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Tue, 24 Mar 2026 14:21:20 +0800 Subject: [PATCH 316/712] net: enetc: add graceful stop to safely reinitialize the TX Ring For ENETC v4, the PIR and CIR will be reset if they are not equal when reinitializing the TX BD ring. However, resetting the PIR and CIR alone is insufficient. When a link-down event occurs while the TX BD ring is transmitting frames, subsequent reinitialization of the TX BD ring may cause it to malfunction. For example, the below steps can reproduce the problem. 1. Unplug the cable when the TX BD ring is busy transmitting frames. 2. Disable the network interface (ifconfig eth0 down). 3. Re-enable the network interface (ifconfig eth0 up). 4. Plug in the cable, the TX BD ring may fail to transmit packets. When the link-down event occurs, enetc4_pl_mac_link_down() only clears PMa_COMMAND_CONFIG[TX_EN] to disable MAC transmit data path. It doesn't set PORT[TXDIS] to 1 to flush the TX BD ring. Therefore, reinitializing the TX BD ring at this point is unsafe. To safely reinitialize the TX BD ring after a link-down event, we checked with the NETC IP team, a proper Ethernet MAC graceful stop is necessary. Therefore, add the Ethernet MAC graceful stop to the link-down event handler enetc4_pl_mac_link_down(). Fixes: 99100d0d9922 ("net: enetc: add preliminary support for i.MX95 ENETC PF") Signed-off-by: Wei Fang Reviewed-by: Claudiu Manoil Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324062121.2745033-3-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/freescale/enetc/enetc4_hw.h | 11 ++ .../net/ethernet/freescale/enetc/enetc4_pf.c | 111 +++++++++++++++--- 2 files changed, 108 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h index 3ed0f7a02767..719c88ceb801 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h @@ -134,6 +134,12 @@ /* Port operational register */ #define ENETC4_POR 0x4100 +#define POR_TXDIS BIT(0) +#define POR_RXDIS BIT(1) + +/* Port status register */ +#define ENETC4_PSR 0x4104 +#define PSR_RX_BUSY BIT(1) /* Port traffic class a transmit maximum SDU register */ #define ENETC4_PTCTMSDUR(a) ((a) * 0x20 + 0x4208) @@ -173,6 +179,11 @@ /* Port internal MDIO base address, use to access PCS */ #define ENETC4_PM_IMDIO_BASE 0x5030 +/* Port MAC 0/1 Interrupt Event Register */ +#define ENETC4_PM_IEVENT(mac) (0x5040 + (mac) * 0x400) +#define PM_IEVENT_TX_EMPTY BIT(5) +#define PM_IEVENT_RX_EMPTY BIT(6) + /* Port MAC 0/1 Pause Quanta Register */ #define ENETC4_PM_PAUSE_QUANTA(mac) (0x5054 + (mac) * 0x400) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 689b9f13c5eb..53cecbb23a97 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -444,20 +444,11 @@ static void enetc4_set_trx_frame_size(struct enetc_pf *pf) enetc4_pf_reset_tc_msdu(&si->hw); } -static void enetc4_enable_trx(struct enetc_pf *pf) -{ - struct enetc_hw *hw = &pf->si->hw; - - /* Enable port transmit/receive */ - enetc_port_wr(hw, ENETC4_POR, 0); -} - static void enetc4_configure_port(struct enetc_pf *pf) { enetc4_configure_port_si(pf); enetc4_set_trx_frame_size(pf); enetc_set_default_rss_key(pf); - enetc4_enable_trx(pf); } static int enetc4_init_ntmp_user(struct enetc_si *si) @@ -801,15 +792,105 @@ static void enetc4_set_tx_pause(struct enetc_pf *pf, int num_rxbdr, bool tx_paus enetc_port_wr(hw, ENETC4_PPAUOFFTR, pause_off_thresh); } -static void enetc4_enable_mac(struct enetc_pf *pf, bool en) +static void enetc4_mac_wait_tx_empty(struct enetc_si *si, int mac) { + u32 val; + + if (read_poll_timeout(enetc_port_rd, val, + val & PM_IEVENT_TX_EMPTY, + 100, 10000, false, &si->hw, + ENETC4_PM_IEVENT(mac))) + dev_warn(&si->pdev->dev, + "MAC %d TX is not empty\n", mac); +} + +static void enetc4_mac_tx_graceful_stop(struct enetc_pf *pf) +{ + struct enetc_hw *hw = &pf->si->hw; + struct enetc_si *si = pf->si; + u32 val; + + val = enetc_port_rd(hw, ENETC4_POR); + val |= POR_TXDIS; + enetc_port_wr(hw, ENETC4_POR, val); + + enetc4_mac_wait_tx_empty(si, 0); + if (si->hw_features & ENETC_SI_F_QBU) + enetc4_mac_wait_tx_empty(si, 1); + + val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0)); + val &= ~PM_CMD_CFG_TX_EN; + enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val); +} + +static void enetc4_mac_tx_enable(struct enetc_pf *pf) +{ + struct enetc_hw *hw = &pf->si->hw; struct enetc_si *si = pf->si; u32 val; val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0)); - val &= ~(PM_CMD_CFG_TX_EN | PM_CMD_CFG_RX_EN); - val |= en ? (PM_CMD_CFG_TX_EN | PM_CMD_CFG_RX_EN) : 0; + val |= PM_CMD_CFG_TX_EN; + enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val); + val = enetc_port_rd(hw, ENETC4_POR); + val &= ~POR_TXDIS; + enetc_port_wr(hw, ENETC4_POR, val); +} + +static void enetc4_mac_wait_rx_empty(struct enetc_si *si, int mac) +{ + u32 val; + + if (read_poll_timeout(enetc_port_rd, val, + val & PM_IEVENT_RX_EMPTY, + 100, 10000, false, &si->hw, + ENETC4_PM_IEVENT(mac))) + dev_warn(&si->pdev->dev, + "MAC %d RX is not empty\n", mac); +} + +static void enetc4_mac_rx_graceful_stop(struct enetc_pf *pf) +{ + struct enetc_hw *hw = &pf->si->hw; + struct enetc_si *si = pf->si; + u32 val; + + if (si->hw_features & ENETC_SI_F_QBU) { + val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(1)); + val &= ~PM_CMD_CFG_RX_EN; + enetc_port_wr(hw, ENETC4_PM_CMD_CFG(1), val); + enetc4_mac_wait_rx_empty(si, 1); + } + + val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(0)); + val &= ~PM_CMD_CFG_RX_EN; + enetc_port_wr(hw, ENETC4_PM_CMD_CFG(0), val); + enetc4_mac_wait_rx_empty(si, 0); + + if (read_poll_timeout(enetc_port_rd, val, + !(val & PSR_RX_BUSY), + 100, 10000, false, hw, + ENETC4_PSR)) + dev_warn(&si->pdev->dev, "Port RX busy\n"); + + val = enetc_port_rd(hw, ENETC4_POR); + val |= POR_RXDIS; + enetc_port_wr(hw, ENETC4_POR, val); +} + +static void enetc4_mac_rx_enable(struct enetc_pf *pf) +{ + struct enetc_hw *hw = &pf->si->hw; + struct enetc_si *si = pf->si; + u32 val; + + val = enetc_port_rd(hw, ENETC4_POR); + val &= ~POR_RXDIS; + enetc_port_wr(hw, ENETC4_POR, val); + + val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0)); + val |= PM_CMD_CFG_RX_EN; enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val); } @@ -853,7 +934,8 @@ static void enetc4_pl_mac_link_up(struct phylink_config *config, enetc4_set_hd_flow_control(pf, hd_fc); enetc4_set_tx_pause(pf, priv->num_rx_rings, tx_pause); enetc4_set_rx_pause(pf, rx_pause); - enetc4_enable_mac(pf, true); + enetc4_mac_tx_enable(pf); + enetc4_mac_rx_enable(pf); } static void enetc4_pl_mac_link_down(struct phylink_config *config, @@ -862,7 +944,8 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config, { struct enetc_pf *pf = phylink_to_enetc_pf(config); - enetc4_enable_mac(pf, false); + enetc4_mac_rx_graceful_stop(pf); + enetc4_mac_tx_graceful_stop(pf); } static const struct phylink_mac_ops enetc_pl_mac_ops = { From f2df9567b123145a07ee4ea7440e233f5d0232cc Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Tue, 24 Mar 2026 14:21:21 +0800 Subject: [PATCH 317/712] net: enetc: do not access non-existent registers on pseudo MAC The ENETC4_PM_IEVENT and ENETC4_PM_CMD_CFG registers do not exist on the ENETC pseudo MAC, so the driver should prevent from accessing them. Fixes: 5175c1e4adca ("net: enetc: add basic support for the ENETC with pseudo MAC for i.MX94") Signed-off-by: Wei Fang Tested-by: Claudiu Manoil Reviewed-by: Claudiu Manoil Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324062121.2745033-4-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc4_pf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 53cecbb23a97..56899f2254aa 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -814,6 +814,9 @@ static void enetc4_mac_tx_graceful_stop(struct enetc_pf *pf) val |= POR_TXDIS; enetc_port_wr(hw, ENETC4_POR, val); + if (enetc_is_pseudo_mac(si)) + return; + enetc4_mac_wait_tx_empty(si, 0); if (si->hw_features & ENETC_SI_F_QBU) enetc4_mac_wait_tx_empty(si, 1); @@ -856,6 +859,9 @@ static void enetc4_mac_rx_graceful_stop(struct enetc_pf *pf) struct enetc_si *si = pf->si; u32 val; + if (enetc_is_pseudo_mac(si)) + goto check_rx_busy; + if (si->hw_features & ENETC_SI_F_QBU) { val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(1)); val &= ~PM_CMD_CFG_RX_EN; @@ -868,6 +874,7 @@ static void enetc4_mac_rx_graceful_stop(struct enetc_pf *pf) enetc_port_wr(hw, ENETC4_PM_CMD_CFG(0), val); enetc4_mac_wait_rx_empty(si, 0); +check_rx_busy: if (read_poll_timeout(enetc_port_rd, val, !(val & PSR_RX_BUSY), 100, 10000, false, hw, From 2428083101f6883f979cceffa76cd8440751ffe6 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Tue, 24 Mar 2026 16:06:44 +0800 Subject: [PATCH 318/712] net: qrtr: replace qrtr_tx_flow radix_tree with xarray to fix memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __radix_tree_create() allocates and links intermediate nodes into the tree one by one. If a subsequent allocation fails, the already-linked nodes remain in the tree with no corresponding leaf entry. These orphaned internal nodes are never reclaimed because radix_tree_for_each_slot() only visits slots containing leaf values. The radix_tree API is deprecated in favor of xarray. As suggested by Matthew Wilcox, migrate qrtr_tx_flow from radix_tree to xarray instead of fixing the radix_tree itself [1]. xarray properly handles cleanup of internal nodes — xa_destroy() frees all internal xarray nodes when the qrtr_node is released, preventing the leak. [1] https://lore.kernel.org/all/20260225071623.41275-1-jiayuan.chen@linux.dev/T/ Reported-by: syzbot+006987d1be3586e13555@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/000000000000bfba3a060bf4ffcf@google.com/T/ Fixes: 5fdeb0d372ab ("net: qrtr: Implement outgoing flow control") Signed-off-by: Jiayuan Chen Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324080645.290197-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/qrtr/af_qrtr.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 55fd2dd37588..d77e9c8212da 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -118,7 +118,7 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports); * @ep: endpoint * @ref: reference count for node * @nid: node id - * @qrtr_tx_flow: tree of qrtr_tx_flow, keyed by node << 32 | port + * @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port * @qrtr_tx_lock: lock for qrtr_tx_flow inserts * @rx_queue: receive queue * @item: list item for broadcast list @@ -129,7 +129,7 @@ struct qrtr_node { struct kref ref; unsigned int nid; - struct radix_tree_root qrtr_tx_flow; + struct xarray qrtr_tx_flow; struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */ struct sk_buff_head rx_queue; @@ -172,6 +172,7 @@ static void __qrtr_node_release(struct kref *kref) struct qrtr_tx_flow *flow; unsigned long flags; void __rcu **slot; + unsigned long index; spin_lock_irqsave(&qrtr_nodes_lock, flags); /* If the node is a bridge for other nodes, there are possibly @@ -189,11 +190,9 @@ static void __qrtr_node_release(struct kref *kref) skb_queue_purge(&node->rx_queue); /* Free tx flow counters */ - radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) { - flow = *slot; - radix_tree_iter_delete(&node->qrtr_tx_flow, &iter, slot); + xa_for_each(&node->qrtr_tx_flow, index, flow) kfree(flow); - } + xa_destroy(&node->qrtr_tx_flow); kfree(node); } @@ -228,9 +227,7 @@ static void qrtr_tx_resume(struct qrtr_node *node, struct sk_buff *skb) key = remote_node << 32 | remote_port; - rcu_read_lock(); - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); - rcu_read_unlock(); + flow = xa_load(&node->qrtr_tx_flow, key); if (flow) { spin_lock(&flow->resume_tx.lock); flow->pending = 0; @@ -269,12 +266,13 @@ static int qrtr_tx_wait(struct qrtr_node *node, int dest_node, int dest_port, return 0; mutex_lock(&node->qrtr_tx_lock); - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); + flow = xa_load(&node->qrtr_tx_flow, key); if (!flow) { flow = kzalloc_obj(*flow); if (flow) { init_waitqueue_head(&flow->resume_tx); - if (radix_tree_insert(&node->qrtr_tx_flow, key, flow)) { + if (xa_err(xa_store(&node->qrtr_tx_flow, key, flow, + GFP_KERNEL))) { kfree(flow); flow = NULL; } @@ -326,9 +324,7 @@ static void qrtr_tx_flow_failed(struct qrtr_node *node, int dest_node, unsigned long key = (u64)dest_node << 32 | dest_port; struct qrtr_tx_flow *flow; - rcu_read_lock(); - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); - rcu_read_unlock(); + flow = xa_load(&node->qrtr_tx_flow, key); if (flow) { spin_lock_irq(&flow->resume_tx.lock); flow->tx_failed = 1; @@ -599,7 +595,7 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) node->nid = QRTR_EP_NID_AUTO; node->ep = ep; - INIT_RADIX_TREE(&node->qrtr_tx_flow, GFP_KERNEL); + xa_init(&node->qrtr_tx_flow); mutex_init(&node->qrtr_tx_lock); qrtr_node_assign(node, nid); @@ -627,6 +623,7 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep) struct qrtr_tx_flow *flow; struct sk_buff *skb; unsigned long flags; + unsigned long index; void __rcu **slot; mutex_lock(&node->ep_lock); @@ -649,10 +646,8 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep) /* Wake up any transmitters waiting for resume-tx from the node */ mutex_lock(&node->qrtr_tx_lock); - radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) { - flow = *slot; + xa_for_each(&node->qrtr_tx_flow, index, flow) wake_up_interruptible_all(&flow->resume_tx); - } mutex_unlock(&node->qrtr_tx_lock); qrtr_node_release(node); From ae05340ccaa9d347fe85415609e075545bec589f Mon Sep 17 00:00:00 2001 From: Yochai Eisenrich Date: Wed, 25 Mar 2026 00:49:25 +0200 Subject: [PATCH 319/712] net: ipv6: ndisc: fix ndisc_ra_useropt to initialize nduseropt_padX fields to zero to prevent an info-leak When processing Router Advertisements with user options the kernel builds an RTM_NEWNDUSEROPT netlink message. The nduseroptmsg struct has three padding fields that are never zeroed and can leak kernel data The fix is simple, just zeroes the padding fields. Fixes: 31910575a9de ("[IPv6]: Export userland ND options through netlink (RDNSS support)") Signed-off-by: Yochai Eisenrich Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324224925.2437775-1-echelonh@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ndisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f6a5d8c73af9..186e60c79214 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1209,6 +1209,9 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type; ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code; ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3; + ndmsg->nduseropt_pad1 = 0; + ndmsg->nduseropt_pad2 = 0; + ndmsg->nduseropt_pad3 = 0; memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3); From 90c5def10bea574b101b7a520c015ca81742183f Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 2 Mar 2026 18:22:52 -0400 Subject: [PATCH 320/712] iommu: Do not call drivers for empty gathers An empty gather is coded with start=U64_MAX, end=0 and several drivers go on to convert that to a size with: end - start + 1 Which gives 2 for an empty gather. This then causes Weird Stuff to happen (for example an UBSAN splat in VT-d) that is hopefully harmless, but maybe not. Prevent drivers from being called right in iommu_iotlb_sync(). Auditing shows that AMD, Intel, Mediatek and RSIC-V drivers all do things on these empty gathers. Further, there are several callers that can trigger empty gathers, especially in unusual conditions. For example iommu_map_nosync() will call a 0 size unmap on some error paths. Also in VFIO, iommupt and other places. Cc: stable@vger.kernel.org Reported-by: Janusz Krzysztofik Closes: https://lore.kernel.org/r/11145826.aFP6jjVeTY@jkrzyszt-mobl2.ger.corp.intel.com Signed-off-by: Jason Gunthorpe Reviewed-by: Lu Baolu Reviewed-by: Samiullah Khawaja Reviewed-by: Robin Murphy Reviewed-by: Vasant Hegde Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 54b8b48c762e..555597b54083 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -980,7 +980,8 @@ static inline void iommu_flush_iotlb_all(struct iommu_domain *domain) static inline void iommu_iotlb_sync(struct iommu_domain *domain, struct iommu_iotlb_gather *iotlb_gather) { - if (domain->ops->iotlb_sync) + if (domain->ops->iotlb_sync && + likely(iotlb_gather->start < iotlb_gather->end)) domain->ops->iotlb_sync(domain, iotlb_gather); iommu_iotlb_gather_init(iotlb_gather); From ee6e69d032550687a3422504bfca3f834c7b5061 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 2 Mar 2026 18:22:53 -0400 Subject: [PATCH 321/712] iommupt: Fix short gather if the unmap goes into a large mapping unmap has the odd behavior that it can unmap more than requested if the ending point lands within the middle of a large or contiguous IOPTE. In this case the gather should flush everything unmapped which can be larger than what was requested to be unmapped. The gather was only flushing the range requested to be unmapped, not extending to the extra range, resulting in a short invalidation if the caller hits this special condition. This was found by the new invalidation/gather test I am adding in preparation for ARMv8. Claude deduced the root cause. As far as I remember nothing relies on unmapping a large entry, so this is likely not a triggerable bug. Cc: stable@vger.kernel.org Fixes: 7c53f4238aa8 ("iommupt: Add unmap_pages op") Signed-off-by: Jason Gunthorpe Reviewed-by: Lu Baolu Reviewed-by: Samiullah Khawaja Reviewed-by: Vasant Hegde Signed-off-by: Joerg Roedel --- drivers/iommu/generic_pt/iommu_pt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h index 3e33fe64feab..7e7a6e7abdee 100644 --- a/drivers/iommu/generic_pt/iommu_pt.h +++ b/drivers/iommu/generic_pt/iommu_pt.h @@ -1057,7 +1057,7 @@ size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova, pt_walk_range(&range, __unmap_range, &unmap); - gather_range_pages(iotlb_gather, iommu_table, iova, len, + gather_range_pages(iotlb_gather, iommu_table, iova, unmap.unmapped, &unmap.free_list); return unmap.unmapped; From 8b72aa5704c77380742346d4ac755b074b7f9eaa Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Thu, 26 Mar 2026 09:17:19 -0700 Subject: [PATCH 322/712] iommupt/amdv1: mark amdv1pt_install_leaf_entry as __always_inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After enabling CONFIG_GCOV_KERNEL and CONFIG_GCOV_PROFILE_ALL, following build failure is observed under GCC 14.2.1: In function 'amdv1pt_install_leaf_entry', inlined from '__do_map_single_page' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:650:3, inlined from '__map_single_page0' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:661:1, inlined from 'pt_descend' at drivers/iommu/generic_pt/fmt/../pt_iter.h:391:9, inlined from '__do_map_single_page' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:657:10, inlined from '__map_single_page1.constprop' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:661:1: ././include/linux/compiler_types.h:706:45: error: call to '__compiletime_assert_71' declared with attribute error: FIELD_PREP: value too large for the field 706 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ...... drivers/iommu/generic_pt/fmt/amdv1.h:220:26: note: in expansion of macro 'FIELD_PREP' 220 | FIELD_PREP(AMDV1PT_FMT_OA, | ^~~~~~~~~~ In the path '__do_map_single_page()', level 0 always invokes 'pt_install_leaf_entry(&pts, map->oa, PAGE_SHIFT, …)'. At runtime that lands in the 'if (oasz_lg2 == isz_lg2)' arm of 'amdv1pt_install_leaf_entry()'; the contiguous-only 'else' block is unreachable for 4 KiB pages. With CONFIG_GCOV_KERNEL + CONFIG_GCOV_PROFILE_ALL, the extra instrumentation changes GCC's inlining so that the "dead" 'else' branch still gets instantiated. The compiler constant-folds the contiguous OA expression, runs the 'FIELD_PREP()' compile-time check, and produces: FIELD_PREP: value too large for the field gcov-enabled builds therefore fail even though the code path never executes. Fix this by marking amdv1pt_install_leaf_entry as __always_inline. Fixes: dcd6a011a8d5 ("iommupt: Add map_pages op") Suggested-by: Jason Gunthorpe Signed-off-by: Sherry Yang Signed-off-by: Joerg Roedel --- drivers/iommu/generic_pt/fmt/amdv1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/generic_pt/fmt/amdv1.h b/drivers/iommu/generic_pt/fmt/amdv1.h index 3b2c41d9654d..8d11b08291d7 100644 --- a/drivers/iommu/generic_pt/fmt/amdv1.h +++ b/drivers/iommu/generic_pt/fmt/amdv1.h @@ -191,7 +191,7 @@ static inline enum pt_entry_type amdv1pt_load_entry_raw(struct pt_state *pts) } #define pt_load_entry_raw amdv1pt_load_entry_raw -static inline void +static __always_inline void amdv1pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa, unsigned int oasz_lg2, const struct pt_write_attrs *attrs) From 7f138de156b20d9f9da6f72f90b63c01941d97d3 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 27 Mar 2026 01:14:12 +0800 Subject: [PATCH 323/712] auxdisplay: line-display: fix NULL dereference in linedisp_release linedisp_release() currently retrieves the enclosing struct linedisp via to_linedisp(). That lookup depends on the attachment list, but the attachment may already have been removed before put_device() invokes the release callback. This can happen in linedisp_unregister(), and can also be reached from some linedisp_register() error paths. In that case, to_linedisp() returns NULL and linedisp_release() dereferences it while freeing the display resources. The struct device released here is the embedded linedisp->dev used by linedisp_register(), so retrieve the enclosing object directly with container_of() instead. Fixes: 66c93809487e ("auxdisplay: linedisp: encapsulate container_of usage within to_linedisp") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Geert Uytterhoeven Signed-off-by: Andy Shevchenko --- drivers/auxdisplay/line-display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c index 81b4aac65807..fb6d9294140d 100644 --- a/drivers/auxdisplay/line-display.c +++ b/drivers/auxdisplay/line-display.c @@ -365,7 +365,7 @@ static DEFINE_IDA(linedisp_id); static void linedisp_release(struct device *dev) { - struct linedisp *linedisp = to_linedisp(dev); + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); kfree(linedisp->map); kfree(linedisp->message); From 0b475e91ecc2313207196c6d7fd5c53e1a878525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:10 +0200 Subject: [PATCH 324/712] drm/i915/dsi: Don't do DSC horizontal timing adjustments in command mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop adjusting the horizontal timing values based on the compression ratio in command mode. Bspec seems to be telling us to do this only in video mode, and this is also how the Windows driver does things. This should also fix a div-by-zero on some machines because the adjusted htotal ends up being so small that we end up with line_time_us==0 when trying to determine the vtotal value in command mode. Note that this doesn't actually make the display on the Huawei Matebook E work, but at least the kernel no longer explodes when the driver loads. Cc: stable@vger.kernel.org Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12045 Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-2-ville.syrjala@linux.intel.com Fixes: 53693f02d80e ("drm/i915/dsi: account for DSC in horizontal timings") Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index c04327979678..a763f2b13ff2 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -888,7 +888,7 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder, * non-compressed link speeds, and simplifies down to the ratio between * compressed and non-compressed bpp. */ - if (crtc_state->dsc.compression_enable) { + if (is_vid_mode(intel_dsi) && crtc_state->dsc.compression_enable) { mul = fxp_q4_to_int(crtc_state->dsc.compressed_bpp_x16); div = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); } @@ -1502,7 +1502,7 @@ static void gen11_dsi_get_timings(struct intel_encoder *encoder, struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - if (pipe_config->dsc.compressed_bpp_x16) { + if (is_vid_mode(intel_dsi) && pipe_config->dsc.compressed_bpp_x16) { int div = fxp_q4_to_int(pipe_config->dsc.compressed_bpp_x16); int mul = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); From ca7fc6a8ae28eaec8c194dc5f8db03f928b2e454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:11 +0200 Subject: [PATCH 325/712] drm/i915/dsi: s/eotp_pkt/eot_pkt/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eotp == "End of Transmission Packet". Drop the redundant extra 'p' from 'eotp_pkt', and make the thing a boolean while at it. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 2 +- drivers/gpu/drm/i915/display/intel_dsi.h | 3 +-- drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 4 ++-- drivers/gpu/drm/i915/display/vlv_dsi.c | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index a763f2b13ff2..6ea37929198c 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -711,7 +711,7 @@ gen11_dsi_configure_transcoder(struct intel_encoder *encoder, dsi_trans = dsi_port_to_transcoder(port); tmp = intel_de_read(display, DSI_TRANS_FUNC_CONF(dsi_trans)); - if (intel_dsi->eotp_pkt) + if (intel_dsi->eot_pkt) tmp &= ~EOTP_DISABLED; else tmp |= EOTP_DISABLED; diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index 489d26ffd235..8e39d2b52c54 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -80,8 +80,7 @@ struct intel_dsi { /* NON_BURST_SYNC_PULSE, NON_BURST_SYNC_EVENTS, or BURST_MODE */ int video_mode; - /* eot for MIPI_EOT_DISABLE register */ - u8 eotp_pkt; + bool eot_pkt; u8 clock_stop; u8 escape_clk_div; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 18755a8e613d..51f6a5b82cb2 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -718,7 +718,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) "burst" : ""); drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); - drm_printf(&p, "Eot %s\n", str_enabled_disabled(intel_dsi->eotp_pkt)); + drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); drm_printf(&p, "Clockstop %s\n", str_enabled_disabled(!intel_dsi->clock_stop)); drm_printf(&p, "Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); if (intel_dsi->dual_link == DSI_DUAL_LINK_FRONT_BACK) @@ -770,7 +770,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); - intel_dsi->eotp_pkt = mipi_config->eot_pkt_disabled ? 0 : 1; + intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; intel_dsi->clock_stop = mipi_config->enable_clk_stop ? 1 : 0; intel_dsi->lane_count = mipi_config->lane_cnt + 1; intel_dsi->pixel_format = diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index 36591d724638..d4db73c184e5 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -1367,7 +1367,7 @@ static void intel_dsi_prepare(struct intel_encoder *encoder, } tmp = 0; - if (intel_dsi->eotp_pkt == 0) + if (!intel_dsi->eot_pkt) tmp |= EOT_DISABLE; if (intel_dsi->clock_stop) tmp |= CLOCKSTOP; From 81ec9556ad69444899e8255652ec80972c09df14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:12 +0200 Subject: [PATCH 326/712] drm/i915/dsi: Make 'clock_stop' boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DSI 'clock_stop' parameter is a boolean, so use a real 'bool' for it. And pimp the debug print while at it. Note that we also remove the incorrect negation of the value in the debug print. That has been there since the code was introduced in commit 2ab8b458c6a1 ("drm/i915: Add support for Generic MIPI panel driver"). An earlier version of the patch https://lore.kernel.org/intel-gfx/1397454507-10273-5-git-send-email-shobhit.kumar@intel.com/ got it right, but looks like it got fumbled while dealing with other review comments. v2: Highlight the removal of the '!' (Jani) Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-4-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dsi.h | 2 +- drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index 8e39d2b52c54..0023ac341aa0 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -81,7 +81,7 @@ struct intel_dsi { int video_mode; bool eot_pkt; - u8 clock_stop; + bool clock_stop; u8 escape_clk_div; u8 dual_link; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 51f6a5b82cb2..23da7f5f9578 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -719,7 +719,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); - drm_printf(&p, "Clockstop %s\n", str_enabled_disabled(!intel_dsi->clock_stop)); + drm_printf(&p, "Clock stop during BLLP %s\n", str_enabled_disabled(intel_dsi->clock_stop)); drm_printf(&p, "Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); if (intel_dsi->dual_link == DSI_DUAL_LINK_FRONT_BACK) drm_printf(&p, "Dual link: DSI_DUAL_LINK_FRONT_BACK\n"); @@ -771,7 +771,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; - intel_dsi->clock_stop = mipi_config->enable_clk_stop ? 1 : 0; + intel_dsi->clock_stop = mipi_config->enable_clk_stop; intel_dsi->lane_count = mipi_config->lane_cnt + 1; intel_dsi->pixel_format = vbt_to_dsi_pixel_format(mipi_config->videomode_color_format); From 765a2635cd257545e732eee13f1e75774c0b79f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:13 +0200 Subject: [PATCH 327/712] drm/i915/dsi: Fill BLLPs with blanking packets if requested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TGL/ADL DSI can be configured to fill all BLLPs with blanking packets. Currently we enable that always, but the VBT actually tells us whether this is desired or not. Hook that up. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-5-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 9 +++++---- drivers/gpu/drm/i915/display/icl_dsi_regs.h | 2 +- drivers/gpu/drm/i915/display/intel_dsi.h | 1 + drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 2 ++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index 6ea37929198c..45ba02486c56 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -765,10 +765,11 @@ gen11_dsi_configure_transcoder(struct intel_encoder *encoder, } } - if (DISPLAY_VER(display) >= 12) { - if (is_vid_mode(intel_dsi)) - tmp |= BLANKING_PACKET_ENABLE; - } + if (DISPLAY_VER(display) >= 12 && + is_vid_mode(intel_dsi) && intel_dsi->blanking_pkt) + tmp |= BLANKING_PACKET_ENABLE; + else + tmp &= ~BLANKING_PACKET_ENABLE; /* program DSI operation mode */ if (is_vid_mode(intel_dsi)) { diff --git a/drivers/gpu/drm/i915/display/icl_dsi_regs.h b/drivers/gpu/drm/i915/display/icl_dsi_regs.h index b601b7632339..641e8f0b8cdb 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi_regs.h +++ b/drivers/gpu/drm/i915/display/icl_dsi_regs.h @@ -232,7 +232,7 @@ #define CALIBRATION_DISABLED (0x0 << 4) #define CALIBRATION_ENABLED_INITIAL_ONLY (0x2 << 4) #define CALIBRATION_ENABLED_INITIAL_PERIODIC (0x3 << 4) -#define BLANKING_PACKET_ENABLE (1 << 2) +#define BLANKING_PACKET_ENABLE (1 << 2) /* tgl+ */ #define S3D_ORIENTATION_LANDSCAPE (1 << 1) #define EOTP_DISABLED (1 << 0) diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index 0023ac341aa0..f55d48e43af1 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -80,6 +80,7 @@ struct intel_dsi { /* NON_BURST_SYNC_PULSE, NON_BURST_SYNC_EVENTS, or BURST_MODE */ int video_mode; + bool blanking_pkt; bool eot_pkt; bool clock_stop; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 23da7f5f9578..c544871dac0b 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -718,6 +718,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) "burst" : ""); drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); + drm_printf(&p, "Blanking packets during BLLP %s\n", str_enabled_disabled(intel_dsi->blanking_pkt)); drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); drm_printf(&p, "Clock stop during BLLP %s\n", str_enabled_disabled(intel_dsi->clock_stop)); drm_printf(&p, "Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); @@ -770,6 +771,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); + intel_dsi->blanking_pkt = mipi_config->blanking_packets_during_bllp; intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; intel_dsi->clock_stop = mipi_config->enable_clk_stop; intel_dsi->lane_count = mipi_config->lane_cnt + 1; From e8a7efa81d734e1c8f4d91e658a162ea32f39dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:14 +0200 Subject: [PATCH 328/712] drm/i915/dsi: Place clock into LP during LPM if requested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TGL/ADL DSI can be configured to place the clock lane into LP state during LPM, if otherwise configured for continuous HS clock. Hook that up. VBT tells us whether this should be done. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-6-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 6 ++++++ drivers/gpu/drm/i915/display/icl_dsi_regs.h | 1 + drivers/gpu/drm/i915/display/intel_dsi.h | 1 + drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 2 ++ 4 files changed, 10 insertions(+) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index 45ba02486c56..afbaa0465842 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -729,6 +729,12 @@ gen11_dsi_configure_transcoder(struct intel_encoder *encoder, else tmp |= CLK_HS_CONTINUOUS; + if (DISPLAY_VER(display) >= 12 && + intel_dsi->lp_clock_during_lpm) + tmp |= LP_CLK_DURING_LPM; + else + tmp &= ~LP_CLK_DURING_LPM; + /* configure buffer threshold limit to minimum */ tmp &= ~PIX_BUF_THRESHOLD_MASK; tmp |= PIX_BUF_THRESHOLD_1_4; diff --git a/drivers/gpu/drm/i915/display/icl_dsi_regs.h b/drivers/gpu/drm/i915/display/icl_dsi_regs.h index 641e8f0b8cdb..55ab57adcb0f 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi_regs.h +++ b/drivers/gpu/drm/i915/display/icl_dsi_regs.h @@ -227,6 +227,7 @@ #define CLK_ENTER_LP_AFTER_DATA (0x0 << 8) #define CLK_HS_OR_LP (0x2 << 8) #define CLK_HS_CONTINUOUS (0x3 << 8) +#define LP_CLK_DURING_LPM (1 << 7) /* tgl+ */ #define LINK_CALIBRATION_MASK (0x3 << 4) #define LINK_CALIBRATION_SHIFT 4 #define CALIBRATION_DISABLED (0x0 << 4) diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index f55d48e43af1..9fcdabbf3740 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -80,6 +80,7 @@ struct intel_dsi { /* NON_BURST_SYNC_PULSE, NON_BURST_SYNC_EVENTS, or BURST_MODE */ int video_mode; + bool lp_clock_during_lpm; bool blanking_pkt; bool eot_pkt; bool clock_stop; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index c544871dac0b..fe12041e913c 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -718,6 +718,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) "burst" : ""); drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); + drm_printf(&p, "LP clock during LPM %s\n", str_enabled_disabled(intel_dsi->lp_clock_during_lpm)); drm_printf(&p, "Blanking packets during BLLP %s\n", str_enabled_disabled(intel_dsi->blanking_pkt)); drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); drm_printf(&p, "Clock stop during BLLP %s\n", str_enabled_disabled(intel_dsi->clock_stop)); @@ -771,6 +772,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); + intel_dsi->lp_clock_during_lpm = mipi_config->lp_clock_during_lpm; intel_dsi->blanking_pkt = mipi_config->blanking_packets_during_bllp; intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; intel_dsi->clock_stop = mipi_config->enable_clk_stop; From fde38b106d50f59f904c72544aa34e41adb14fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 23 Mar 2026 12:16:08 +0200 Subject: [PATCH 329/712] drm/i915/selftests: Nuke live_forcewake_domains selftest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The live_forcewake_domains selftest doesn't really test anything particularly sensible. It only runs on platforms that have RMbus unclaimer error detection, but that only catches display registers which the test doesn't even access. I suppose if we really wanted to we might try to make the test exercise the GT FIFO instead by writing GT registers as fast as possible, and then checking GTFIFODBG to see if the FIFO has overflowed. But dunno if there's much point in that. I think a GT FIFO overflow might even be fatal to the machine. So in its current for the test doesn't really make sense, and it's in the way of moving all the RMbus noclaim stuff to the display driver side. So let's just get rid of it. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260323101609.8391-2-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- drivers/gpu/drm/i915/selftests/intel_uncore.c | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/drivers/gpu/drm/i915/selftests/intel_uncore.c b/drivers/gpu/drm/i915/selftests/intel_uncore.c index 507bf42a1aaf..514d2200751b 100644 --- a/drivers/gpu/drm/i915/selftests/intel_uncore.c +++ b/drivers/gpu/drm/i915/selftests/intel_uncore.c @@ -272,67 +272,6 @@ static int live_forcewake_ops(void *arg) return err; } -static int live_forcewake_domains(void *arg) -{ -#define FW_RANGE 0x40000 - struct intel_gt *gt = arg; - struct intel_uncore *uncore = gt->uncore; - struct drm_i915_private *i915 = gt->i915; - struct intel_display *display = i915->display; - unsigned long *valid; - u32 offset; - int err; - - if (!HAS_FPGA_DBG_UNCLAIMED(display) && - !IS_VALLEYVIEW(i915) && - !IS_CHERRYVIEW(i915)) - return 0; - - /* - * This test may lockup the machine or cause GPU hangs afterwards. - */ - if (!IS_ENABLED(CONFIG_DRM_I915_SELFTEST_BROKEN)) - return 0; - - valid = bitmap_zalloc(FW_RANGE, GFP_KERNEL); - if (!valid) - return -ENOMEM; - - intel_uncore_forcewake_get(uncore, FORCEWAKE_ALL); - - check_for_unclaimed_mmio(uncore); - for (offset = 0; offset < FW_RANGE; offset += 4) { - i915_reg_t reg = { offset }; - - intel_uncore_posting_read_fw(uncore, reg); - if (!check_for_unclaimed_mmio(uncore)) - set_bit(offset, valid); - } - - intel_uncore_forcewake_put(uncore, FORCEWAKE_ALL); - - err = 0; - for_each_set_bit(offset, valid, FW_RANGE) { - i915_reg_t reg = { offset }; - - iosf_mbi_punit_acquire(); - intel_uncore_forcewake_reset(uncore); - iosf_mbi_punit_release(); - - check_for_unclaimed_mmio(uncore); - - intel_uncore_posting_read_fw(uncore, reg); - if (check_for_unclaimed_mmio(uncore)) { - pr_err("Unclaimed mmio read to register 0x%04x\n", - offset); - err = -EINVAL; - } - } - - bitmap_free(valid); - return err; -} - static int live_fw_table(void *arg) { struct intel_gt *gt = arg; @@ -348,7 +287,6 @@ int intel_uncore_live_selftests(struct drm_i915_private *i915) static const struct i915_subtest tests[] = { SUBTEST(live_fw_table), SUBTEST(live_forcewake_ops), - SUBTEST(live_forcewake_domains), }; return intel_gt_live_subtests(tests, to_gt(i915)); From e012fa31f90de0928d85ab22d9cc5fc8fe84c5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 23 Mar 2026 12:16:09 +0200 Subject: [PATCH 330/712] drm/i915/uncore: Do GT FIFO checks in early sanitize and forcewake get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're mixing up the GT FIFO debug checks (overflows and such) with RMbus unclaimed register checks. The two are quite different things as RMbus is only relevant for display registers, and the GT FIFO only relevant for GT registers. Split the GT FIFO debugs out from the unclaimed register logic and just do the checks during forcewake_get() and early init. That is still sufficient to detect if any errors have happened. Any errors would anyway be caused by overflowing the FIFO rather than accessing specific registers, so trying to figure out exactly when the error happened isn't particularly useful. To fix such issues we'd rather have to do something to slow down the rate at which registers are accessed (eg. increase GT_FIFO_NUM_RESERVED_ENTRIES or something). Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260323101609.8391-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/intel_uncore.c | 72 ++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 5b698d4d7a7f..170e83a8c9fc 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -399,6 +399,35 @@ static void fw_domains_get_with_thread_status(struct intel_uncore *uncore, __gen6_gt_wait_for_thread_c0(uncore); } +static void +gen6_check_for_fifo_debug(struct intel_uncore *uncore) +{ + u32 fifodbg; + + fifodbg = __raw_uncore_read32(uncore, GTFIFODBG); + + if (unlikely(fifodbg)) { + drm_dbg(&uncore->i915->drm, "GTFIFODBG = 0x08%x\n", fifodbg); + __raw_uncore_write32(uncore, GTFIFODBG, fifodbg); + } +} + +static void +fw_domains_get_normal_fifo(struct intel_uncore *uncore, + enum forcewake_domains fw_domains) +{ + gen6_check_for_fifo_debug(uncore); + fw_domains_get_normal(uncore, fw_domains); +} + +static void +fw_domains_get_with_thread_status_fifo(struct intel_uncore *uncore, + enum forcewake_domains fw_domains) +{ + gen6_check_for_fifo_debug(uncore); + fw_domains_get_with_thread_status(uncore, fw_domains); +} + static inline u32 fifo_free_entries(struct intel_uncore *uncore) { u32 count = __raw_uncore_read32(uncore, GTFIFOCTL); @@ -561,21 +590,6 @@ vlv_check_for_unclaimed_mmio(struct intel_uncore *uncore) return true; } -static bool -gen6_check_for_fifo_debug(struct intel_uncore *uncore) -{ - u32 fifodbg; - - fifodbg = __raw_uncore_read32(uncore, GTFIFODBG); - - if (unlikely(fifodbg)) { - drm_dbg(&uncore->i915->drm, "GTFIFODBG = 0x08%x\n", fifodbg); - __raw_uncore_write32(uncore, GTFIFODBG, fifodbg); - } - - return fifodbg; -} - static bool check_for_unclaimed_mmio(struct intel_uncore *uncore) { @@ -592,9 +606,6 @@ check_for_unclaimed_mmio(struct intel_uncore *uncore) if (intel_uncore_has_dbg_unclaimed(uncore)) ret |= vlv_check_for_unclaimed_mmio(uncore); - if (intel_uncore_has_fifo(uncore)) - ret |= gen6_check_for_fifo_debug(uncore); - return ret; } @@ -611,6 +622,9 @@ static void forcewake_early_sanitize(struct intel_uncore *uncore, GT_FIFO_CTL_RC6_POLICY_STALL); } + if (intel_uncore_has_fifo(uncore)) + gen6_check_for_fifo_debug(uncore); + iosf_mbi_punit_acquire(); intel_uncore_forcewake_reset(uncore); if (restore_forcewake) { @@ -2155,6 +2169,14 @@ static const struct intel_uncore_fw_get uncore_get_thread_status = { .force_wake_get = fw_domains_get_with_thread_status }; +static const struct intel_uncore_fw_get uncore_get_normal_fifo = { + .force_wake_get = fw_domains_get_normal_fifo, +}; + +static const struct intel_uncore_fw_get uncore_get_thread_status_fifo = { + .force_wake_get = fw_domains_get_with_thread_status_fifo +}; + static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) { struct drm_i915_private *i915 = uncore->i915; @@ -2218,13 +2240,19 @@ static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA, FORCEWAKE_MEDIA_GEN9, FORCEWAKE_ACK_MEDIA_GEN9); } else if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) { - uncore->fw_get_funcs = &uncore_get_normal; + if (intel_uncore_has_fifo(uncore)) + uncore->fw_get_funcs = &uncore_get_normal_fifo; + else + uncore->fw_get_funcs = &uncore_get_normal; fw_domain_init(uncore, FW_DOMAIN_ID_RENDER, FORCEWAKE_VLV, FORCEWAKE_ACK_VLV); fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA, FORCEWAKE_MEDIA_VLV, FORCEWAKE_ACK_MEDIA_VLV); } else if (IS_HASWELL(i915) || IS_BROADWELL(i915)) { - uncore->fw_get_funcs = &uncore_get_thread_status; + if (intel_uncore_has_fifo(uncore)) + uncore->fw_get_funcs = &uncore_get_thread_status_fifo; + else + uncore->fw_get_funcs = &uncore_get_thread_status; fw_domain_init(uncore, FW_DOMAIN_ID_RENDER, FORCEWAKE_MT, FORCEWAKE_ACK_HSW); } else if (IS_IVYBRIDGE(i915)) { @@ -2239,7 +2267,7 @@ static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) * (correctly) interpreted by the test below as MT * forcewake being disabled. */ - uncore->fw_get_funcs = &uncore_get_thread_status; + uncore->fw_get_funcs = &uncore_get_thread_status_fifo; /* We need to init first for ECOBUS access and then * determine later if we want to reinit, in case of MT access is @@ -2270,7 +2298,7 @@ static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) FORCEWAKE, FORCEWAKE_ACK); } } else if (GRAPHICS_VER(i915) == 6) { - uncore->fw_get_funcs = &uncore_get_thread_status; + uncore->fw_get_funcs = &uncore_get_thread_status_fifo; fw_domain_init(uncore, FW_DOMAIN_ID_RENDER, FORCEWAKE, FORCEWAKE_ACK); } From 5e67ba9bb531e1ec6599a82a065dea9040b9ce50 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 25 Mar 2026 15:41:52 +0800 Subject: [PATCH 331/712] net/ipv6: ioam6: prevent schema length wraparound in trace fill ioam6_fill_trace_data() stores the schema contribution to the trace length in a u8. With bit 22 enabled and the largest schema payload, sclen becomes 1 + 1020 / 4, wraps from 256 to 0, and bypasses the remaining-space check. __ioam6_fill_trace_data() then positions the write cursor without reserving the schema area but still copies the 4-byte schema header and the full schema payload, overrunning the trace buffer. Keep sclen in an unsigned int so the remaining-space check and the write cursor calculation both see the full schema length. Fixes: 8c6f6fa67726 ("ipv6: ioam: IOAM Generic Netlink API") Signed-off-by: Pengpeng Hou Reviewed-by: Justin Iurman Signed-off-by: David S. Miller --- net/ipv6/ioam6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index b76f89d92e7b..3978773bec42 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -708,7 +708,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, struct ioam6_namespace *ns, struct ioam6_trace_hdr *trace, struct ioam6_schema *sc, - u8 sclen, bool is_input) + unsigned int sclen, bool is_input) { struct net_device *dev = skb_dst_dev(skb); struct timespec64 ts; @@ -939,7 +939,7 @@ void ioam6_fill_trace_data(struct sk_buff *skb, bool is_input) { struct ioam6_schema *sc; - u8 sclen = 0; + unsigned int sclen = 0; /* Skip if Overflow flag is set */ From bb417456c7814d1493d98b7dd9c040bf3ce3b4ed Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Wed, 25 Mar 2026 12:20:53 +0100 Subject: [PATCH 332/712] tg3: Fix race for querying speed/duplex When driver signals carrier up via netif_carrier_on() its internal link_up state isn't updated immediately. This leads to inconsistent speed/duplex in /proc/net/bonding/bondX where the speed and duplex is shown as unknown while ethtool shows correct values. Fix this by using netif_carrier_ok() for link checking in get_ksettings function. Fixes: 84421b99cedc ("tg3: Update link_up flag for phylib devices") Signed-off-by: Thomas Bogendoerfer Reviewed-by: Pavan Chebbi Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 21a5dd342724..73a4b569b03e 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -12299,7 +12299,7 @@ static int tg3_get_link_ksettings(struct net_device *dev, ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, advertising); - if (netif_running(dev) && tp->link_up) { + if (netif_running(dev) && netif_carrier_ok(dev)) { cmd->base.speed = tp->link_config.active_speed; cmd->base.duplex = tp->link_config.active_duplex; ethtool_convert_legacy_u32_to_link_mode( From 5597dd284ff8c556c0b00f6a34473677426e3f81 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 25 Mar 2026 12:51:30 +0000 Subject: [PATCH 333/712] net: ti: icssg-prueth: fix missing data copy and wrong recycle in ZC RX dispatch emac_dispatch_skb_zc() allocates a new skb via napi_alloc_skb() but never copies the packet data from the XDP buffer into it. The skb is passed up the stack containing uninitialized heap memory instead of the actual received packet, leaking kernel heap contents to userspace. Copy the received packet data from the XDP buffer into the skb using skb_copy_to_linear_data(). Additionally, remove the skb_mark_for_recycle() call since the skb is backed by the NAPI page frag allocator, not page_pool. Marking a non-page_pool skb for recycle causes the free path to return pages to a page_pool that does not own them, corrupting page_pool state. The non-ZC path (emac_rx_packet) does not have these issues because it uses napi_build_skb() to wrap the existing page_pool page directly, requiring no copy, and correctly marks for recycle since the page comes from page_pool_dev_alloc_pages(). Fixes: 7a64bb388df3 ("net: ti: icssg-prueth: Add AF_XDP zero copy for RX") Signed-off-by: David Carlier Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/icssg/icssg_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index fd4e7622f123..a28a608f9bf4 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -902,6 +902,7 @@ static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp, skb_reserve(skb, headroom); skb_put(skb, pkt_len); + skb_copy_to_linear_data(skb, xdp->data, pkt_len); skb->dev = ndev; /* RX HW timestamp */ @@ -912,7 +913,6 @@ static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp, skb->offload_fwd_mark = emac->offload_fwd_mark; skb->protocol = eth_type_trans(skb, ndev); - skb_mark_for_recycle(skb); napi_gro_receive(&emac->napi_rx, skb); ndev->stats.rx_bytes += pkt_len; ndev->stats.rx_packets++; From 73ff3916d803f7ca3a4325af649e46ff89d6c3a7 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Fri, 27 Mar 2026 18:12:15 +0800 Subject: [PATCH 334/712] ALSA: hda/realtek: change quirk for HP OmniBook 7 Laptop 16-bh0xxx HP OmniBook 7 Laptop 16-bh0xxx has the same PCI subsystem ID 0x103c8e60, and the ALC245 on it needs this quirk to control the mute LED. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221214 Cc: Tested-by: Artem S. Tashkinov Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260327101215.481108-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index b6a58852752a..6787e54fcfe6 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4122,6 +4122,7 @@ enum { ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY, ALC245_FIXUP_BASS_HP_DAC, ALC245_FIXUP_ACER_MICMUTE_LED, + ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6651,6 +6652,12 @@ static const struct hda_fixup alc269_fixups[] = { .v.func = alc285_fixup_hp_coef_micmute_led, .chained = true, .chain_id = ALC2XX_FIXUP_HEADSET_MIC, + }, + [ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_coefbit, + .chained = true, + .chain_id = ALC287_FIXUP_CS35L41_I2C_2, } }; @@ -7177,7 +7184,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e8a, "HP NexusX", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), From fffca572f9ca51607f180a37d0c898404c8f9112 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 15:06:31 +0100 Subject: [PATCH 335/712] mpage: Provide variant of mpage_writepages() with own optional folio handler Some filesystems need to treat some folios specially (for example for inodes with inline data). Doing the handling in their .writepages method in a race-free manner results in duplicating some of the writeback internals. So provide generalized version of mpage_writepages() that allows filesystem to provide a handler called for each folio which can handle the folio in a special way. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260326140635.15895-3-jack@suse.cz Signed-off-by: Jan Kara --- fs/mpage.c | 30 ++++++++++++++++++++++++------ include/linux/mpage.h | 11 +++++++++-- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/fs/mpage.c b/fs/mpage.c index 7dae5afc2b9e..b3d9f231a04a 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -646,17 +646,24 @@ static int mpage_write_folio(struct writeback_control *wbc, struct folio *folio, } /** - * mpage_writepages - walk the list of dirty pages of the given address space & writepage() all of them + * __mpage_writepages - walk the list of dirty pages of the given address space + * & writepage() all of them * @mapping: address space structure to write * @wbc: subtract the number of written pages from *@wbc->nr_to_write * @get_block: the filesystem's block mapper function. + * @write_folio: handler to call for each folio before calling + * mpage_write_folio() * * This is a library function, which implements the writepages() - * address_space_operation. + * address_space_operation. It calls @write_folio handler for each folio. If + * the handler returns value > 0, it calls mpage_write_folio() to do the + * folio writeback. */ int -mpage_writepages(struct address_space *mapping, - struct writeback_control *wbc, get_block_t get_block) +__mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t get_block, + int (*write_folio)(struct folio *folio, + struct writeback_control *wbc)) { struct mpage_data mpd = { .get_block = get_block, @@ -666,11 +673,22 @@ mpage_writepages(struct address_space *mapping, int error; blk_start_plug(&plug); - while ((folio = writeback_iter(mapping, wbc, folio, &error))) + while ((folio = writeback_iter(mapping, wbc, folio, &error))) { + if (write_folio) { + error = write_folio(folio, wbc); + /* + * == 0 means folio is handled, < 0 means error. In + * both cases hand back control to writeback_iter() + */ + if (error <= 0) + continue; + /* Let mpage_write_folio() handle the folio. */ + } error = mpage_write_folio(wbc, folio, &mpd); + } if (mpd.bio) mpage_bio_submit_write(mpd.bio); blk_finish_plug(&plug); return error; } -EXPORT_SYMBOL(mpage_writepages); +EXPORT_SYMBOL(__mpage_writepages); diff --git a/include/linux/mpage.h b/include/linux/mpage.h index 1bdc39daac0a..358946990bfa 100644 --- a/include/linux/mpage.h +++ b/include/linux/mpage.h @@ -17,7 +17,14 @@ struct readahead_control; void mpage_readahead(struct readahead_control *, get_block_t get_block); int mpage_read_folio(struct folio *folio, get_block_t get_block); -int mpage_writepages(struct address_space *mapping, - struct writeback_control *wbc, get_block_t get_block); +int __mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t get_block, + int (*write_folio)(struct folio *folio, + struct writeback_control *wbc)); +static inline int mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t get_block) +{ + return __mpage_writepages(mapping, wbc, get_block, NULL); +} #endif From 102e57d56f81fa5c5ed78f576101d1bf1b3e6fe2 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 15:06:32 +0100 Subject: [PATCH 336/712] udf: Fix race between file type conversion and writeback udf_setsize() can race with udf_writepages() as follows: udf_setsize() udf_writepages() if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) err = udf_expand_file_adinicb(inode); err = udf_extend_file(inode, newsize); udf_adinicb_writepages() memcpy_from_file_folio() - crash because inode size is too big. Fix the problem by checking the file type under folio lock in udf_handle_page_wb() handler called from __mpage_writepages() which properly serializes with udf_expand_file_adinicb(). Reported-by: Jianzhou Zhao Link: https://lore.kernel.org/all/f622c01.67ac.19cdbdd777d.Coremail.luckd0g@163.com Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260326140635.15895-4-jack@suse.cz Signed-off-by: Jan Kara --- fs/udf/inode.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 7fae8002344a..23e894092dab 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -181,22 +181,23 @@ static void udf_write_failed(struct address_space *mapping, loff_t to) } } -static int udf_adinicb_writepages(struct address_space *mapping, - struct writeback_control *wbc) +static int udf_handle_page_wb(struct folio *folio, + struct writeback_control *wbc) { - struct inode *inode = mapping->host; + struct inode *inode = folio->mapping->host; struct udf_inode_info *iinfo = UDF_I(inode); - struct folio *folio = NULL; - int error = 0; - while ((folio = writeback_iter(mapping, wbc, folio, &error))) { - BUG_ON(!folio_test_locked(folio)); - BUG_ON(folio->index != 0); - memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio, + /* + * Inodes in the normal format are handled by the generic code. This + * check is race-free as the folio lock protects us from inode type + * conversion. + */ + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) + return 1; + + memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio, 0, i_size_read(inode)); - folio_unlock(folio); - } - + folio_unlock(folio); mark_inode_dirty(inode); return 0; } @@ -204,12 +205,8 @@ static int udf_adinicb_writepages(struct address_space *mapping, static int udf_writepages(struct address_space *mapping, struct writeback_control *wbc) { - struct inode *inode = mapping->host; - struct udf_inode_info *iinfo = UDF_I(inode); - - if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) - return udf_adinicb_writepages(mapping, wbc); - return mpage_writepages(mapping, wbc, udf_get_block_wb); + return __mpage_writepages(mapping, wbc, udf_get_block_wb, + udf_handle_page_wb); } static void udf_adinicb_read_folio(struct folio *folio) From 2feec5ae5df785658924ab6bd91280dc3926507c Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Thu, 5 Feb 2026 13:34:14 +0100 Subject: [PATCH 337/712] accel/qaic: Handle DBC deactivation if the owner went away When a DBC is released, the device sends a QAIC_TRANS_DEACTIVATE_FROM_DEV transaction to the host over the QAIC_CONTROL MHI channel. QAIC handles this by calling decode_deactivate() to release the resources allocated for that DBC. Since that handling is done in the qaic_manage_ioctl() context, if the user goes away before receiving and handling the deactivation, the host will be out-of-sync with the DBCs available for use, and the DBC resources will not be freed unless the device is removed. If another user loads and requests to activate a network, then the device assigns the same DBC to that network, QAIC will "indefinitely" wait for dbc->in_use = false, leading the user process to hang. As a solution to this, handle QAIC_TRANS_DEACTIVATE_FROM_DEV transactions that are received after the user has gone away. Fixes: 129776ac2e38 ("accel/qaic: Add control path") Signed-off-by: Youssef Samir Reviewed-by: Lizhi Hou Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://patch.msgid.link/20260205123415.3870898-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_control.c | 47 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index f698d5dfd326..43f84d438960 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -914,7 +914,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len */ return -ENODEV; - if (status) { + if (usr && status) { /* * Releasing resources failed on the device side, which puts * us in a bind since they may still be in use, so enable the @@ -1109,6 +1109,9 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u mutex_lock(&qdev->cntl_mutex); if (!list_empty(&elem.list)) list_del(&elem.list); + /* resp_worker() processed the response but the wait was interrupted */ + else if (ret == -ERESTARTSYS) + ret = 0; if (!ret && !elem.buf) ret = -ETIMEDOUT; else if (ret > 0 && !elem.buf) @@ -1419,9 +1422,49 @@ static void resp_worker(struct work_struct *work) } mutex_unlock(&qdev->cntl_mutex); - if (!found) + if (!found) { + /* + * The user might have gone away at this point without waiting + * for QAIC_TRANS_DEACTIVATE_FROM_DEV transaction coming from + * the device. If this is not handled correctly, the host will + * not know that the DBC[n] has been freed on the device. + * Due to this failure in synchronization between the device and + * the host, if another user requests to activate a network, and + * the device assigns DBC[n] again, save_dbc_buf() will hang, + * waiting for dbc[n]->in_use to be set to false, which will not + * happen unless the qaic_dev_reset_clean_local_state() gets + * called by resetting the device (or re-inserting the module). + * + * As a solution, we look for QAIC_TRANS_DEACTIVATE_FROM_DEV + * transactions in the message before disposing of it, then + * handle releasing the DBC resources. + * + * Since the user has gone away, if the device could not + * deactivate the network (status != 0), there is no way to + * enable and reassign the DBC to the user. We can put trust in + * the device that it will release all the active DBCs in + * response to the QAIC_TRANS_TERMINATE_TO_DEV transaction, + * otherwise, the user can issue an soc_reset to the device. + */ + u32 msg_count = le32_to_cpu(msg->hdr.count); + u32 msg_len = le32_to_cpu(msg->hdr.len); + u32 len = 0; + int j; + + for (j = 0; j < msg_count && len < msg_len; ++j) { + struct wire_trans_hdr *trans_hdr; + + trans_hdr = (struct wire_trans_hdr *)(msg->data + len); + if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) { + if (decode_deactivate(qdev, trans_hdr, &len, NULL)) + len += le32_to_cpu(trans_hdr->len); + } else { + len += le32_to_cpu(trans_hdr->len); + } + } /* request must have timed out, drop packet */ kfree(msg); + } kfree(resp); } From 9b836641d3bfa1ab096ec6263f0fa6880cb9c5ef Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Fri, 20 Mar 2026 16:08:26 +0000 Subject: [PATCH 338/712] rust: helpers: Add bindings/wrappers for dma_resv_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just for basic usage in the DRM shmem abstractions for implied locking, not intended as a full DMA Reservation abstraction yet. Cc: Sumit Semwal Cc: Christian König Signed-off-by: Asahi Lina Signed-off-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Lyude Paul Reviewed-by: Janne Grunau Reviewed-by: Daniel Almeida Acked-by: David Airlie Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260320-gpuvm-rust-v5-2-76fd44f17a87@google.com Signed-off-by: Danilo Krummrich --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/dma-resv.c | 14 ++++++++++++++ rust/helpers/helpers.c | 1 + 4 files changed, 17 insertions(+) create mode 100644 rust/helpers/dma-resv.c diff --git a/MAINTAINERS b/MAINTAINERS index 55bf6bf26107..b01791963e25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7534,6 +7534,7 @@ F: include/linux/*fence.h F: include/linux/dma-buf.h F: include/linux/dma-buf/ F: include/linux/dma-resv.h +F: rust/helpers/dma-resv.c K: \bdma_(?:buf|fence|resv)\b DMA GENERIC OFFLOAD ENGINE SUBSYSTEM diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index dbb765a9fdbd..563863d96d38 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/dma-resv.c b/rust/helpers/dma-resv.c new file mode 100644 index 000000000000..71914d8241e2 --- /dev/null +++ b/rust/helpers/dma-resv.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +__rust_helper +int rust_helper_dma_resv_lock(struct dma_resv *obj, struct ww_acquire_ctx *ctx) +{ + return dma_resv_lock(obj, ctx); +} + +__rust_helper void rust_helper_dma_resv_unlock(struct dma_resv *obj) +{ + dma_resv_unlock(obj); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a53929ce52a3..b6b20ad2e0e6 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -28,6 +28,7 @@ #include "cred.c" #include "device.c" #include "dma.c" +#include "dma-resv.c" #include "drm.c" #include "err.c" #include "irq.c" From e5046823f8fa3677341b541a25af2fcb99a5b1e0 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 25 Mar 2026 20:29:20 -0700 Subject: [PATCH 339/712] lib/crypto: chacha: Zeroize permuted_state before it leaves scope Since the ChaCha permutation is invertible, the local variable 'permuted_state' is sufficient to compute the original 'state', and thus the key, even after the permutation has been done. While the kernel is quite inconsistent about zeroizing secrets on the stack (and some prominent userspace crypto libraries don't bother at all since it's not guaranteed to work anyway), the kernel does try to do it as a best practice, especially in cases involving the RNG. Thus, explicitly zeroize 'permuted_state' before it goes out of scope. Fixes: c08d0e647305 ("crypto: chacha20 - Add a generic ChaCha20 stream cipher implementation") Cc: stable@vger.kernel.org Acked-by: Ard Biesheuvel Link: https://lore.kernel.org/r/20260326032920.39408-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- lib/crypto/chacha-block-generic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/crypto/chacha-block-generic.c b/lib/crypto/chacha-block-generic.c index 77f68de71066..4a6d627580cb 100644 --- a/lib/crypto/chacha-block-generic.c +++ b/lib/crypto/chacha-block-generic.c @@ -87,6 +87,8 @@ void chacha_block_generic(struct chacha_state *state, &out[i * sizeof(u32)]); state->x[12]++; + + chacha_zeroize_state(&permuted_state); } EXPORT_SYMBOL(chacha_block_generic); @@ -110,5 +112,7 @@ void hchacha_block_generic(const struct chacha_state *state, memcpy(&out[0], &permuted_state.x[0], 16); memcpy(&out[4], &permuted_state.x[12], 16); + + chacha_zeroize_state(&permuted_state); } EXPORT_SYMBOL(hchacha_block_generic); From 89b4964c0456d9939a9f5187891a36bb87111e58 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 16 Mar 2026 17:16:10 -0400 Subject: [PATCH 340/712] rust: drm: gem: Add raw_dma_resv() function For retrieving a pointer to the struct dma_resv for a given GEM object. We also introduce it in a new trait, BaseObjectPrivate, which we automatically implement for all gem objects and don't expose to users outside of the crate. Signed-off-by: Lyude Paul Reviewed-by: Janne Grunau Tested-by: Janne Grunau Tested-by: Deborah Brouwer Link: https://patch.msgid.link/20260316211646.650074-3-lyude@redhat.com [ Fix incorrect reference in safety comment. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/drm/gem/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 6cc441ee5b63..a78b98c40d56 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -215,6 +215,18 @@ fn create_mmap_offset(&self) -> Result { impl BaseObject for T {} +/// Crate-private base operations shared by all GEM object classes. +#[expect(unused)] +pub(crate) trait BaseObjectPrivate: IntoGEMObject { + /// Return a pointer to this object's dma_resv. + fn raw_dma_resv(&self) -> *mut bindings::dma_resv { + // SAFETY: `self.as_raw()` always returns a valid pointer to the base DRM GEM object. + unsafe { (*self.as_raw()).resv } + } +} + +impl BaseObjectPrivate for T {} + /// A base GEM object. /// /// # Invariants From 80df573af9ef3aa63e1bacb6e17d57a7cd69afe2 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Mon, 16 Mar 2026 17:16:13 -0400 Subject: [PATCH 341/712] rust: drm: gem: shmem: Add DRM shmem helper abstraction The DRM shmem helper includes common code useful for drivers which allocate GEM objects as anonymous shmem. Add a Rust abstraction for this. Drivers can choose the raw GEM implementation or the shmem layer, depending on their needs. Signed-off-by: Asahi Lina Signed-off-by: Daniel Almeida Reviewed-by: Daniel Almeida Signed-off-by: Lyude Paul Reviewed-by: Janne Grunau Tested-by: Deborah Brouwer Link: https://patch.msgid.link/20260316211646.650074-6-lyude@redhat.com [ * DRM_GEM_SHMEM_HELPER is a tristate; when a module driver selects it, it becomes =m. The Rust kernel crate and its C helpers are always built into vmlinux and can't reference symbols from a module, causing link errors. Thus, add RUST_DRM_GEM_SHMEM_HELPER bool Kconfig that selects DRM_GEM_SHMEM_HELPER, forcing it built-in when Rust drivers need it; use cfg(CONFIG_RUST_DRM_GEM_SHMEM_HELPER) for the shmem module. * Add cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), expect(unused)) on pub(crate) use impl_aref_for_gem_obj and BaseObjectPrivate, so that unused warnings are suppressed when shmem is not enabled. * Enable const_refs_to_static (stabilized in 1.83) to prevent build errors with older compilers. * Use &raw const for bindings::drm_gem_shmem_vm_ops and add #[allow(unused_unsafe, reason = "Safe since Rust 1.82.0")]. * Fix incorrect C Header path and minor spelling and formatting issues. * Drop shmem::Object::sg_table() as the current implementation is unsound. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/Kconfig | 7 + rust/bindings/bindings_helper.h | 2 + rust/helpers/drm.c | 56 +++++++- rust/kernel/drm/gem/mod.rs | 7 +- rust/kernel/drm/gem/shmem.rs | 228 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 6 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/drm/gem/shmem.rs diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0d0657dd1b41..0f68446c9122 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -258,6 +258,13 @@ config DRM_GEM_SHMEM_HELPER help Choose this if you need the GEM shmem helper functions +config RUST_DRM_GEM_SHMEM_HELPER + bool + depends on DRM && MMU + select DRM_GEM_SHMEM_HELPER + help + Choose this if you need the GEM shmem helper functions In Rust + config DRM_SUBALLOC_HELPER tristate depends on DRM diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 563863d96d38..eda8f50d3a3c 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/drm.c b/rust/helpers/drm.c index fe226f7b53ef..65f3f22b0e1d 100644 --- a/rust/helpers/drm.c +++ b/rust/helpers/drm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #ifdef CONFIG_DRM @@ -21,4 +22,57 @@ rust_helper_drm_vma_node_offset_addr(struct drm_vma_offset_node *node) return drm_vma_node_offset_addr(node); } -#endif +#ifdef CONFIG_DRM_GEM_SHMEM_HELPER +__rust_helper void +rust_helper_drm_gem_shmem_object_free(struct drm_gem_object *obj) +{ + return drm_gem_shmem_object_free(obj); +} + +__rust_helper void +rust_helper_drm_gem_shmem_object_print_info(struct drm_printer *p, unsigned int indent, + const struct drm_gem_object *obj) +{ + drm_gem_shmem_object_print_info(p, indent, obj); +} + +__rust_helper int +rust_helper_drm_gem_shmem_object_pin(struct drm_gem_object *obj) +{ + return drm_gem_shmem_object_pin(obj); +} + +__rust_helper void +rust_helper_drm_gem_shmem_object_unpin(struct drm_gem_object *obj) +{ + drm_gem_shmem_object_unpin(obj); +} + +__rust_helper struct sg_table * +rust_helper_drm_gem_shmem_object_get_sg_table(struct drm_gem_object *obj) +{ + return drm_gem_shmem_object_get_sg_table(obj); +} + +__rust_helper int +rust_helper_drm_gem_shmem_object_vmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + return drm_gem_shmem_object_vmap(obj, map); +} + +__rust_helper void +rust_helper_drm_gem_shmem_object_vunmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + drm_gem_shmem_object_vunmap(obj, map); +} + +__rust_helper int +rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + return drm_gem_shmem_object_mmap(obj, vma); +} + +#endif /* CONFIG_DRM_GEM_SHMEM_HELPER */ +#endif /* CONFIG_DRM */ diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index a78b98c40d56..75acda7ba500 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -26,6 +26,9 @@ ptr::NonNull, // }; +#[cfg(CONFIG_RUST_DRM_GEM_SHMEM_HELPER)] +pub mod shmem; + /// A macro for implementing [`AlwaysRefCounted`] for any GEM object type. /// /// Since all GEM objects use the same refcounting scheme. @@ -60,6 +63,8 @@ unsafe fn dec_ref(obj: core::ptr::NonNull) { } }; } +#[cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), allow(unused))] +pub(crate) use impl_aref_for_gem_obj; /// A type alias for retrieving a [`Driver`]s [`DriverFile`] implementation from its /// [`DriverObject`] implementation. @@ -216,7 +221,7 @@ fn create_mmap_offset(&self) -> Result { impl BaseObject for T {} /// Crate-private base operations shared by all GEM object classes. -#[expect(unused)] +#[cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), expect(unused))] pub(crate) trait BaseObjectPrivate: IntoGEMObject { /// Return a pointer to this object's dma_resv. fn raw_dma_resv(&self) -> *mut bindings::dma_resv { diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs new file mode 100644 index 000000000000..d025fb035195 --- /dev/null +++ b/rust/kernel/drm/gem/shmem.rs @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! DRM GEM shmem helper objects +//! +//! C header: [`include/linux/drm/drm_gem_shmem_helper.h`](srctree/include/drm/drm_gem_shmem_helper.h) + +// TODO: +// - There are a number of spots here that manually acquire/release the DMA reservation lock using +// dma_resv_(un)lock(). In the future we should add support for ww mutex, expose a method to +// acquire a reference to the WwMutex, and then use that directly instead of the C functions here. + +use crate::{ + container_of, + drm::{ + device, + driver, + gem, + private::Sealed, // + }, + error::to_result, + prelude::*, + types::{ + ARef, + Opaque, // + }, // +}; +use core::{ + ops::{ + Deref, + DerefMut, // + }, + ptr::NonNull, +}; +use gem::{ + BaseObjectPrivate, + DriverObject, + IntoGEMObject, // +}; + +/// A struct for controlling the creation of shmem-backed GEM objects. +/// +/// This is used with [`Object::new()`] to control various properties that can only be set when +/// initially creating a shmem-backed GEM object. +#[derive(Default)] +pub struct ObjectConfig<'a, T: DriverObject> { + /// Whether to set the write-combine map flag. + pub map_wc: bool, + + /// Reuse the DMA reservation from another GEM object. + /// + /// The newly created [`Object`] will hold an owned refcount to `parent_resv_obj` if specified. + pub parent_resv_obj: Option<&'a Object>, +} + +/// A shmem-backed GEM object. +/// +/// # Invariants +/// +/// `obj` contains a valid initialized `struct drm_gem_shmem_object` for the lifetime of this +/// object. +#[repr(C)] +#[pin_data] +pub struct Object { + #[pin] + obj: Opaque, + /// Parent object that owns this object's DMA reservation object. + parent_resv_obj: Option>>, + #[pin] + inner: T, +} + +super::impl_aref_for_gem_obj!(impl for Object where T: DriverObject); + +// SAFETY: All GEM objects are thread-safe. +unsafe impl Send for Object {} + +// SAFETY: All GEM objects are thread-safe. +unsafe impl Sync for Object {} + +impl Object { + /// `drm_gem_object_funcs` vtable suitable for GEM shmem objects. + const VTABLE: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { + free: Some(Self::free_callback), + open: Some(super::open_callback::), + close: Some(super::close_callback::), + print_info: Some(bindings::drm_gem_shmem_object_print_info), + export: None, + pin: Some(bindings::drm_gem_shmem_object_pin), + unpin: Some(bindings::drm_gem_shmem_object_unpin), + get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table), + vmap: Some(bindings::drm_gem_shmem_object_vmap), + vunmap: Some(bindings::drm_gem_shmem_object_vunmap), + mmap: Some(bindings::drm_gem_shmem_object_mmap), + status: None, + rss: None, + #[allow(unused_unsafe, reason = "Safe since Rust 1.82.0")] + // SAFETY: `drm_gem_shmem_vm_ops` is a valid, static const on the C side. + vm_ops: unsafe { &raw const bindings::drm_gem_shmem_vm_ops }, + evict: None, + }; + + /// Return a raw pointer to the embedded drm_gem_shmem_object. + fn as_raw_shmem(&self) -> *mut bindings::drm_gem_shmem_object { + self.obj.get() + } + + /// Create a new shmem-backed DRM object of the given size. + /// + /// Additional config options can be specified using `config`. + pub fn new( + dev: &device::Device, + size: usize, + config: ObjectConfig<'_, T>, + args: T::Args, + ) -> Result> { + let new: Pin> = KBox::try_pin_init( + try_pin_init!(Self { + obj <- Opaque::init_zeroed(), + parent_resv_obj: config.parent_resv_obj.map(|p| p.into()), + inner <- T::new(dev, size, args), + }), + GFP_KERNEL, + )?; + + // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. + unsafe { (*new.as_raw()).funcs = &Self::VTABLE }; + + // SAFETY: The arguments are all valid via the type invariants. + to_result(unsafe { bindings::drm_gem_shmem_init(dev.as_raw(), new.as_raw_shmem(), size) })?; + + // SAFETY: We never move out of `self`. + let new = KBox::into_raw(unsafe { Pin::into_inner_unchecked(new) }); + + // SAFETY: We're taking over the owned refcount from `drm_gem_shmem_init`. + let obj = unsafe { ARef::from_raw(NonNull::new_unchecked(new)) }; + + // Start filling out values from `config` + if let Some(parent_resv) = config.parent_resv_obj { + // SAFETY: We have yet to expose the new gem object outside of this function, so it is + // safe to modify this field. + unsafe { (*obj.obj.get()).base.resv = parent_resv.raw_dma_resv() }; + } + + // SAFETY: We have yet to expose this object outside of this function, so we're guaranteed + // to have exclusive access - thus making this safe to hold a mutable reference to. + let shmem = unsafe { &mut *obj.as_raw_shmem() }; + shmem.set_map_wc(config.map_wc); + + Ok(obj) + } + + /// Returns the `Device` that owns this GEM object. + pub fn dev(&self) -> &device::Device { + // SAFETY: `dev` will have been initialized in `Self::new()` by `drm_gem_shmem_init()`. + unsafe { device::Device::from_raw((*self.as_raw()).dev) } + } + + extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { + // SAFETY: + // - DRM always passes a valid gem object here + // - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that + // `obj` is contained within a drm_gem_shmem_object + let this = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) }; + + // SAFETY: + // - We're in free_callback - so this function is safe to call. + // - We won't be using the gem resources on `this` after this call. + unsafe { bindings::drm_gem_shmem_release(this) }; + + // SAFETY: + // - We verified above that `obj` is valid, which makes `this` valid + // - This function is set in AllocOps, so we know that `this` is contained within a + // `Object` + let this = unsafe { container_of!(Opaque::cast_from(this), Self, obj) }.cast_mut(); + + // SAFETY: We're recovering the Kbox<> we created in gem_create_object() + let _ = unsafe { KBox::from_raw(this) }; + } +} + +impl Deref for Object { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Object { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl Sealed for Object {} + +impl gem::IntoGEMObject for Object { + fn as_raw(&self) -> *mut bindings::drm_gem_object { + // SAFETY: + // - Our immutable reference is proof that this is safe to dereference. + // - `obj` is always a valid drm_gem_shmem_object via our type invariants. + unsafe { &raw mut (*self.obj.get()).base } + } + + unsafe fn from_raw<'a>(obj: *mut bindings::drm_gem_object) -> &'a Object { + // SAFETY: The safety contract of from_gem_obj() guarantees that `obj` is contained within + // `Self` + unsafe { + let obj = Opaque::cast_from(container_of!(obj, bindings::drm_gem_shmem_object, base)); + + &*container_of!(obj, Object, obj) + } + } +} + +impl driver::AllocImpl for Object { + type Driver = T::Driver; + + const ALLOC_OPS: driver::AllocOps = driver::AllocOps { + gem_create_object: None, + prime_handle_to_fd: None, + prime_fd_to_handle: None, + gem_prime_import: None, + gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_import_sg_table), + dumb_create: Some(bindings::drm_gem_shmem_dumb_create), + dumb_map_offset: None, + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e0837ffc91bf..40de00ce4f97 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -38,6 +38,7 @@ #![feature(const_option)] #![feature(const_ptr_write)] #![feature(const_refs_to_cell)] +#![feature(const_refs_to_static)] // // Stable since Rust 1.84.0. #![feature(strict_provenance)] From 25fe63db0024da172457055532c96bef924a8c78 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 25 Mar 2026 01:39:15 +0100 Subject: [PATCH 342/712] rust: uaccess: generalize write_dma() to accept any Coherent Generalize write_dma() from &Coherent<[u8]> to &Coherent where T: KnownSize + AsBytes + ?Sized. The function body only uses as_ptr() and size(), which work for any such T, so there is no reason to restrict it to byte slices. Acked-by: Miguel Ojeda Acked-by: Gary Guo Reviewed-by: Alexandre Courbot Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260325003921.3420-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/uaccess.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index e26ef90ba8ad..6c9c1cce3c63 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -12,6 +12,7 @@ ffi::{c_char, c_void}, fs::file, prelude::*, + ptr::KnownSize, transmute::{AsBytes, FromBytes}, }; use core::mem::{size_of, MaybeUninit}; @@ -524,7 +525,12 @@ pub fn write_slice(&mut self, data: &[u8]) -> Result { /// writer.write_dma(alloc, 0, 256) /// } /// ``` - pub fn write_dma(&mut self, alloc: &Coherent<[u8]>, offset: usize, count: usize) -> Result { + pub fn write_dma( + &mut self, + alloc: &Coherent, + offset: usize, + count: usize, + ) -> Result { let len = alloc.size(); if offset.checked_add(count).ok_or(EOVERFLOW)? > len { return Err(ERANGE); From d1619a433806c61d240a1eb9b4f03cb33ac40fce Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 25 Mar 2026 01:39:16 +0100 Subject: [PATCH 343/712] rust: dma: generalize BinaryWriter impl for Coherent Generalize the BinaryWriter implementation from Coherent<[u8]> to Coherent where T: KnownSize + AsBytes + ?Sized. The implementation only uses size() and write_dma(), neither of which depends on the inner type being a byte slice. This allows any Coherent allocation with an AsBytes inner type to be exposed as a debugfs binary file. Acked-by: Gary Guo Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260325003921.3420-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 3eef7c2396bb..7bc97f9f83fd 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -885,7 +885,7 @@ unsafe impl Send for Coherent {} // The safe methods only return metadata or raw pointers whose use requires `unsafe`. unsafe impl Sync for Coherent {} -impl debugfs::BinaryWriter for Coherent<[u8]> { +impl debugfs::BinaryWriter for Coherent { fn write_to_slice( &self, writer: &mut UserSliceWriter, From 15a4bb87abac5229a4c36e34d388c4279d984b96 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 25 Mar 2026 01:39:17 +0100 Subject: [PATCH 344/712] gpu: nova-core: use sized array for GSP log buffers Switch LogBuffer from Coherent<[u8]> (unsized) to Coherent<[u8; LOG_BUFFER_SIZE]> (sized). The buffer size is a compile-time constant (RM_LOG_BUFFER_NUM_PAGES * GSP_PAGE_SIZE), so a fixed-size array is more precise and avoids the need for the runtime length parameter of zeroed_slice(). Acked-by: Gary Guo Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260325003921.3420-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gsp.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 04e3976127cc..ba5b7f990031 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -42,6 +42,7 @@ /// Number of GSP pages to use in a RM log buffer. const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10; +const LOG_BUFFER_SIZE: usize = RM_LOG_BUFFER_NUM_PAGES * GSP_PAGE_SIZE; /// Array of page table entries, as understood by the GSP bootloader. #[repr(C)] @@ -77,24 +78,19 @@ fn entry(start: DmaAddress, index: usize) -> Result { /// then pp points to index into the buffer where the next logging entry will /// be written. Therefore, the logging data is valid if: /// 1 <= pp < sizeof(buffer)/sizeof(u64) -struct LogBuffer(Coherent<[u8]>); +struct LogBuffer(Coherent<[u8; LOG_BUFFER_SIZE]>); impl LogBuffer { /// Creates a new `LogBuffer` mapped on `dev`. fn new(dev: &device::Device) -> Result { - const NUM_PAGES: usize = RM_LOG_BUFFER_NUM_PAGES; - - let obj = Self(Coherent::::zeroed_slice( - dev, - NUM_PAGES * GSP_PAGE_SIZE, - GFP_KERNEL, - )?); + let obj = Self(Coherent::zeroed(dev, GFP_KERNEL)?); let start_addr = obj.0.dma_handle(); // SAFETY: `obj` has just been created and we are its sole user. - let pte_region = - unsafe { &mut obj.0.as_mut()[size_of::()..][..NUM_PAGES * size_of::()] }; + let pte_region = unsafe { + &mut obj.0.as_mut()[size_of::()..][..RM_LOG_BUFFER_NUM_PAGES * size_of::()] + }; // Write values one by one to avoid an on-stack instance of `PteArray`. for (i, chunk) in pte_region.chunks_exact_mut(size_of::()).enumerate() { From 86ab3e55673a7a49a841838776f1ab18d23a67b5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Mar 2026 20:26:08 +0000 Subject: [PATCH 345/712] ipv6: icmp: clear skb2->cb[] in ip6_err_gen_icmpv6_unreach() Sashiko AI-review observed: In ip6_err_gen_icmpv6_unreach(), the skb is an outer IPv4 ICMP error packet where its cb contains an IPv4 inet_skb_parm. When skb is cloned into skb2 and passed to icmp6_send(), it uses IP6CB(skb2). IP6CB interprets the IPv4 inet_skb_parm as an inet6_skb_parm. The cipso offset in inet_skb_parm.opt directly overlaps with dsthao in inet6_skb_parm at offset 18. If an attacker sends a forged ICMPv4 error with a CIPSO IP option, dsthao would be a non-zero offset. Inside icmp6_send(), mip6_addr_swap() is called and uses ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO). This would scan the inner, attacker-controlled IPv6 packet starting at that offset, potentially returning a fake TLV without checking if the remaining packet length can hold the full 18-byte struct ipv6_destopt_hao. Could mip6_addr_swap() then perform a 16-byte swap that extends past the end of the packet data into skb_shared_info? Should the cb array also be cleared in ip6_err_gen_icmpv6_unreach() and ip6ip6_err() to prevent this? This patch implements the first suggestion. I am not sure if ip6ip6_err() needs to be changed. A separate patch would be better anyway. Fixes: ca15a078bd90 ("sit: generate icmpv6 error when receiving icmpv4 error") Reported-by: Ido Schimmel Closes: https://sashiko.dev/#/patchset/20260326155138.2429480-1-edumazet%40google.com Signed-off-by: Eric Dumazet Cc: Oskar Kjos Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260326202608.2976021-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/icmp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 813d2e9edb8b..d5d23a9296ea 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -875,6 +875,9 @@ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type, if (!skb2) return 1; + /* Remove debris left by IPv4 stack. */ + memset(IP6CB(skb2), 0, sizeof(*IP6CB(skb2))); + skb_dst_drop(skb2); skb_pull(skb2, nhs); skb_reset_network_header(skb2); From 2edfa31769a4add828a7e604b21cb82aaaa05925 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Mar 2026 15:51:38 +0000 Subject: [PATCH 346/712] ip6_tunnel: clear skb2->cb[] in ip4ip6_err() Oskar Kjos reported the following problem. ip4ip6_err() calls icmp_send() on a cloned skb whose cb[] was written by the IPv6 receive path as struct inet6_skb_parm. icmp_send() passes IPCB(skb2) to __ip_options_echo(), which interprets that cb[] region as struct inet_skb_parm (IPv4). The layouts differ: inet6_skb_parm.nhoff at offset 14 overlaps inet_skb_parm.opt.rr, producing a non-zero rr value. __ip_options_echo() then reads optlen from attacker-controlled packet data at sptr[rr+1] and copies that many bytes into dopt->__data, a fixed 40-byte stack buffer (IP_OPTIONS_DATA_FIXED_SIZE). To fix this we clear skb2->cb[], as suggested by Oskar Kjos. Also add minimal IPv4 header validation (version == 4, ihl >= 5). Fixes: c4d3efafcc93 ("[IPV6] IP6TUNNEL: Add support to IPv4 over IPv6 tunnel.") Reported-by: Oskar Kjos Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260326155138.2429480-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_tunnel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 4c29aa94e86e..0b53488a9229 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -601,11 +601,16 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!skb2) return 0; + /* Remove debris left by IPv6 stack. */ + memset(IPCB(skb2), 0, sizeof(*IPCB(skb2))); + skb_dst_drop(skb2); skb_pull(skb2, offset); skb_reset_network_header(skb2); eiph = ip_hdr(skb2); + if (eiph->version != 4 || eiph->ihl < 5) + goto out; /* Try to guess incoming interface */ rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr, From b38c55320bf85a84a4f04803c57b261fc87e9b4b Mon Sep 17 00:00:00 2001 From: Dimitri Daskalakis Date: Tue, 24 Mar 2026 12:51:22 -0700 Subject: [PATCH 347/712] eth: fbnic: Account for page fragments when updating BDQ tail FBNIC supports fixed size buffers of 4K. When PAGE_SIZE > 4K, we fragment the page across multiple descriptors (FBNIC_BD_FRAG_COUNT). When refilling the BDQ, the correct number of entries are populated, but tail was only incremented by one. So on a system with 64K pages, HW would get one descriptor refilled for every 16 we populate. Additionally, we program the ring size in the HW when enabling the BDQ. This was not accounting for page fragments, so on systems with 64K pages, the HW used 1/16th of the ring. Fixes: 0cb4c0a13723 ("eth: fbnic: Implement Rx queue alloc/start/stop/free") Signed-off-by: Dimitri Daskalakis Link: https://patch.msgid.link/20260324195123.3486219-2-dimitri.daskalakis1@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/meta/fbnic/fbnic_txrx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index 9fb91d4f3971..9cd85a0d0c3a 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -927,7 +927,7 @@ static void fbnic_fill_bdq(struct fbnic_ring *bdq) /* Force DMA writes to flush before writing to tail */ dma_wmb(); - writel(i, bdq->doorbell); + writel(i * FBNIC_BD_FRAG_COUNT, bdq->doorbell); } } @@ -2564,7 +2564,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq) hpq->tail = 0; hpq->head = 0; - log_size = fls(hpq->size_mask); + log_size = fls(hpq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT); /* Store descriptor ring address and size */ fbnic_ring_wr32(hpq, FBNIC_QUEUE_BDQ_HPQ_BAL, lower_32_bits(hpq->dma)); @@ -2576,7 +2576,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq) if (!ppq->size_mask) goto write_ctl; - log_size = fls(ppq->size_mask); + log_size = fls(ppq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT); /* Add enabling of PPQ to BDQ control */ bdq_ctl |= FBNIC_QUEUE_BDQ_CTL_PPQ_ENABLE; From f3567dd428b264b3f06f881e5e85a738c7c910df Mon Sep 17 00:00:00 2001 From: Dimitri Daskalakis Date: Tue, 24 Mar 2026 12:51:23 -0700 Subject: [PATCH 348/712] eth: fbnic: Fix debugfs output for BDQ's with page frags The rings size_mask represents the number of pages, so we need to determine the number of page frags when dumping the descriptors. Fixes: df04373b0dab ("eth fbnic: Add debugfs hooks for tx/rx rings") Signed-off-by: Dimitri Daskalakis Link: https://patch.msgid.link/20260324195123.3486219-3-dimitri.daskalakis1@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c index 08270db2dee8..3c4563c8f403 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c @@ -197,7 +197,7 @@ static int fbnic_dbg_bdq_desc_seq_show(struct seq_file *s, void *v) return 0; } - for (i = 0; i <= ring->size_mask; i++) { + for (i = 0; i < (ring->size_mask + 1) * FBNIC_BD_FRAG_COUNT; i++) { u64 bd = le64_to_cpu(ring->desc[i]); seq_printf(s, "%04x %#04llx %#014llx\n", i, From a01aee7cafc575bb82f5529e8734e7052f9b16ea Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 26 Mar 2026 03:44:39 +0000 Subject: [PATCH 349/712] bridge: br_nd_send: linearize skb before parsing ND options br_nd_send() parses neighbour discovery options from ns->opt[] and assumes that these options are in the linear part of request. Its callers only guarantee that the ICMPv6 header and target address are available, so the option area can still be non-linear. Parsing ns->opt[] in that case can access data past the linear buffer. Linearize request before option parsing and derive ns from the linear network header. Fixes: ed842faeb2bd ("bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports") Reported-by: Yifan Wu Reported-by: Juefei Pu Tested-by: Ao Zhou Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Yang Yang Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260326034441.2037420-2-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski --- net/bridge/br_arp_nd_proxy.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 1e2b51769eec..af3d1e33f50b 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -251,12 +251,12 @@ struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg) static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, struct sk_buff *request, struct neighbour *n, - __be16 vlan_proto, u16 vlan_tci, struct nd_msg *ns) + __be16 vlan_proto, u16 vlan_tci) { struct net_device *dev = request->dev; struct net_bridge_vlan_group *vg; + struct nd_msg *na, *ns; struct sk_buff *reply; - struct nd_msg *na; struct ipv6hdr *pip6; int na_olen = 8; /* opt hdr + ETH_ALEN for target */ int ns_olen; @@ -264,7 +264,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, u8 *daddr; u16 pvid; - if (!dev) + if (!dev || skb_linearize(request)) return; len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) + @@ -281,6 +281,8 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, skb_set_mac_header(reply, 0); daddr = eth_hdr(request)->h_source; + ns = (struct nd_msg *)(skb_network_header(request) + + sizeof(struct ipv6hdr)); /* Do we need option processing ? */ ns_olen = request->len - (skb_network_offset(request) + @@ -472,9 +474,9 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, if (vid != 0) br_nd_send(br, p, skb, n, skb->vlan_proto, - skb_vlan_tag_get(skb), msg); + skb_vlan_tag_get(skb)); else - br_nd_send(br, p, skb, n, 0, 0, msg); + br_nd_send(br, p, skb, n, 0, 0); replied = true; } From 850837965af15707fd3142c1cf3c5bfaf022299b Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 26 Mar 2026 03:44:40 +0000 Subject: [PATCH 350/712] bridge: br_nd_send: validate ND option lengths br_nd_send() walks ND options according to option-provided lengths. A malformed option can make the parser advance beyond the computed option span or use a too-short source LLADDR option payload. Validate option lengths against the remaining NS option area before advancing, and only read source LLADDR when the option is large enough for an Ethernet address. Fixes: ed842faeb2bd ("bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports") Cc: stable@vger.kernel.org Reported-by: Yifan Wu Reported-by: Juefei Pu Tested-by: Ao Zhou Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Yang Yang Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260326034441.2037420-3-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski --- net/bridge/br_arp_nd_proxy.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index af3d1e33f50b..6b5595868a39 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -288,12 +288,14 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, ns_olen = request->len - (skb_network_offset(request) + sizeof(struct ipv6hdr)) - sizeof(*ns); for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) { - if (!ns->opt[i + 1]) { + if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) { kfree_skb(reply); return; } if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { - daddr = ns->opt + i + sizeof(struct nd_opt_hdr); + if ((ns->opt[i + 1] << 3) >= + sizeof(struct nd_opt_hdr) + ETH_ALEN) + daddr = ns->opt + i + sizeof(struct nd_opt_hdr); break; } } From afa9a05e6c4971bd5586f1b304e14d61fb3d9385 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 26 Mar 2026 03:44:41 +0000 Subject: [PATCH 351/712] vxlan: validate ND option lengths in vxlan_na_create vxlan_na_create() walks ND options according to option-provided lengths. A malformed option can make the parser advance beyond the computed option span or use a too-short source LLADDR option payload. Validate option lengths against the remaining NS option area before advancing, and only read source LLADDR when the option is large enough for an Ethernet address. Fixes: 4b29dba9c085 ("vxlan: fix nonfunctional neigh_reduce()") Cc: stable@vger.kernel.org Reported-by: Yifan Wu Reported-by: Juefei Pu Tested-by: Ao Zhou Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Yang Yang Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260326034441.2037420-4-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 17c941aac32d..a94ac82a6136 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1965,12 +1965,14 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, ns_olen = request->len - skb_network_offset(request) - sizeof(struct ipv6hdr) - sizeof(*ns); for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) { - if (!ns->opt[i + 1]) { + if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) { kfree_skb(reply); return NULL; } if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { - daddr = ns->opt + i + sizeof(struct nd_opt_hdr); + if ((ns->opt[i + 1] << 3) >= + sizeof(struct nd_opt_hdr) + ETH_ALEN) + daddr = ns->opt + i + sizeof(struct nd_opt_hdr); break; } } From 4576100b8cd03118267513cafacde164b498b322 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 26 Mar 2026 13:43:09 -0700 Subject: [PATCH 352/712] net/sched: sch_hfsc: fix divide-by-zero in rtsc_min() m2sm() converts a u32 slope to a u64 scaled value. For large inputs (e.g. m1=4000000000), the result can reach 2^32. rtsc_min() stores the difference of two such u64 values in a u32 variable `dsm` and uses it as a divisor. When the difference is exactly 2^32 the truncation yields zero, causing a divide-by-zero oops in the concave-curve intersection path: Oops: divide error: 0000 RIP: 0010:rtsc_min (net/sched/sch_hfsc.c:601) Call Trace: init_ed (net/sched/sch_hfsc.c:629) hfsc_enqueue (net/sched/sch_hfsc.c:1569) [...] Widen `dsm` to u64 and replace do_div() with div64_u64() so the full difference is preserved. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260326204310.1549327-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- net/sched/sch_hfsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index b5657ffbbf84..83b2ca2e37fc 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -555,7 +555,7 @@ static void rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y) { u64 y1, y2, dx, dy; - u32 dsm; + u64 dsm; if (isc->sm1 <= isc->sm2) { /* service curve is convex */ @@ -598,7 +598,7 @@ rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y) */ dx = (y1 - y) << SM_SHIFT; dsm = isc->sm1 - isc->sm2; - do_div(dx, dsm); + dx = div64_u64(dx, dsm); /* * check if (x, y1) belongs to the 1st segment of rtsc. * if so, add the offset. From 5d17af9eb2dd3de6846ed344c883de7812e6cc09 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 26 Mar 2026 13:43:10 -0700 Subject: [PATCH 353/712] selftests/tc-testing: add test for HFSC divide-by-zero in rtsc_min() Add a regression test for the divide-by-zero in rtsc_min() triggered when m2sm() converts a large m1 value (e.g. 32gbit) to a u64 scaled slope reaching 2^32. rtsc_min() stores the difference of two such u64 values (sm1 - sm2) in a u32 variable `dsm`, truncating 2^32 to zero and causing a divide-by-zero oops in the concave-curve intersection path. The test configures an HFSC class with m1=32gbit d=1ms m2=0bit, sends a packet to activate the class, waits for it to drain and go idle, then sends another packet to trigger reactivation through rtsc_min(). Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260326204310.1549327-2-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- .../tc-testing/tc-tests/infra/qdiscs.json | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json index 6a39640aa2a8..1e5efb2a31eb 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json @@ -1111,5 +1111,30 @@ "teardown": [ "$TC qdisc del dev $DUMMY root handle 1:" ] + }, + { + "id": "a3d7", + "name": "HFSC with large m1 - no divide-by-zero on class reactivation", + "category": [ + "qdisc", + "hfsc" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc replace dev $DUMMY root handle 1: hfsc default 1", + "$TC class replace dev $DUMMY parent 1: classid 1:1 hfsc rt m1 32gbit d 1ms m2 0bit ls m1 32gbit d 1ms m2 0bit", + "ping -I$DUMMY -f -c1 -s64 -W1 10.10.10.1 || true", + "sleep 1" + ], + "cmdUnderTest": "ping -I$DUMMY -f -c1 -s64 -W1 10.10.10.1 || true", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc hfsc 1: root", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] } ] From eeee5a710f26ce57807024ef330fe5a850eaecd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 26 Mar 2026 13:20:38 +0100 Subject: [PATCH 354/712] net: sfp: Fix Ubiquiti U-Fiber Instant SFP module on mvneta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 8110633db49d7de2 ("net: sfp-bus: allow SFP quirks to override Autoneg and pause bits") we moved the setting of Autoneg and pause bits before the call to SFP quirk when parsing SFP module support. Since the quirk for Ubiquiti U-Fiber Instant SFP module zeroes the support bits and sets 1000baseX_Full only, the above mentioned commit changed the overall computed support from 1000baseX_Full, Autoneg, Pause, Asym_Pause to just 1000baseX_Full. This broke the SFP module for mvneta, which requires Autoneg for 1000baseX since commit c762b7fac1b249a9 ("net: mvneta: deny disabling autoneg for 802.3z modes"). Fix this by setting back the Autoneg, Pause and Asym_Pause bits in the quirk. Fixes: 8110633db49d7de2 ("net: sfp-bus: allow SFP quirks to override Autoneg and pause bits") Signed-off-by: Marek Behún Reviewed-by: Russell King (Oracle) Link: https://patch.msgid.link/20260326122038.2489589-1-kabel@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 5db841377199..ce8924613363 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -480,11 +480,16 @@ static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, { /* Ubiquiti U-Fiber Instant module claims that support all transceiver * types including 10G Ethernet which is not truth. So clear all claimed - * modes and set only one mode which module supports: 1000baseX_Full. + * modes and set only one mode which module supports: 1000baseX_Full, + * along with the Autoneg and pause bits. */ linkmode_zero(caps->link_modes); linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, caps->link_modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, caps->link_modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, caps->link_modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, caps->link_modes); + phy_interface_zero(caps->interfaces); __set_bit(PHY_INTERFACE_MODE_1000BASEX, caps->interfaces); } From d389954a6cae7bf76b7b082ac3511d177b77ef2d Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Thu, 26 Mar 2026 15:52:32 +0800 Subject: [PATCH 355/712] net: enetc: check whether the RSS algorithm is Toeplitz Both ENETC v1 and v4 only provide Toeplitz RSS support. This patch adds a validation check to reject attempts to configure other RSS algorithms, avoiding misleading configuration options for users. Fixes: d382563f541b ("enetc: Add RFS and RSS support") Signed-off-by: Wei Fang Reviewed-by: Clark Wang Reviewed-by: Claudiu Manoil Link: https://patch.msgid.link/20260326075233.3628047-2-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc_ethtool.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index 2fe140ddebb2..a393647e6062 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -795,6 +795,10 @@ static int enetc_set_rxfh(struct net_device *ndev, struct enetc_si *si = priv->si; int err = 0; + if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && + rxfh->hfunc != ETH_RSS_HASH_TOP) + return -EOPNOTSUPP; + /* set hash key, if PF */ if (rxfh->key && enetc_si_is_pf(si)) enetc_set_rss_key(si, rxfh->key); From a142d139168cce8d5776245b5494c7f7f5d7fb7d Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Thu, 26 Mar 2026 15:52:33 +0800 Subject: [PATCH 356/712] net: enetc: do not allow VF to configure the RSS key VFs do not have privilege to configure the RSS key because the registers are owned by the PF. Currently, if VF attempts to configure the RSS key, enetc_set_rxfh() simply skips the configuration and does not generate a warning, which may mislead users into thinking the feature is supported. To improve this situation, add a check to reject RSS key configuration on VFs. Fixes: d382563f541b ("enetc: Add RFS and RSS support") Signed-off-by: Wei Fang Reviewed-by: Clark Wang Reviewed-by: Claudiu Manoil Link: https://patch.msgid.link/20260326075233.3628047-3-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc_ethtool.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index a393647e6062..7c17acaf7a38 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -800,8 +800,12 @@ static int enetc_set_rxfh(struct net_device *ndev, return -EOPNOTSUPP; /* set hash key, if PF */ - if (rxfh->key && enetc_si_is_pf(si)) + if (rxfh->key) { + if (!enetc_si_is_pf(si)) + return -EOPNOTSUPP; + enetc_set_rss_key(si, rxfh->key); + } /* set RSS table */ if (rxfh->indir) From bc5b4e5ae1a67700a618328217b6a3bd0f296e97 Mon Sep 17 00:00:00 2001 From: Phil Willoughby Date: Sat, 28 Mar 2026 08:07:34 +0000 Subject: [PATCH 357/712] ALSA: usb-audio: Fix quirk flags for NeuralDSP Quad Cortex The NeuralDSP Quad Cortex does not support DSD playback. We need this product-specific entry with zero quirks because otherwise it falls through to the vendor-specific entry which marks it as supporting DSD playback. Cc: Yue Wang Cc: Jaroslav Kysela Cc: Takashi Iwai Signed-off-by: Phil Willoughby Link: https://patch.msgid.link/20260328080921.3310-1-willerz@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 1f82e9e02d4b..6e40c18c37f9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2305,6 +2305,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */ + 0), /* Doesn't have the vendor quirk which would otherwise apply */ DEVICE_FLG(0x154e, 0x1002, /* Denon DCD-1500RE */ QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY), DEVICE_FLG(0x154e, 0x1003, /* Denon DA-300USB */ From bac1e57adf08c9ee33e95fb09cd032f330294e70 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 27 Mar 2026 10:54:40 -0500 Subject: [PATCH 358/712] ALSA: hda/realtek: add quirk for Framework F111:000F Similar to commit 7b509910b3ad ("ALSA hda/realtek: Add quirk for Framework F111:000C") and previous quirks for Framework systems with Realtek codecs. 000F is another new platform with an ALC285 which needs the same quirk. Signed-off-by: Dustin L. Howett Link: https://patch.msgid.link/20260327-framework-alsa-000f-v1-1-74013aba1c00@howett.net Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 6787e54fcfe6..393910857e89 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7757,6 +7757,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x000b, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0xf111, 0x000f, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), #if 0 /* Below is a quirk table taken from the old code. From 696b0a9bd2e4b4c7082369202fe9406345a6d11e Mon Sep 17 00:00:00 2001 From: Stuart Hayhurst Date: Fri, 27 Mar 2026 15:57:36 +0000 Subject: [PATCH 359/712] ALSA: hda/intel: Add MSI X870E Tomahawk to denylist by DMI ID This motherboard uses USB audio instead, causing this driver to complain about "no codecs found!". Add it to the denylist to silence the warning. The first attempt only matched on the PCI device, but this caused issues for some laptops, so DMI match against the board as well. Signed-off-by: Stuart Hayhurst Link: https://patch.msgid.link/20260327155737.21818-2-stuart.a.hayhurst@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/controllers/intel.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 2edbcab597c8..8a7bd49e411f 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2085,6 +2085,11 @@ static struct pci_device_id driver_denylist_ideapad_z570[] = { {} }; +static struct pci_device_id driver_denylist_msi_x870e[] = { + { PCI_DEVICE_SUB(0x1022, 0x15e3, 0x1462, 0xee59) }, /* MSI X870E Tomahawk WiFi */ + {} +}; + /* DMI-based denylist, to be used when: * - PCI subsystem IDs are zero, impossible to distinguish from valid sound cards. * - Different modifications of the same laptop use different GPU models. @@ -2098,6 +2103,14 @@ static const struct dmi_system_id driver_denylist_dmi[] = { }, .driver_data = &driver_denylist_ideapad_z570, }, + { + /* PCI device matching alone incorrectly matches some laptops */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "MAG X870E TOMAHAWK WIFI (MS-7E59)"), + }, + .driver_data = &driver_denylist_msi_x870e, + }, {} }; From 1fbf85dbf02c96c318e056fb5b8fc614758fee3c Mon Sep 17 00:00:00 2001 From: Sourav Nayak Date: Fri, 27 Mar 2026 19:58:05 +0530 Subject: [PATCH 360/712] ALSA: hda/realtek: add quirk for HP Victus 15-fb0xxx This adds a mute led quirck for HP Victus 15-fb0xxx (103c:8a3d) model - As it used 0x8(full bright)/0x7f(little dim) for mute led on and other values as 0ff (0x0, 0x4, ...) - So, use ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT insted for safer approach Cc: Signed-off-by: Sourav Nayak Link: https://patch.msgid.link/20260327142805.17139-1-nonameblank007@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 393910857e89..78ab0e86621b 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7009,6 +7009,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8a34, "HP Pavilion x360 2-in-1 Laptop 14-ek0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), From 18fb5f1f0289b8217c0c43d54d12bccc201dd640 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Sat, 21 Mar 2026 18:27:46 +0100 Subject: [PATCH 361/712] rust: dma: remove DMA_ATTR_NO_KERNEL_MAPPING from public attrs When DMA_ATTR_NO_KERNEL_MAPPING is passed to dma_alloc_attrs(), the returned CPU address is not a pointer to the allocated memory but an opaque handle (e.g. struct page *). Coherent (or CoherentAllocation respectively) stores this value as NonNull and exposes methods that dereference it and even modify its contents. Remove the flag from the public attrs module such that drivers cannot pass it to Coherent (or CoherentAllocation respectively) in the first place. Instead DMA_ATTR_NO_KERNEL_MAPPING can be supported with an additional opaque type (e.g. CoherentHandle) which does not provide access to the allocated memory. Cc: stable@vger.kernel.org Fixes: ad2907b4e308 ("rust: add dma coherent allocator abstraction") Signed-off-by: Danilo Krummrich Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260321172749.592387-1-dakr@kernel.org Signed-off-by: Alexandre Courbot --- rust/kernel/dma.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 7bc97f9f83fd..beb4dea7dd84 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -267,9 +267,6 @@ pub mod attrs { /// Specifies that writes to the mapping may be buffered to improve performance. pub const DMA_ATTR_WRITE_COMBINE: Attrs = Attrs(bindings::DMA_ATTR_WRITE_COMBINE); - /// Lets the platform to avoid creating a kernel virtual mapping for the allocated buffer. - pub const DMA_ATTR_NO_KERNEL_MAPPING: Attrs = Attrs(bindings::DMA_ATTR_NO_KERNEL_MAPPING); - /// Allows platform code to skip synchronization of the CPU cache for the given buffer assuming /// that it has been already transferred to 'device' domain. pub const DMA_ATTR_SKIP_CPU_SYNC: Attrs = Attrs(bindings::DMA_ATTR_SKIP_CPU_SYNC); From 6dd782af1eb6be16a80349ea7822f0f8a23bb3e8 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Sat, 21 Mar 2026 18:27:47 +0100 Subject: [PATCH 362/712] rust: dma: add CoherentHandle for DMA allocations without kernel mapping Add CoherentHandle, an opaque DMA allocation type for buffers that are only ever accessed by hardware. Unlike Coherent, it does not provide CPU access to the allocated memory. CoherentHandle implicitly sets DMA_ATTR_NO_KERNEL_MAPPING and stores the value returned by dma_alloc_attrs() as an opaque handle (NonNull) rather than a typed pointer, since with this flag the C API returns an opaque cookie (e.g. struct page *), not a CPU pointer to the allocated memory. Only the DMA bus address is exposed to drivers; the opaque handle is used solely to free the allocation on drop. This commit is for reference only; there is currently no in-tree user. Signed-off-by: Danilo Krummrich Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260321172749.592387-2-dakr@kernel.org [acourbot: fix conflict in dma.rs.] Signed-off-by: Alexandre Courbot --- rust/kernel/dma.rs | 119 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index beb4dea7dd84..7ea3c3b73367 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -907,6 +907,125 @@ fn write_to_slice( } } +/// An opaque DMA allocation without a kernel virtual mapping. +/// +/// Unlike [`Coherent`], a `CoherentHandle` does not provide CPU access to the allocated memory. +/// The allocation is always performed with `DMA_ATTR_NO_KERNEL_MAPPING`, meaning no kernel +/// virtual mapping is created for the buffer. The value returned by the C API as the CPU +/// address is an opaque handle used only to free the allocation. +/// +/// This is useful for buffers that are only ever accessed by hardware. +/// +/// # Invariants +/// +/// - `cpu_handle` holds the opaque handle returned by `dma_alloc_attrs` with +/// `DMA_ATTR_NO_KERNEL_MAPPING` set, and is only valid for passing back to `dma_free_attrs`. +/// - `dma_handle` is the corresponding bus address for device DMA. +/// - `size` is the allocation size in bytes as passed to `dma_alloc_attrs`. +/// - `dma_attrs` contains the attributes used for the allocation, always including +/// `DMA_ATTR_NO_KERNEL_MAPPING`. +pub struct CoherentHandle { + dev: ARef, + dma_handle: DmaAddress, + cpu_handle: NonNull, + size: usize, + dma_attrs: Attrs, +} + +impl CoherentHandle { + /// Allocates `size` bytes of coherent DMA memory without creating a kernel virtual mapping. + /// + /// Additional DMA attributes may be passed via `dma_attrs`; `DMA_ATTR_NO_KERNEL_MAPPING` is + /// always set implicitly. + /// + /// Returns `EINVAL` if `size` is zero, `ENOMEM` if the allocation fails. + pub fn alloc_with_attrs( + dev: &device::Device, + size: usize, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result { + if size == 0 { + return Err(EINVAL); + } + + let dma_attrs = dma_attrs | Attrs(bindings::DMA_ATTR_NO_KERNEL_MAPPING); + let mut dma_handle = 0; + // SAFETY: `dev.as_raw()` is valid by the type invariant on `device::Device`. + let cpu_handle = unsafe { + bindings::dma_alloc_attrs( + dev.as_raw(), + size, + &mut dma_handle, + gfp_flags.as_raw(), + dma_attrs.as_raw(), + ) + }; + + let cpu_handle = NonNull::new(cpu_handle).ok_or(ENOMEM)?; + + // INVARIANT: `cpu_handle` is the opaque handle from a successful `dma_alloc_attrs` call + // with `DMA_ATTR_NO_KERNEL_MAPPING`, `dma_handle` is the corresponding DMA address, + // and we hold a refcounted reference to the device. + Ok(Self { + dev: dev.into(), + dma_handle, + cpu_handle, + size, + dma_attrs, + }) + } + + /// Allocates `size` bytes of coherent DMA memory without creating a kernel virtual mapping. + #[inline] + pub fn alloc( + dev: &device::Device, + size: usize, + gfp_flags: kernel::alloc::Flags, + ) -> Result { + Self::alloc_with_attrs(dev, size, gfp_flags, Attrs(0)) + } + + /// Returns the DMA handle for this allocation. + /// + /// This address can be programmed into device hardware for DMA access. + #[inline] + pub fn dma_handle(&self) -> DmaAddress { + self.dma_handle + } + + /// Returns the size in bytes of this allocation. + #[inline] + pub fn size(&self) -> usize { + self.size + } +} + +impl Drop for CoherentHandle { + fn drop(&mut self) { + // SAFETY: All values are valid by the type invariants on `CoherentHandle`. + // `cpu_handle` is the opaque handle from `dma_alloc_attrs` and is passed back unchanged. + unsafe { + bindings::dma_free_attrs( + self.dev.as_raw(), + self.size, + self.cpu_handle.as_ptr(), + self.dma_handle, + self.dma_attrs.as_raw(), + ) + } + } +} + +// SAFETY: `CoherentHandle` only holds a device reference, a DMA handle, an opaque CPU handle, +// and a size. None of these are tied to a specific thread. +unsafe impl Send for CoherentHandle {} + +// SAFETY: `CoherentHandle` provides no CPU access to the underlying allocation. The only +// operations on `&CoherentHandle` are reading the DMA handle and size, both of which are +// plain `Copy` values. +unsafe impl Sync for CoherentHandle {} + /// Reads a field of an item from an allocated region of structs. /// /// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating From 816718c611cab2164d718b91ad8204afcd0af81f Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 27 Mar 2026 00:22:07 +0900 Subject: [PATCH 363/712] rust: dma: add from-slice constructors for Coherent and CoherentBox A very common pattern is to create a block of coherent memory with the content of an already-existing slice of bytes (e.g. a loaded firmware blob). `CoherentBox` makes this easier, but still implies a potentially panicking operation with `copy_from_slice` that requires a `PANIC` comment. Add `from_slice_with_attrs` and `from_slice` methods to both `Coherent` and `CoherentBox` to turn this into a trivial one-step operation. Reviewed-by: Andreas Hindborg Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260327-b4-nova-dma-removal-v2-1-616e1d0b5cb3@nvidia.com Signed-off-by: Alexandre Courbot --- rust/kernel/dma.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 7ea3c3b73367..4995ee5dc689 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -453,6 +453,66 @@ pub fn init_at(&mut self, i: usize, init: impl Init) -> Result Ok(()) } + + /// Allocates a region of coherent memory of the same size as `data` and initializes it with a + /// copy of its contents. + /// + /// This is the [`CoherentBox`] variant of [`Coherent::from_slice_with_attrs`]. + /// + /// # Examples + /// + /// ``` + /// use core::ops::Deref; + /// + /// # use kernel::device::{Bound, Device}; + /// use kernel::dma::{ + /// attrs::*, + /// CoherentBox + /// }; + /// + /// # fn test(dev: &Device) -> Result { + /// let data = [0u8, 1u8, 2u8, 3u8]; + /// let c: CoherentBox<[u8]> = + /// CoherentBox::from_slice_with_attrs(dev, &data, GFP_KERNEL, DMA_ATTR_NO_WARN)?; + /// + /// assert_eq!(c.deref(), &data); + /// # Ok::<(), Error>(()) } + /// ``` + pub fn from_slice_with_attrs( + dev: &device::Device, + data: &[T], + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result + where + T: Copy, + { + let mut slice = Self(Coherent::::alloc_slice_with_attrs( + dev, + data.len(), + gfp_flags, + dma_attrs, + )?); + + // PANIC: `slice` was created with length `data.len()`. + slice.copy_from_slice(data); + + Ok(slice) + } + + /// Performs the same functionality as [`CoherentBox::from_slice_with_attrs`], except the + /// `dma_attrs` is 0 by default. + #[inline] + pub fn from_slice( + dev: &device::Device, + data: &[T], + gfp_flags: kernel::alloc::Flags, + ) -> Result + where + T: Copy, + { + Self::from_slice_with_attrs(dev, data, gfp_flags, Attrs(0)) + } } impl CoherentBox { @@ -839,6 +899,53 @@ pub fn zeroed_slice( ) -> Result> { Self::zeroed_slice_with_attrs(dev, len, gfp_flags, Attrs(0)) } + + /// Allocates a region of coherent memory of the same size as `data` and initializes it with a + /// copy of its contents. + /// + /// # Examples + /// + /// ``` + /// # use kernel::device::{Bound, Device}; + /// use kernel::dma::{ + /// attrs::*, + /// Coherent + /// }; + /// + /// # fn test(dev: &Device) -> Result { + /// let data = [0u8, 1u8, 2u8, 3u8]; + /// // `c` has the same content as `data`. + /// let c: Coherent<[u8]> = + /// Coherent::from_slice_with_attrs(dev, &data, GFP_KERNEL, DMA_ATTR_NO_WARN)?; + /// + /// # Ok::<(), Error>(()) } + /// ``` + #[inline] + pub fn from_slice_with_attrs( + dev: &device::Device, + data: &[T], + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result> + where + T: Copy, + { + CoherentBox::from_slice_with_attrs(dev, data, gfp_flags, dma_attrs).map(Into::into) + } + + /// Performs the same functionality as [`Coherent::from_slice_with_attrs`], except the + /// `dma_attrs` is 0 by default. + #[inline] + pub fn from_slice( + dev: &device::Device, + data: &[T], + gfp_flags: kernel::alloc::Flags, + ) -> Result> + where + T: Copy, + { + Self::from_slice_with_attrs(dev, data, gfp_flags, Attrs(0)) + } } impl Coherent<[T]> { From 308eb645b57a91fe78d3065b8924f5c92b69a4a0 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 27 Mar 2026 00:22:08 +0900 Subject: [PATCH 364/712] gpu: nova-core: firmware: riscv: use dma::Coherent Replace the nova-core local `DmaObject` with a `Coherent` that can fulfill the same role. Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260327-b4-nova-dma-removal-v2-2-616e1d0b5cb3@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/riscv.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs index 14aad2f0ee8a..2afa7f36404e 100644 --- a/drivers/gpu/nova-core/firmware/riscv.rs +++ b/drivers/gpu/nova-core/firmware/riscv.rs @@ -5,13 +5,13 @@ use kernel::{ device, + dma::Coherent, firmware::Firmware, prelude::*, transmute::FromBytes, // }; use crate::{ - dma::DmaObject, firmware::BinFirmware, num::FromSafeCast, // }; @@ -66,7 +66,7 @@ pub(crate) struct RiscvFirmware { /// Application version. pub(crate) app_version: u32, /// Device-mapped firmware image. - pub(crate) ucode: DmaObject, + pub(crate) ucode: Coherent<[u8]>, } impl RiscvFirmware { @@ -81,7 +81,7 @@ pub(crate) fn new(dev: &device::Device, fw: &Firmware) -> Result< let len = usize::from_safe_cast(bin_fw.hdr.data_size); let end = start.checked_add(len).ok_or(EINVAL)?; - DmaObject::from_data(dev, fw.data().get(start..end).ok_or(EINVAL)?)? + Coherent::from_slice(dev, fw.data().get(start..end).ok_or(EINVAL)?, GFP_KERNEL)? }; Ok(Self { From 1f9283afd3f1780bd629f02e149afe7b0c78fc5b Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 27 Mar 2026 00:22:09 +0900 Subject: [PATCH 365/712] gpu: nova-core: firmware: fwsec: use dma::Coherent Replace the nova-core local `DmaObject` with a `Coherent` that can fulfill the same role. Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260327-b4-nova-dma-removal-v2-3-616e1d0b5cb3@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/fwsec/bootloader.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs index 3b12d90d9412..bcb713a868e2 100644 --- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs +++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs @@ -12,6 +12,7 @@ self, Device, // }, + dma::Coherent, io::{ register::WithBase, // Io, @@ -29,7 +30,6 @@ }; use crate::{ - dma::DmaObject, driver::Bar0, falcon::{ self, @@ -129,7 +129,7 @@ unsafe impl AsBytes for BootloaderDmemDescV2 {} /// operation. pub(crate) struct FwsecFirmwareWithBl { /// DMA object the bootloader will copy the firmware from. - _firmware_dma: DmaObject, + _firmware_dma: Coherent<[u8]>, /// Code of the bootloader to be loaded into non-secure IMEM. ucode: KVec, /// Descriptor to be loaded into DMEM for the bootloader to read. @@ -211,7 +211,7 @@ pub(crate) fn new( ( align_padding, - DmaObject::from_data(dev, firmware_obj.as_slice())?, + Coherent::from_slice(dev, firmware_obj.as_slice(), GFP_KERNEL)?, ) }; From a88831502c8f0530e1390a5f704fbc5e73f19b8c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 27 Mar 2026 00:22:10 +0900 Subject: [PATCH 366/712] gpu: nova-core: falcon: use dma::Coherent Replace the nova-core local `DmaObject` with a `Coherent` that can fulfill the same role. Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260327-b4-nova-dma-removal-v2-4-616e1d0b5cb3@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index c49ec6ded909..e0315fda576b 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -10,6 +10,7 @@ Device, // }, dma::{ + Coherent, DmaAddress, DmaMask, // }, @@ -28,7 +29,6 @@ use crate::{ bounded_enum, - dma::DmaObject, driver::Bar0, falcon::hal::LoadMethod, gpu::Chipset, @@ -504,7 +504,7 @@ pub(crate) fn pio_load + FalconPioLoadable>( fn dma_wr( &self, bar: &Bar0, - dma_obj: &DmaObject, + dma_obj: &Coherent<[u8]>, target_mem: FalconMem, load_offsets: FalconDmaLoadTarget, ) -> Result { @@ -614,7 +614,7 @@ fn dma_load + FalconDmaLoadable>( fw: &F, ) -> Result { // Create DMA object with firmware content as the source of the DMA engine. - let dma_obj = DmaObject::from_data(dev, fw.as_slice())?; + let dma_obj = Coherent::from_slice(dev, fw.as_slice(), GFP_KERNEL)?; self.dma_reset(bar); bar.update(regs::NV_PFALCON_FBIF_TRANSCFG::of::().at(0), |v| { From c1c79e3bebc6f8b634fcf11d08d72a0df1cb85a0 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 27 Mar 2026 00:22:11 +0900 Subject: [PATCH 367/712] gpu: nova-core: fb: use dma::CoherentHandle Replace the nova-core local `DmaObject` with a `CoherentHandle` that can fulfill the same role. Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260327-b4-nova-dma-removal-v2-5-616e1d0b5cb3@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/fb.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index 62fc90fa6a84..bdd5eed760e1 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -7,6 +7,7 @@ use kernel::{ device, + dma::CoherentHandle, fmt, io::Io, prelude::*, @@ -19,7 +20,6 @@ }; use crate::{ - dma::DmaObject, driver::Bar0, firmware::gsp::GspFirmware, gpu::Chipset, @@ -53,7 +53,7 @@ pub(crate) struct SysmemFlush { chipset: Chipset, device: ARef, /// Keep the page alive as long as we need it. - page: DmaObject, + page: CoherentHandle, } impl SysmemFlush { @@ -63,7 +63,7 @@ pub(crate) fn register( bar: &Bar0, chipset: Chipset, ) -> Result { - let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?; + let page = CoherentHandle::alloc(dev, kernel::page::PAGE_SIZE, GFP_KERNEL)?; hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?; From 371db8bcb925bfb0ac68db2f66aeaa0350ac1d06 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 27 Mar 2026 00:22:12 +0900 Subject: [PATCH 368/712] gpu: nova-core: firmware: gsp: use dma::Coherent for signatures Replace the nova-core local `DmaObject` with a `Coherent` that can fulfill the same role. Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260327-b4-nova-dma-removal-v2-6-616e1d0b5cb3@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/gsp.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 9488a626352f..1e0d545a74fe 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -3,6 +3,7 @@ use kernel::{ device, dma::{ + Coherent, DataDirection, DmaAddress, // }, @@ -140,7 +141,7 @@ pub(crate) struct GspFirmware { /// Size in bytes of the firmware contained in [`Self::fw`]. pub(crate) size: usize, /// Device-mapped GSP signatures matching the GPU's [`Chipset`]. - pub(crate) signatures: DmaObject, + pub(crate) signatures: Coherent<[u8]>, /// GSP bootloader, verifies the GSP firmware before loading and running it. pub(crate) bootloader: RiscvFirmware, } @@ -226,7 +227,7 @@ pub(crate) fn new<'a>( elf::elf64_section(firmware.data(), sigs_section) .ok_or(EINVAL) - .and_then(|data| DmaObject::from_data(dev, data))? + .and_then(|data| Coherent::from_slice(dev, data, GFP_KERNEL))? }, bootloader: { let bl = super::request_firmware(dev, chipset, "bootloader", ver)?; From e10dcb9d654177270b48119fa79f3924aef9ff48 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 27 Mar 2026 00:22:13 +0900 Subject: [PATCH 369/712] gpu: nova-core: firmware: gsp: use dma::Coherent for level0 table Replace the nova-core local `DmaObject` with a `CoherentBox` that can fulfill the same role. Since `CoherentBox` is more flexible than `DmaObject`, we can use the native `u64` type for page table entries instead of messing with bytes. The `dma` module becomes unused with that change, so remove it as well. Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260327-b4-nova-dma-removal-v2-7-616e1d0b5cb3@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/dma.rs | 53 --------------------------- drivers/gpu/nova-core/firmware/gsp.rs | 22 ++++++----- drivers/gpu/nova-core/nova_core.rs | 1 - 3 files changed, 12 insertions(+), 64 deletions(-) delete mode 100644 drivers/gpu/nova-core/dma.rs diff --git a/drivers/gpu/nova-core/dma.rs b/drivers/gpu/nova-core/dma.rs deleted file mode 100644 index 3c19d5ffcfe8..000000000000 --- a/drivers/gpu/nova-core/dma.rs +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! Simple DMA object wrapper. - -use core::ops::{ - Deref, - DerefMut, // -}; - -use kernel::{ - device, - dma::Coherent, - page::PAGE_SIZE, - prelude::*, // -}; - -pub(crate) struct DmaObject { - dma: Coherent<[u8]>, -} - -impl DmaObject { - pub(crate) fn new(dev: &device::Device, len: usize) -> Result { - let len = core::alloc::Layout::from_size_align(len, PAGE_SIZE) - .map_err(|_| EINVAL)? - .pad_to_align() - .size(); - let dma = Coherent::zeroed_slice(dev, len, GFP_KERNEL)?; - - Ok(Self { dma }) - } - - pub(crate) fn from_data(dev: &device::Device, data: &[u8]) -> Result { - let dma_obj = Self::new(dev, data.len())?; - // SAFETY: We have just allocated the DMA memory, we are the only users and - // we haven't made the device aware of the handle yet. - unsafe { dma_obj.as_mut()[..data.len()].copy_from_slice(data) }; - Ok(dma_obj) - } -} - -impl Deref for DmaObject { - type Target = Coherent<[u8]>; - - fn deref(&self) -> &Self::Target { - &self.dma - } -} - -impl DerefMut for DmaObject { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.dma - } -} diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 1e0d545a74fe..86fb3f074195 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -4,10 +4,10 @@ device, dma::{ Coherent, + CoherentBox, DataDirection, DmaAddress, // }, - kvec, prelude::*, scatterlist::{ Owned, @@ -16,7 +16,6 @@ }; use crate::{ - dma::DmaObject, firmware::riscv::RiscvFirmware, gpu::{ Architecture, @@ -137,7 +136,7 @@ pub(crate) struct GspFirmware { #[pin] level1: SGTable>>, /// Level 0 page table (single 4KB page) with one entry: DMA address of first level 1 page. - level0: DmaObject, + level0: Coherent<[u64]>, /// Size in bytes of the firmware contained in [`Self::fw`]. pub(crate) size: usize, /// Device-mapped GSP signatures matching the GPU's [`Chipset`]. @@ -198,17 +197,20 @@ pub(crate) fn new<'a>( // Allocate the level 0 page table as a device-visible DMA object, and map the // level 1 page table onto it. - // Level 0 page table data. - let mut level0_data = kvec![0u8; GSP_PAGE_SIZE]?; - // Fill level 1 page entry. let level1_entry = level1.iter().next().ok_or(EINVAL)?; let level1_entry_addr = level1_entry.dma_address(); - let dst = &mut level0_data[..size_of_val(&level1_entry_addr)]; - dst.copy_from_slice(&level1_entry_addr.to_le_bytes()); - // Turn the level0 page table into a [`DmaObject`]. - DmaObject::from_data(dev, &level0_data)? + // Create level 0 page table data and fill its first entry with the level 1 + // table. + let mut level0 = CoherentBox::<[u64]>::zeroed_slice( + dev, + GSP_PAGE_SIZE / size_of::(), + GFP_KERNEL + )?; + level0[0] = level1_entry_addr.to_le(); + + level0.into() }, size, signatures: { diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 98675c69d2b7..04a1fa6b25f8 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -13,7 +13,6 @@ #[macro_use] mod bitfield; -mod dma; mod driver; mod falcon; mod fb; From b045ab3dff97edae6d538eeff900a34c098761f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 29 Mar 2026 11:12:37 +0200 Subject: [PATCH 370/712] ALSA: ctxfi: Fix missing SPDIFI1 index handling SPDIF1 DAIO type isn't properly handled in daio_device_index() for hw20k2, and it returned -EINVAL, which ended up with the out-of-bounds array access. Follow the hw20k1 pattern and return the proper index for this type, too. Reported-and-tested-by: Karsten Hohmeier Closes: https://lore.kernel.org/20260315155004.15633-1-linux@hohmatik.de Cc: Link: https://patch.msgid.link/20260329091240.420194-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctdaio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index b8bde27f3a1d..19faa81d5400 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -118,6 +118,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) switch (type) { case SPDIFOO: return 0; case SPDIFIO: return 0; + case SPDIFI1: return 1; case LINEO1: return 4; case LINEO2: return 7; case LINEO3: return 5; From 277c6960d4ddb94d16198afd70c92c3d4593d131 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 29 Mar 2026 11:12:38 +0200 Subject: [PATCH 371/712] ALSA: ctxfi: Check the error for index mapping The ctxfi driver blindly assumed a proper value returned from daio_device_index(), but it's not always true. Add a proper error check to deal with the error from the function. Cc: Link: https://lore.kernel.org/87cy149n6k.wl-tiwai@suse.de Link: https://patch.msgid.link/20260329091240.420194-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctdaio.c | 81 +++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 19faa81d5400..4dbb1dd7af32 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -99,7 +99,7 @@ static const struct rsc_ops daio_in_rsc_ops_20k2 = { .output_slot = daio_index, }; -static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) +static int daio_device_index(enum DAIOTYP type, struct hw *hw) { switch (hw->chip_type) { case ATC20K1: @@ -112,7 +112,9 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) case LINEO3: return 5; case LINEO4: return 6; case LINEIM: return 7; - default: return -EINVAL; + default: + pr_err("ctxfi: Invalid type %d for hw20k1\n", type); + return -EINVAL; } case ATC20K2: switch (type) { @@ -126,9 +128,12 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) case LINEIM: return 4; case MIC: return 5; case RCA: return 3; - default: return -EINVAL; + default: + pr_err("ctxfi: Invalid type %d for hw20k2\n", type); + return -EINVAL; } default: + pr_err("ctxfi: Invalid chip type %d\n", hw->chip_type); return -EINVAL; } } @@ -149,8 +154,11 @@ static int dao_spdif_set_spos(struct dao *dao, unsigned int spos) static int dao_commit_write(struct dao *dao) { - dao->hw->dao_commit_write(dao->hw, - daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk); + int idx = daio_device_index(dao->daio.type, dao->hw); + + if (idx < 0) + return idx; + dao->hw->dao_commit_write(dao->hw, idx, dao->ctrl_blk); return 0; } @@ -288,8 +296,11 @@ static int dai_set_enb_srt(struct dai *dai, unsigned int enb) static int dai_commit_write(struct dai *dai) { - dai->hw->dai_commit_write(dai->hw, - daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk); + int idx = daio_device_index(dai->daio.type, dai->hw); + + if (idx < 0) + return idx; + dai->hw->dai_commit_write(dai->hw, idx, dai->ctrl_blk); return 0; } @@ -368,7 +379,7 @@ static int dao_rsc_init(struct dao *dao, { struct hw *hw = mgr->mgr.hw; unsigned int conf; - int err; + int idx, err; err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw); if (err) @@ -387,15 +398,18 @@ static int dao_rsc_init(struct dao *dao, if (err) goto error2; - hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, - daio_device_index(dao->daio.type, hw)); + idx = daio_device_index(dao->daio.type, hw); + if (idx < 0) { + err = idx; + goto error2; + } + + hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, idx); hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); conf = (desc->msr & 0x7) | (desc->passthru << 3); - hw->daio_mgr_dao_init(hw, mgr->mgr.ctrl_blk, - daio_device_index(dao->daio.type, hw), conf); - hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, - daio_device_index(dao->daio.type, hw)); + hw->daio_mgr_dao_init(hw, mgr->mgr.ctrl_blk, idx, conf); + hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, idx); hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); return 0; @@ -444,7 +458,7 @@ static int dai_rsc_init(struct dai *dai, const struct daio_desc *desc, struct daio_mgr *mgr) { - int err; + int idx, err; struct hw *hw = mgr->mgr.hw; unsigned int rsr, msr; @@ -458,6 +472,12 @@ static int dai_rsc_init(struct dai *dai, if (err) goto error1; + idx = daio_device_index(dai->daio.type, dai->hw); + if (idx < 0) { + err = idx; + goto error1; + } + for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1) rsr++; @@ -466,8 +486,7 @@ static int dai_rsc_init(struct dai *dai, /* default to disabling control of a SRC */ hw->dai_srt_set_ec(dai->ctrl_blk, 0); hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */ - hw->dai_commit_write(hw, - daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk); + hw->dai_commit_write(hw, idx, dai->ctrl_blk); return 0; @@ -582,28 +601,28 @@ static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio) static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio) { struct hw *hw = mgr->mgr.hw; + int idx = daio_device_index(daio->type, hw); - if (daio->output) { - hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, - daio_device_index(daio->type, hw)); - } else { - hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk, - daio_device_index(daio->type, hw)); - } + if (idx < 0) + return idx; + if (daio->output) + hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, idx); + else + hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk, idx); return 0; } static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio) { struct hw *hw = mgr->mgr.hw; + int idx = daio_device_index(daio->type, hw); - if (daio->output) { - hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, - daio_device_index(daio->type, hw)); - } else { - hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk, - daio_device_index(daio->type, hw)); - } + if (idx < 0) + return idx; + if (daio->output) + hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, idx); + else + hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk, idx); return 0; } From b948f9d5d3057b01188e36664e7c7604d1c8ecb5 Mon Sep 17 00:00:00 2001 From: Junxi Qian Date: Sun, 29 Mar 2026 23:39:09 +0800 Subject: [PATCH 372/712] io_uring/net: fix slab-out-of-bounds read in io_bundle_nbufs() sqe->len is __u32 but gets stored into sr->len which is int. When userspace passes sqe->len values exceeding INT_MAX (e.g. 0xFFFFFFFF), sr->len overflows to a negative value. This negative value propagates through the bundle recv/send path: 1. io_recv(): sel.val = sr->len (ssize_t gets -1) 2. io_recv_buf_select(): arg.max_len = sel->val (size_t gets 0xFFFFFFFFFFFFFFFF) 3. io_ring_buffers_peek(): buf->len is not clamped because max_len is astronomically large 4. iov[].iov_len = 0xFFFFFFFF flows into io_bundle_nbufs() 5. io_bundle_nbufs(): min_t(int, 0xFFFFFFFF, ret) yields -1, causing ret to increase instead of decrease, creating an infinite loop that reads past the allocated iov[] array This results in a slab-out-of-bounds read in io_bundle_nbufs() from the kmalloc-64 slab, as nbufs increments past the allocated iovec entries. BUG: KASAN: slab-out-of-bounds in io_bundle_nbufs+0x128/0x160 Read of size 8 at addr ffff888100ae05c8 by task exp/145 Call Trace: io_bundle_nbufs+0x128/0x160 io_recv_finish+0x117/0xe20 io_recv+0x2db/0x1160 Fix this by rejecting negative sr->len values early in both io_sendmsg_prep() and io_recvmsg_prep(). Since sqe->len is __u32, any value > INT_MAX indicates overflow and is not a valid length. Fixes: a05d1f625c7a ("io_uring/net: support bundles for send") Cc: stable@vger.kernel.org Signed-off-by: Junxi Qian Link: https://patch.msgid.link/20260329153909.279046-1-qjx1298677004@gmail.com Signed-off-by: Jens Axboe --- io_uring/net.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io_uring/net.c b/io_uring/net.c index d27adbe3f20b..8885d944130a 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -421,6 +421,8 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) sr->done_io = 0; sr->len = READ_ONCE(sqe->len); + if (unlikely(sr->len < 0)) + return -EINVAL; sr->flags = READ_ONCE(sqe->ioprio); if (sr->flags & ~SENDMSG_FLAGS) return -EINVAL; @@ -791,6 +793,8 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) sr->umsg = u64_to_user_ptr(READ_ONCE(sqe->addr)); sr->len = READ_ONCE(sqe->len); + if (unlikely(sr->len < 0)) + return -EINVAL; sr->flags = READ_ONCE(sqe->ioprio); if (sr->flags & ~RECVMSG_FLAGS) return -EINVAL; From 111a12b422a8cfa93deabaef26fec48237163214 Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Mon, 30 Mar 2026 00:49:36 +0800 Subject: [PATCH 373/712] io_uring/rsrc: reject zero-length fixed buffer import validate_fixed_range() admits buf_addr at the exact end of the registered region when len is zero, because the check uses strict greater-than (buf_end > imu->ubuf + imu->len). io_import_fixed() then computes offset == imu->len, which causes the bvec skip logic to advance past the last bio_vec entry and read bv_offset from out-of-bounds slab memory. Return early from io_import_fixed() when len is zero. A zero-length import has no data to transfer and should not walk the bvec array at all. BUG: KASAN: slab-out-of-bounds in io_import_reg_buf+0x697/0x7f0 Read of size 4 at addr ffff888002bcc254 by task poc/103 Call Trace: io_import_reg_buf+0x697/0x7f0 io_write_fixed+0xd9/0x250 __io_issue_sqe+0xad/0x710 io_issue_sqe+0x7d/0x1100 io_submit_sqes+0x86a/0x23c0 __do_sys_io_uring_enter+0xa98/0x1590 Allocated by task 103: The buggy address is located 12 bytes to the right of allocated 584-byte region [ffff888002bcc000, ffff888002bcc248) Fixes: 8622b20f23ed ("io_uring: add validate_fixed_range() for validate fixed buffer") Signed-off-by: Qi Tang Link: https://patch.msgid.link/20260329164936.240871-1-tpluszz77@gmail.com Signed-off-by: Jens Axboe --- io_uring/rsrc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index 842e231c8a7c..3f82ed7dd11e 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -1062,6 +1062,10 @@ static int io_import_fixed(int ddir, struct iov_iter *iter, return ret; if (!(imu->dir & (1 << ddir))) return -EFAULT; + if (unlikely(!len)) { + iov_iter_bvec(iter, ddir, NULL, 0, 0); + return 0; + } offset = buf_addr - imu->ubuf; From 0e211f6aaa6a00fd0ee0c1eea5498f168c6725e6 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 29 Mar 2026 17:09:40 +0000 Subject: [PATCH 374/712] hwmon: (tps53679) Fix array access with zero-length block read i2c_smbus_read_block_data() can return 0, indicating a zero-length read. When this happens, tps53679_identify_chip() accesses buf[ret - 1] which is buf[-1], reading one byte before the buffer on the stack. Fix by changing the check from "ret < 0" to "ret <= 0", treating a zero-length read as an error (-EIO), which prevents the out-of-bounds array access. Also fix a typo in the adjacent comment: "if present" instead of duplicate "if". Fixes: 75ca1e5875fe ("hwmon: (pmbus/tps53679) Add support for TPS53685") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260329170925.34581-2-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/tps53679.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index ca2bfa25eb04..3bca543817a6 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -103,10 +103,10 @@ static int tps53679_identify_chip(struct i2c_client *client, } ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); - if (ret < 0) - return ret; + if (ret <= 0) + return ret < 0 ? ret : -EIO; - /* Adjust length if null terminator if present */ + /* Adjust length if null terminator is present */ buf_len = (buf[ret - 1] != '\x00' ? ret : ret - 1); id_len = strlen(id); From ccf70c41e562b29d1c05d1bbf53391785e09c6fb Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 29 Mar 2026 17:09:48 +0000 Subject: [PATCH 375/712] hwmon: (pxe1610) Check return value of page-select write in probe pxe1610_probe() writes PMBUS_PAGE to select page 0 but does not check the return value. If the write fails, subsequent register reads operate on an indeterminate page, leading to silent misconfiguration. Check the return value and propagate the error using dev_err_probe(), which also handles -EPROBE_DEFER correctly without log spam. Fixes: 344757bac526 ("hwmon: (pmbus) Add Infineon PXE1610 VR driver") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260329170925.34581-4-sanman.pradhan@hpe.com [groeck: Fix "Fixes" SHA] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pxe1610.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index 6a4a978eca7e..24c1f961c766 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -104,7 +104,10 @@ static int pxe1610_probe(struct i2c_client *client) * By default this device doesn't boot to page 0, so set page 0 * to access all pmbus registers. */ - i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to set page 0\n"); /* Read Manufacturer id */ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); From a9d2fbd3ad0e6ac588386e699beeccfe7516755f Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 29 Mar 2026 17:09:53 +0000 Subject: [PATCH 376/712] hwmon: (ltc4286) Add missing MODULE_IMPORT_NS("PMBUS") ltc4286.c uses PMBus core symbols exported in the PMBUS namespace, such as pmbus_do_probe(), but does not declare MODULE_IMPORT_NS("PMBUS"). Add the missing namespace import to avoid modpost warnings. Fixes: 0c459759ca97 ("hwmon: (pmbus) Add ltc4286 driver") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260329170925.34581-5-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/ltc4286.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/pmbus/ltc4286.c b/drivers/hwmon/pmbus/ltc4286.c index aabd0bcdfeee..8715d380784a 100644 --- a/drivers/hwmon/pmbus/ltc4286.c +++ b/drivers/hwmon/pmbus/ltc4286.c @@ -173,3 +173,4 @@ module_i2c_driver(ltc4286_driver); MODULE_AUTHOR("Delphine CC Chiu "); MODULE_DESCRIPTION("PMBUS driver for LTC4286 and compatibles"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From fda9522ed6afaec45cabc198d8492270c394c7bc Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Wed, 25 Mar 2026 09:14:22 +0900 Subject: [PATCH 377/712] ksmbd: fix OOB write in QUERY_INFO for compound requests When a compound request such as READ + QUERY_INFO(Security) is received, and the first command (READ) consumes most of the response buffer, ksmbd could write beyond the allocated buffer while building a security descriptor. The root cause was that smb2_get_info_sec() checked buffer space using ppntsd_size from xattr, while build_sec_desc() often synthesized a significantly larger descriptor from POSIX ACLs. This patch introduces smb_acl_sec_desc_scratch_len() to accurately compute the final descriptor size beforehand, performs proper buffer checking with smb2_calc_max_out_buf_len(), and uses exact-sized allocation + iov pinning. Cc: stable@vger.kernel.org Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") Signed-off-by: Asim Viladi Oglu Manizada Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 121 +++++++++++++++++++++++++++++----------- fs/smb/server/smbacl.c | 43 ++++++++++++++ fs/smb/server/smbacl.h | 2 + 3 files changed, 134 insertions(+), 32 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 6fb7a795ff5d..8e4cfdc0ba02 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3402,20 +3402,24 @@ int smb2_open(struct ksmbd_work *work) KSMBD_SHARE_FLAG_ACL_XATTR)) { struct smb_fattr fattr; struct smb_ntsd *pntsd; - int pntsd_size, ace_num = 0; + int pntsd_size; + size_t scratch_len; ksmbd_acls_fattr(&fattr, idmap, inode); - if (fattr.cf_acls) - ace_num = fattr.cf_acls->a_count; - if (fattr.cf_dacls) - ace_num += fattr.cf_dacls->a_count; + scratch_len = smb_acl_sec_desc_scratch_len(&fattr, + NULL, 0, + OWNER_SECINFO | GROUP_SECINFO | + DACL_SECINFO); + if (!scratch_len || scratch_len == SIZE_MAX) { + rc = -EFBIG; + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + goto err_out; + } - pntsd = kmalloc(sizeof(struct smb_ntsd) + - sizeof(struct smb_sid) * 3 + - sizeof(struct smb_acl) + - sizeof(struct smb_ace) * ace_num * 2, - KSMBD_DEFAULT_GFP); + pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP); if (!pntsd) { + rc = -ENOMEM; posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); goto err_out; @@ -3430,7 +3434,7 @@ int smb2_open(struct ksmbd_work *work) posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); if (rc) { - kfree(pntsd); + kvfree(pntsd); goto err_out; } @@ -3440,7 +3444,7 @@ int smb2_open(struct ksmbd_work *work) pntsd, pntsd_size, false); - kfree(pntsd); + kvfree(pntsd); if (rc) pr_err("failed to store ntacl in xattr : %d\n", rc); @@ -5372,8 +5376,9 @@ static int smb2_get_info_file(struct ksmbd_work *work, if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { /* smb2 info file called for pipe */ - return smb2_get_info_file_pipe(work->sess, req, rsp, + rc = smb2_get_info_file_pipe(work->sess, req, rsp, work->response_buf); + goto iov_pin_out; } if (work->next_smb2_rcv_hdr_off) { @@ -5473,6 +5478,12 @@ static int smb2_get_info_file(struct ksmbd_work *work, rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), rsp, work->response_buf); ksmbd_fd_put(work, fp); + +iov_pin_out: + if (!rc) + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_query_info_rsp, Buffer) + + le32_to_cpu(rsp->OutputBufferLength)); return rc; } @@ -5699,6 +5710,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), rsp, work->response_buf); path_put(&path); + + if (!rc) + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_query_info_rsp, Buffer) + + le32_to_cpu(rsp->OutputBufferLength)); return rc; } @@ -5708,13 +5724,14 @@ static int smb2_get_info_sec(struct ksmbd_work *work, { struct ksmbd_file *fp; struct mnt_idmap *idmap; - struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; + struct smb_ntsd *pntsd = NULL, *ppntsd = NULL; struct smb_fattr fattr = {{0}}; struct inode *inode; __u32 secdesclen = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; int addition_info = le32_to_cpu(req->AdditionalInformation); - int rc = 0, ppntsd_size = 0; + int rc = 0, ppntsd_size = 0, max_len; + size_t scratch_len = 0; if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | PROTECTED_DACL_SECINFO | @@ -5722,6 +5739,11 @@ static int smb2_get_info_sec(struct ksmbd_work *work, ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", addition_info); + pntsd = kzalloc(ALIGN(sizeof(struct smb_ntsd), 8), + KSMBD_DEFAULT_GFP); + if (!pntsd) + return -ENOMEM; + pntsd->revision = cpu_to_le16(1); pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); pntsd->osidoffset = 0; @@ -5730,9 +5752,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, pntsd->dacloffset = 0; secdesclen = sizeof(struct smb_ntsd); - rsp->OutputBufferLength = cpu_to_le32(secdesclen); - - return 0; + goto iov_pin; } if (work->next_smb2_rcv_hdr_off) { @@ -5764,18 +5784,58 @@ static int smb2_get_info_sec(struct ksmbd_work *work, &ppntsd); /* Check if sd buffer size exceeds response buffer size */ - if (smb2_resp_buf_len(work, 8) > ppntsd_size) - rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size, - addition_info, &secdesclen, &fattr); + max_len = smb2_calc_max_out_buf_len(work, + offsetof(struct smb2_query_info_rsp, Buffer), + le32_to_cpu(req->OutputBufferLength)); + if (max_len < 0) { + rc = -EINVAL; + goto release_acl; + } + + scratch_len = smb_acl_sec_desc_scratch_len(&fattr, ppntsd, + ppntsd_size, addition_info); + if (!scratch_len || scratch_len == SIZE_MAX) { + rc = -EFBIG; + goto release_acl; + } + + pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP); + if (!pntsd) { + rc = -ENOMEM; + goto release_acl; + } + + rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size, + addition_info, &secdesclen, &fattr); + +release_acl: posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); kfree(ppntsd); ksmbd_fd_put(work, fp); - if (rc) - return rc; + if (!rc && ALIGN(secdesclen, 8) > scratch_len) + rc = -EFBIG; + if (rc) + goto err_out; + +iov_pin: rsp->OutputBufferLength = cpu_to_le32(secdesclen); - return 0; + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf); + if (rc) + goto err_out; + + rc = ksmbd_iov_pin_rsp_read(work, (void *)rsp, + offsetof(struct smb2_query_info_rsp, Buffer), + pntsd, secdesclen); +err_out: + if (rc) { + rsp->OutputBufferLength = 0; + kvfree(pntsd); + } + + return rc; } /** @@ -5799,6 +5859,9 @@ int smb2_query_info(struct ksmbd_work *work) goto err_out; } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + switch (req->InfoType) { case SMB2_O_INFO_FILE: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); @@ -5819,14 +5882,6 @@ int smb2_query_info(struct ksmbd_work *work) } ksmbd_revert_fsids(work); - if (!rc) { - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(72); - rc = ksmbd_iov_pin_rsp(work, (void *)rsp, - offsetof(struct smb2_query_info_rsp, Buffer) + - le32_to_cpu(rsp->OutputBufferLength)); - } - err_out: if (rc < 0) { if (rc == -EACCES) @@ -5837,6 +5892,8 @@ int smb2_query_info(struct ksmbd_work *work) rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; else if (rc == -ENOMEM) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc == -EINVAL && rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; smb2_set_err_rsp(work); diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 49c2abb29bf5..c30d01877c41 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -915,6 +915,49 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, return 0; } +size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info) +{ + size_t len = sizeof(struct smb_ntsd); + size_t tmp; + + if (addition_info & OWNER_SECINFO) + len += sizeof(struct smb_sid); + if (addition_info & GROUP_SECINFO) + len += sizeof(struct smb_sid); + if (!(addition_info & DACL_SECINFO)) + return len; + + len += sizeof(struct smb_acl); + if (ppntsd && ppntsd_size > 0) { + unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); + + if (dacl_offset < ppntsd_size && + check_add_overflow(len, ppntsd_size - dacl_offset, &len)) + return 0; + } + + if (fattr->cf_acls) { + if (check_mul_overflow((size_t)fattr->cf_acls->a_count, + 2 * sizeof(struct smb_ace), &tmp) || + check_add_overflow(len, tmp, &len)) + return 0; + } else { + /* default/minimum DACL */ + if (check_add_overflow(len, 5 * sizeof(struct smb_ace), &len)) + return 0; + } + + if (fattr->cf_dacls) { + if (check_mul_overflow((size_t)fattr->cf_dacls->a_count, + sizeof(struct smb_ace), &tmp) || + check_add_overflow(len, tmp, &len)) + return 0; + } + + return len; +} + /* Convert permission bits from mode to equivalent CIFS ACL */ int build_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h index 355adaee39b8..ab21ba2cd4df 100644 --- a/fs/smb/server/smbacl.h +++ b/fs/smb/server/smbacl.h @@ -101,6 +101,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, bool type_check, bool get_write); void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); void ksmbd_init_domain(u32 *sub_auth); +size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info); static inline uid_t posix_acl_uid_translate(struct mnt_idmap *idmap, struct posix_acl_entry *pace) From b3d24269b3c7e764b694689b5fd7517546625150 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Wed, 25 Mar 2026 18:38:38 -0700 Subject: [PATCH 378/712] gpu: nova-core: firmware: move firmware image parsing code to firmware.rs Up until now, only the GSP required parsing of its firmware headers. However, upcoming support for Hopper/Blackwell+ adds another firmware image (FMC), along with another format (ELF32). Therefore, the current ELF64 section parsing support needs to be moved up a level, so that both of the above can use it. There are no functional changes. This is pure code movement. Reviewed-by: Gary Guo Signed-off-by: John Hubbard Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260326013902.588242-8-jhubbard@nvidia.com [acourbot: use fuller prefix in commit message.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 88 ++++++++++++++++++++++++++ drivers/gpu/nova-core/firmware/gsp.rs | 91 ++------------------------- 2 files changed, 92 insertions(+), 87 deletions(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 2bb20081befd..177b8ede151c 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -457,3 +457,91 @@ pub(crate) const fn create( this.0 } } + +/// Ad-hoc and temporary module to extract sections from ELF images. +/// +/// Some firmware images are currently packaged as ELF files, where sections names are used as keys +/// to specific and related bits of data. Future firmware versions are scheduled to move away from +/// that scheme before nova-core becomes stable, which means this module will eventually be +/// removed. +mod elf { + use core::mem::size_of; + + use kernel::{ + bindings, + str::CStr, + transmute::FromBytes, // + }; + + /// Newtype to provide a [`FromBytes`] implementation. + #[repr(transparent)] + struct Elf64Hdr(bindings::elf64_hdr); + // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. + unsafe impl FromBytes for Elf64Hdr {} + + #[repr(transparent)] + struct Elf64SHdr(bindings::elf64_shdr); + // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. + unsafe impl FromBytes for Elf64SHdr {} + + /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it. + pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> { + let hdr = &elf + .get(0..size_of::()) + .and_then(Elf64Hdr::from_bytes)? + .0; + + // Get all the section headers. + let mut shdr = { + let shdr_num = usize::from(hdr.e_shnum); + let shdr_start = usize::try_from(hdr.e_shoff).ok()?; + let shdr_end = shdr_num + .checked_mul(size_of::()) + .and_then(|v| v.checked_add(shdr_start))?; + + elf.get(shdr_start..shdr_end) + .map(|slice| slice.chunks_exact(size_of::()))? + }; + + // Get the strings table. + let strhdr = shdr + .clone() + .nth(usize::from(hdr.e_shstrndx)) + .and_then(Elf64SHdr::from_bytes)?; + + // Find the section which name matches `name` and return it. + shdr.find(|&sh| { + let Some(hdr) = Elf64SHdr::from_bytes(sh) else { + return false; + }; + + let Some(name_idx) = strhdr + .0 + .sh_offset + .checked_add(u64::from(hdr.0.sh_name)) + .and_then(|idx| usize::try_from(idx).ok()) + else { + return false; + }; + + // Get the start of the name. + elf.get(name_idx..) + .and_then(|nstr| CStr::from_bytes_until_nul(nstr).ok()) + // Convert into str. + .and_then(|c_str| c_str.to_str().ok()) + // Check that the name matches. + .map(|str| str == name) + .unwrap_or(false) + }) + // Return the slice containing the section. + .and_then(|sh| { + let hdr = Elf64SHdr::from_bytes(sh)?; + let start = usize::try_from(hdr.0.sh_offset).ok()?; + let end = usize::try_from(hdr.0.sh_size) + .ok() + .and_then(|sh_size| start.checked_add(sh_size))?; + + elf.get(start..end) + }) + } +} diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 86fb3f074195..2fcc255c3bc8 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -16,7 +16,10 @@ }; use crate::{ - firmware::riscv::RiscvFirmware, + firmware::{ + elf, + riscv::RiscvFirmware, // + }, gpu::{ Architecture, Chipset, // @@ -25,92 +28,6 @@ num::FromSafeCast, }; -/// Ad-hoc and temporary module to extract sections from ELF images. -/// -/// Some firmware images are currently packaged as ELF files, where sections names are used as keys -/// to specific and related bits of data. Future firmware versions are scheduled to move away from -/// that scheme before nova-core becomes stable, which means this module will eventually be -/// removed. -mod elf { - use kernel::{ - bindings, - prelude::*, - transmute::FromBytes, // - }; - - /// Newtype to provide a [`FromBytes`] implementation. - #[repr(transparent)] - struct Elf64Hdr(bindings::elf64_hdr); - // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. - unsafe impl FromBytes for Elf64Hdr {} - - #[repr(transparent)] - struct Elf64SHdr(bindings::elf64_shdr); - // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. - unsafe impl FromBytes for Elf64SHdr {} - - /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it. - pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> { - let hdr = &elf - .get(0..size_of::()) - .and_then(Elf64Hdr::from_bytes)? - .0; - - // Get all the section headers. - let mut shdr = { - let shdr_num = usize::from(hdr.e_shnum); - let shdr_start = usize::try_from(hdr.e_shoff).ok()?; - let shdr_end = shdr_num - .checked_mul(size_of::()) - .and_then(|v| v.checked_add(shdr_start))?; - - elf.get(shdr_start..shdr_end) - .map(|slice| slice.chunks_exact(size_of::()))? - }; - - // Get the strings table. - let strhdr = shdr - .clone() - .nth(usize::from(hdr.e_shstrndx)) - .and_then(Elf64SHdr::from_bytes)?; - - // Find the section which name matches `name` and return it. - shdr.find(|&sh| { - let Some(hdr) = Elf64SHdr::from_bytes(sh) else { - return false; - }; - - let Some(name_idx) = strhdr - .0 - .sh_offset - .checked_add(u64::from(hdr.0.sh_name)) - .and_then(|idx| usize::try_from(idx).ok()) - else { - return false; - }; - - // Get the start of the name. - elf.get(name_idx..) - .and_then(|nstr| CStr::from_bytes_until_nul(nstr).ok()) - // Convert into str. - .and_then(|c_str| c_str.to_str().ok()) - // Check that the name matches. - .map(|str| str == name) - .unwrap_or(false) - }) - // Return the slice containing the section. - .and_then(|sh| { - let hdr = Elf64SHdr::from_bytes(sh)?; - let start = usize::try_from(hdr.0.sh_offset).ok()?; - let end = usize::try_from(hdr.0.sh_size) - .ok() - .and_then(|sh_size| start.checked_add(sh_size))?; - - elf.get(start..end) - }) - } -} - /// GSP firmware with 3-level radix page tables for the GSP bootloader. /// /// The bootloader expects firmware to be mapped starting at address 0 in GSP's virtual address From 7c50d748b4a635bc39802ea3f6b120e66b1b9067 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Wed, 25 Mar 2026 18:38:39 -0700 Subject: [PATCH 379/712] gpu: nova-core: firmware: factor out an elf_str() function Factor out a chunk of complexity into a new subroutine. This is an incremental step in adding ELF32 support to the existing ELF64 section support, for handling GPU firmware. Signed-off-by: John Hubbard Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260326013902.588242-9-jhubbard@nvidia.com [acourbot: use fuller prefix in commit message.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 40 ++++++++++++------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 177b8ede151c..6c2ab69cb605 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -484,6 +484,13 @@ unsafe impl FromBytes for Elf64Hdr {} // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. unsafe impl FromBytes for Elf64SHdr {} + /// Returns a NULL-terminated string from the ELF image at `offset`. + fn elf_str(elf: &[u8], offset: u64) -> Option<&str> { + let idx = usize::try_from(offset).ok()?; + let bytes = elf.get(idx..)?; + CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok() + } + /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it. pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> { let hdr = &elf @@ -510,32 +517,15 @@ pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a .and_then(Elf64SHdr::from_bytes)?; // Find the section which name matches `name` and return it. - shdr.find(|&sh| { - let Some(hdr) = Elf64SHdr::from_bytes(sh) else { - return false; - }; - - let Some(name_idx) = strhdr - .0 - .sh_offset - .checked_add(u64::from(hdr.0.sh_name)) - .and_then(|idx| usize::try_from(idx).ok()) - else { - return false; - }; - - // Get the start of the name. - elf.get(name_idx..) - .and_then(|nstr| CStr::from_bytes_until_nul(nstr).ok()) - // Convert into str. - .and_then(|c_str| c_str.to_str().ok()) - // Check that the name matches. - .map(|str| str == name) - .unwrap_or(false) - }) - // Return the slice containing the section. - .and_then(|sh| { + shdr.find_map(|sh| { let hdr = Elf64SHdr::from_bytes(sh)?; + let name_offset = strhdr.0.sh_offset.checked_add(u64::from(hdr.0.sh_name))?; + let section_name = elf_str(elf, name_offset)?; + + if section_name != name { + return None; + } + let start = usize::try_from(hdr.0.sh_offset).ok()?; let end = usize::try_from(hdr.0.sh_size) .ok() From 4dfce79e098915d8e5fc2b9e1d980bc3251dd32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:10 +0200 Subject: [PATCH 380/712] drm/i915/dsi: Don't do DSC horizontal timing adjustments in command mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop adjusting the horizontal timing values based on the compression ratio in command mode. Bspec seems to be telling us to do this only in video mode, and this is also how the Windows driver does things. This should also fix a div-by-zero on some machines because the adjusted htotal ends up being so small that we end up with line_time_us==0 when trying to determine the vtotal value in command mode. Note that this doesn't actually make the display on the Huawei Matebook E work, but at least the kernel no longer explodes when the driver loads. Cc: stable@vger.kernel.org Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12045 Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-2-ville.syrjala@linux.intel.com Fixes: 53693f02d80e ("drm/i915/dsi: account for DSC in horizontal timings") Reviewed-by: Jani Nikula (cherry picked from commit 0b475e91ecc2313207196c6d7fd5c53e1a878525) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/icl_dsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index fc265f71d72b..298b3a48197c 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -889,7 +889,7 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder, * non-compressed link speeds, and simplifies down to the ratio between * compressed and non-compressed bpp. */ - if (crtc_state->dsc.compression_enable) { + if (is_vid_mode(intel_dsi) && crtc_state->dsc.compression_enable) { mul = fxp_q4_to_int(crtc_state->dsc.compressed_bpp_x16); div = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); } @@ -1503,7 +1503,7 @@ static void gen11_dsi_get_timings(struct intel_encoder *encoder, struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - if (pipe_config->dsc.compressed_bpp_x16) { + if (is_vid_mode(intel_dsi) && pipe_config->dsc.compressed_bpp_x16) { int div = fxp_q4_to_int(pipe_config->dsc.compressed_bpp_x16); int mul = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); From 45424e871abf2a152e247a9cff78359f18dd95c0 Mon Sep 17 00:00:00 2001 From: Berk Cem Goksel Date: Sun, 29 Mar 2026 16:38:25 +0300 Subject: [PATCH 381/712] ALSA: caiaq: fix stack out-of-bounds read in init_card The loop creates a whitespace-stripped copy of the card shortname where `len < sizeof(card->id)` is used for the bounds check. Since sizeof(card->id) is 16 and the local id buffer is also 16 bytes, writing 16 non-space characters fills the entire buffer, overwriting the terminating nullbyte. When this non-null-terminated string is later passed to snd_card_set_id() -> copy_valid_id_string(), the function scans forward with `while (*nid && ...)` and reads past the end of the stack buffer, reading the contents of the stack. A USB device with a product name containing many non-ASCII, non-space characters (e.g. multibyte UTF-8) will reliably trigger this as follows: BUG: KASAN: stack-out-of-bounds in copy_valid_id_string sound/core/init.c:696 [inline] BUG: KASAN: stack-out-of-bounds in snd_card_set_id_no_lock+0x698/0x74c sound/core/init.c:718 The off-by-one has been present since commit bafeee5b1f8d ("ALSA: snd_usb_caiaq: give better shortname") from June 2009 (v2.6.31-rc1), which first introduced this whitespace-stripping loop. The original code never accounted for the null terminator when bounding the copy. Fix this by changing the loop bound to `sizeof(card->id) - 1`, ensuring at least one byte remains as the null terminator. Fixes: bafeee5b1f8d ("ALSA: snd_usb_caiaq: give better shortname") Cc: stable@vger.kernel.org Cc: Andrey Konovalov Reported-by: Berk Cem Goksel Signed-off-by: Berk Cem Goksel Link: https://patch.msgid.link/20260329133825.581585-1-berkcgoksel@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/caiaq/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index dfd820483849..3a71bab8a477 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -488,7 +488,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev) memset(id, 0, sizeof(id)); for (c = card->shortname, len = 0; - *c && len < sizeof(card->id); c++) + *c && len < sizeof(card->id) - 1; c++) if (*c != ' ') id[len++] = *c; From f025ac8c698ac7d29eb3b5025bcdaf7ad675785d Mon Sep 17 00:00:00 2001 From: Dag Smedberg Date: Sun, 29 Mar 2026 19:04:20 +0200 Subject: [PATCH 382/712] ALSA: usb-audio: Exclude Scarlett Solo 1st Gen from SKIP_IFACE_SETUP Same issue that the Scarlett 2i2 1st Gen had: QUIRK_FLAG_SKIP_IFACE_SETUP causes distorted audio on the Scarlett Solo 1st Gen (1235:801c). Fixes: 38c322068a26 ("ALSA: usb-audio: Add QUIRK_FLAG_SKIP_IFACE_SETUP") Reported-by: Dag Smedberg Tested-by: Dag Smedberg Signed-off-by: Dag Smedberg Link: https://patch.msgid.link/20260329170420.4122-1-dag@dsmedberg.se Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 6e40c18c37f9..116da076a194 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2435,6 +2435,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_VALIDATE_RATES), DEVICE_FLG(0x1235, 0x8006, 0), /* Focusrite Scarlett 2i2 1st Gen */ DEVICE_FLG(0x1235, 0x800a, 0), /* Focusrite Scarlett 2i4 1st Gen */ + DEVICE_FLG(0x1235, 0x801c, 0), /* Focusrite Scarlett Solo 1st Gen */ VENDOR_FLG(0x1235, /* Focusrite Novation */ QUIRK_FLAG_SKIP_CLOCK_SELECTOR | QUIRK_FLAG_SKIP_IFACE_SETUP), From 310a4a9cbb17037668ea440f6a3964d00705b400 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Mar 2026 12:06:38 +0100 Subject: [PATCH 383/712] gpio: shared: shorten the critical section in gpiochip_setup_shared() Commit 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set") introduced a critical section around the adjustmenet of entry->offset. However this may cause a deadlock if we create the auxiliary shared proxy devices with this lock taken. We only need to protect entry->offset while it's read/written so shorten the critical section and release the lock before creating the proxy device as the field in question is no longer accessed at this point. Fixes: 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set") Reported-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260325-gpio-shared-deadlock-v1-1-e4e7a5319e95@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 56 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index e257212fa5e3..e02d6b93a4ab 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -533,48 +533,48 @@ int gpiochip_setup_shared(struct gpio_chip *gc) * exposing shared pins. Find them and create the proxy devices. */ list_for_each_entry(entry, &gpio_shared_list, list) { - guard(mutex)(&entry->lock); - if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; if (list_count_nodes(&entry->refs) <= 1) continue; + scoped_guard(mutex, &entry->lock) { #if IS_ENABLED(CONFIG_OF) - if (is_of_node(entry->fwnode) && gc->of_xlate) { - /* - * This is the earliest that we can tranlate the - * devicetree offset to the chip offset. - */ - struct of_phandle_args gpiospec = { }; + if (is_of_node(entry->fwnode) && gc->of_xlate) { + /* + * This is the earliest that we can tranlate the + * devicetree offset to the chip offset. + */ + struct of_phandle_args gpiospec = { }; - gpiospec.np = to_of_node(entry->fwnode); - gpiospec.args_count = 2; - gpiospec.args[0] = entry->offset; + gpiospec.np = to_of_node(entry->fwnode); + gpiospec.args_count = 2; + gpiospec.args[0] = entry->offset; - ret = gc->of_xlate(gc, &gpiospec, NULL); - if (ret < 0) - return ret; + ret = gc->of_xlate(gc, &gpiospec, NULL); + if (ret < 0) + return ret; - entry->offset = ret; - } + entry->offset = ret; + } #endif /* CONFIG_OF */ - desc = &gdev->descs[entry->offset]; + desc = &gdev->descs[entry->offset]; - __set_bit(GPIOD_FLAG_SHARED, &desc->flags); - /* - * Shared GPIOs are not requested via the normal path. Make - * them inaccessible to anyone even before we register the - * chip. - */ - ret = gpiod_request_commit(desc, "shared"); - if (ret) - return ret; + __set_bit(GPIOD_FLAG_SHARED, &desc->flags); + /* + * Shared GPIOs are not requested via the normal path. Make + * them inaccessible to anyone even before we register the + * chip. + */ + ret = gpiod_request_commit(desc, "shared"); + if (ret) + return ret; - pr_debug("GPIO %u owned by %s is shared by multiple consumers\n", - entry->offset, gpio_device_get_label(gdev)); + pr_debug("GPIO %u owned by %s is shared by multiple consumers\n", + entry->offset, gpio_device_get_label(gdev)); + } list_for_each_entry(ref, &entry->refs, list) { pr_debug("Setting up a shared GPIO entry for %s (con_id: '%s')\n", From 6b5ef8c88854b343b733b574ea8754c9dab61f41 Mon Sep 17 00:00:00 2001 From: Jamie Gibbons Date: Thu, 26 Mar 2026 17:02:34 +0000 Subject: [PATCH 384/712] dt-bindings: gpio: fix microchip #interrupt-cells The GPIO controller on PolarFire SoC supports more than one type of interrupt and needs two interrupt cells. Fixes: 735806d8a68e9 ("dt-bindings: gpio: add bindings for microchip mpfs gpio") Signed-off-by: Jamie Gibbons Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20260326-wise-gumdrop-49217723a72a@spud Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/microchip,mpfs-gpio.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml index 184432d24ea1..f42c54653d52 100644 --- a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml @@ -37,7 +37,7 @@ properties: const: 2 "#interrupt-cells": - const: 1 + const: 2 ngpios: description: @@ -86,7 +86,7 @@ examples: gpio-controller; #gpio-cells = <2>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, From 2f42c1a6161646cbd29b443459fd635d29eda634 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 27 Mar 2026 14:32:53 +0100 Subject: [PATCH 385/712] drm/ast: dp501: Fix initialization of SCU2C Ast's DP501 initialization reads the register SCU2C at offset 0x1202c and tries to set it to source data from VGA. But writes the update to offset 0x0, with unknown results. Write the result to SCU instead. The bug only happens in ast_init_analog(). There's similar code in ast_init_dvo(), which works correctly. Signed-off-by: Thomas Zimmermann Fixes: 83c6620bae3f ("drm/ast: initial DP501 support (v0.2)") Reviewed-by: Jocelyn Falempe Cc: Dave Airlie Cc: Thomas Zimmermann Cc: Jocelyn Falempe Cc: dri-devel@lists.freedesktop.org Cc: # v3.16+ Link: https://patch.msgid.link/20260327133532.79696-2-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_dp501.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index 9e19d8c17730..677c52c0d99a 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -436,7 +436,7 @@ static void ast_init_analog(struct ast_device *ast) /* Finally, clear bits [17:16] of SCU2c */ data = ast_read32(ast, 0x1202c); data &= 0xfffcffff; - ast_write32(ast, 0, data); + ast_write32(ast, 0x1202c, data); /* Disable DVO */ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00); From f1af71d568e55536d9297bfa7907ad497108cf30 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 30 Mar 2026 15:53:34 +0800 Subject: [PATCH 386/712] ALSA: hda/realtek: Add quirk for ASUS ROG Strix SCAR 15 ASUS ROG Strix SCAR 15, like the Strix G15, requires the ALC285_FIXUP_ASUS_G533Z_PINS quirk to work properly. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221247 Cc: Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260330075334.50962-2-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 78ab0e86621b..c14046e09aa4 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7269,6 +7269,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1584, "ASUS UM3406GA ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1602, "ASUS ROG Strix SCAR 15", ALC285_FIXUP_ASUS_G533Z_PINS), SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), From 917e3ad3321e75ca0223d5ccf26ceda116aa51e1 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 25 Mar 2026 16:48:24 +0100 Subject: [PATCH 387/712] x86/kexec: Disable KCOV instrumentation after load_segments() The load_segments() function changes segment registers, invalidating GS base (which KCOV relies on for per-cpu data). When CONFIG_KCOV is enabled, any subsequent instrumented C code call (e.g. native_gdt_invalidate()) begins crashing the kernel in an endless loop. To reproduce the problem, it's sufficient to do kexec on a KCOV-instrumented kernel: $ kexec -l /boot/otherKernel $ kexec -e The real-world context for this problem is enabling crash dump collection in syzkaller. For this, the tool loads a panic kernel before fuzzing and then calls makedumpfile after the panic. This workflow requires both CONFIG_KEXEC and CONFIG_KCOV to be enabled simultaneously. Adding safeguards directly to the KCOV fast-path (__sanitizer_cov_trace_pc()) is also undesirable as it would introduce an extra performance overhead. Disabling instrumentation for the individual functions would be too fragile, so disable KCOV instrumentation for the entire machine_kexec_64.c and physaddr.c. If coverage-guided fuzzing ever needs these components in the future, other approaches should be considered. The problem is not relevant for 32 bit kernels as CONFIG_KCOV is not supported there. [ bp: Space out comment for better readability. ] Fixes: 0d345996e4cb ("x86/kernel: increase kcov coverage under arch/x86/kernel folder") Signed-off-by: Aleksandr Nogikh Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Dmitry Vyukov Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260325154825.551191-1-nogikh@google.com --- arch/x86/kernel/Makefile | 14 ++++++++++++++ arch/x86/mm/Makefile | 2 ++ 2 files changed, 16 insertions(+) diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index e9aeeeafad17..47a32f583930 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -44,6 +44,20 @@ KCOV_INSTRUMENT_unwind_orc.o := n KCOV_INSTRUMENT_unwind_frame.o := n KCOV_INSTRUMENT_unwind_guess.o := n +# Disable KCOV to prevent crashes during kexec: load_segments() invalidates +# the GS base, which KCOV relies on for per-CPU data. +# +# As KCOV and KEXEC compatibility should be preserved (e.g. syzkaller is +# using it to collect crash dumps during kernel fuzzing), disabling +# KCOV for KEXEC kernels is not an option. Selectively disabling KCOV +# instrumentation for individual affected functions can be fragile, while +# adding more checks to KCOV would slow it down. +# +# As a compromise solution, disable KCOV instrumentation for the whole +# source code file. If its coverage is ever needed, other approaches +# should be considered. +KCOV_INSTRUMENT_machine_kexec_64.o := n + CFLAGS_head32.o := -fno-stack-protector CFLAGS_head64.o := -fno-stack-protector CFLAGS_irq.o := -I $(src)/../include/asm/trace diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 5b9908f13dcf..3a5364853eab 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -4,6 +4,8 @@ KCOV_INSTRUMENT_tlb.o := n KCOV_INSTRUMENT_mem_encrypt.o := n KCOV_INSTRUMENT_mem_encrypt_amd.o := n KCOV_INSTRUMENT_pgprot.o := n +# See the "Disable KCOV" comment in arch/x86/kernel/Makefile. +KCOV_INSTRUMENT_physaddr.o := n KASAN_SANITIZE_mem_encrypt.o := n KASAN_SANITIZE_mem_encrypt_amd.o := n From 73cd1f97946ae3796544448ff12c07f399bb2881 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 29 Mar 2026 19:14:05 +0800 Subject: [PATCH 388/712] spi: stm32-ospi: Fix resource leak in remove() callback The remove() callback returned early if pm_runtime_resume_and_get() failed, skipping the cleanup of spi controller and other resources. Remove the early return so cleanup completes regardless of PM resume result. Fixes: 79b8a705e26c ("spi: stm32: Add OSPI driver") Signed-off-by: Felix Gu Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260329-ospi-v1-1-cc8cf1c82c4a@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index c98afe02a1b6..2baf651c0a6d 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -989,11 +989,8 @@ static int stm32_ospi_probe(struct platform_device *pdev) static void stm32_ospi_remove(struct platform_device *pdev) { struct stm32_ospi *ospi = platform_get_drvdata(pdev); - int ret; - ret = pm_runtime_resume_and_get(ospi->dev); - if (ret < 0) - return; + pm_runtime_resume_and_get(ospi->dev); spi_unregister_controller(ospi->ctrl); /* Disable ospi */ From 5a570c8d6e55689253f6fcc4a198c56cca7e39d6 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 29 Mar 2026 00:07:06 +0800 Subject: [PATCH 389/712] spi: stm32-ospi: Fix reset control leak on probe error When spi_register_controller() fails after reset_control_acquire() succeeds, the reset control is never released. This causes a resource leak in the error path. Add the missing reset_control_release() call in the error path. Fixes: cf2c3eceb757 ("spi: stm32-ospi: Make usage of reset_control_acquire/release() API") Signed-off-by: Felix Gu Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260329-stm32-ospi-v1-1-142122466412@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 2baf651c0a6d..c9f92e85253f 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -965,13 +965,15 @@ static int stm32_ospi_probe(struct platform_device *pdev) if (ret) { /* Disable ospi */ writel_relaxed(0, ospi->regs_base + OSPI_CR); - goto err_pm_resume; + goto err_reset_control; } pm_runtime_put_autosuspend(ospi->dev); return 0; +err_reset_control: + reset_control_release(ospi->rstc); err_pm_resume: pm_runtime_put_sync_suspend(ospi->dev); From 534025950c9fe4dfbe476b3938d73a26814047d1 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 29 Mar 2026 00:07:07 +0800 Subject: [PATCH 390/712] spi: stm32-ospi: Fix DMA channel leak on stm32_ospi_dma_setup() failure When stm32_ospi_dma_setup() fails, the DMA channels allocated by stm32_ospi_get_resources() were never released. Add proper cleanup in the error path. Fixes: e35a7607e05d ("spi: stm32-ospi: Set DMA maxburst dynamically") Signed-off-by: Felix Gu Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260329-stm32-ospi-v1-2-142122466412@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index c9f92e85253f..38405f8f547f 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -928,7 +928,7 @@ static int stm32_ospi_probe(struct platform_device *pdev) dma_cfg.dst_addr = ospi->regs_phys_base + OSPI_DR; ret = stm32_ospi_dma_setup(ospi, &dma_cfg); if (ret) - return ret; + goto err_dma_free; mutex_init(&ospi->lock); @@ -980,6 +980,7 @@ static int stm32_ospi_probe(struct platform_device *pdev) err_pm_enable: pm_runtime_force_suspend(ospi->dev); mutex_destroy(&ospi->lock); +err_dma_free: if (ospi->dma_chtx) dma_release_channel(ospi->dma_chtx); if (ospi->dma_chrx) From 6d192b4f2d644d15d9a9f1d33dab05af936f6540 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Tue, 24 Mar 2026 15:29:37 +0000 Subject: [PATCH 391/712] drm/xe/xe_pagefault: Disallow writes to read-only VMAs The page fault handler should reject write/atomic access to read only VMAs. Add code to handle this in xe_pagefault_service after the VMA lookup. v2: - Apply max line length (Matthew) Fixes: fb544b844508 ("drm/xe: Implement xe_pagefault_queue_work") Signed-off-by: Jonathan Cavitt Suggested-by: Matthew Brost Cc: Shuicheng Lin Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260324152935.72444-7-jonathan.cavitt@intel.com (cherry picked from commit 714ee6754ac5fa3dc078856a196a6b124cd797a0) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pagefault.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c index 6bee53d6ffc3..922a4f3344b1 100644 --- a/drivers/gpu/drm/xe/xe_pagefault.c +++ b/drivers/gpu/drm/xe/xe_pagefault.c @@ -187,6 +187,12 @@ static int xe_pagefault_service(struct xe_pagefault *pf) goto unlock_vm; } + if (xe_vma_read_only(vma) && + pf->consumer.access_type != XE_PAGEFAULT_ACCESS_TYPE_READ) { + err = -EPERM; + goto unlock_vm; + } + atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type); if (xe_vma_is_cpu_addr_mirror(vma)) From 7b9a3a910e96f20b101b0df6b1f5c77b023a4097 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 26 Mar 2026 18:38:38 +0530 Subject: [PATCH 392/712] drm/xe/madvise: Accept canonical GPU addresses in xe_vm_madvise_ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Userspace passes canonical (sign-extended) GPU addresses where bits 63:48 mirror bit 47. The internal GPUVM uses non-canonical form (upper bits zeroed), so passing raw canonical addresses into GPUVM lookups causes mismatches for addresses above 128TiB. Strip the sign extension with xe_device_uncanonicalize_addr() at the top of xe_vm_madvise_ioctl(). Non-canonical addresses are unaffected. Fixes: ada7486c5668 ("drm/xe: Implement madvise ioctl for xe") Suggested-by: Matthew Brost Cc: Thomas Hellström Reviewed-by: Matthew Brost Signed-off-by: Himal Prasad Ghimiray Signed-off-by: Arvind Yadav Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260326130843.3545241-13-arvind.yadav@intel.com (cherry picked from commit 05c8b1cdc54036465ea457a0501a8c2f9409fce7) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_vm_madvise.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index bc39a9a9790c..b4086129a364 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -408,8 +408,15 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil struct xe_device *xe = to_xe_device(dev); struct xe_file *xef = to_xe_file(file); struct drm_xe_madvise *args = data; - struct xe_vmas_in_madvise_range madvise_range = {.addr = args->start, - .range = args->range, }; + struct xe_vmas_in_madvise_range madvise_range = { + /* + * Userspace may pass canonical (sign-extended) addresses. + * Strip the sign extension to get the internal non-canonical + * form used by the GPUVM, matching xe_vm_bind_ioctl() behavior. + */ + .addr = xe_device_uncanonicalize_addr(xe, args->start), + .range = args->range, + }; struct xe_madvise_details details; struct xe_vm *vm; struct drm_exec exec; @@ -439,7 +446,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil if (err) goto unlock_vm; - err = xe_vm_alloc_madvise_vma(vm, args->start, args->range); + err = xe_vm_alloc_madvise_vma(vm, madvise_range.addr, args->range); if (err) goto madv_fini; @@ -482,7 +489,8 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args, &details); - err = xe_vm_invalidate_madvise_range(vm, args->start, args->start + args->range); + err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, + madvise_range.addr + args->range); if (madvise_range.has_svm_userptr_vmas) xe_svm_notifier_unlock(vm); From e2628e670bb0923fcdc00828bfcd67b26a7df020 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:20 -0700 Subject: [PATCH 393/712] drm/xe/pxp: Clean up termination status on failure If the PXP HW termination fails during PXP start, the normal completion code won't be called, so the termination will remain uncomplete. To avoid unnecessary waits, mark the termination as completed from the error path. Note that we already do this if the termination fails when handling a termination irq from the HW. Fixes: f8caa80154c4 ("drm/xe/pxp: Add PXP queue tracking and session start") Signed-off-by: Daniele Ceraolo Spurio Cc: Alan Previn Teres Alexis Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Link: https://patch.msgid.link/20260324153718.3155504-7-daniele.ceraolospurio@intel.com (cherry picked from commit 5d9e708d2a69ab1f64a17aec810cd7c70c5b9fab) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index d61446bf9c19..782281dbe9e0 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -583,6 +583,7 @@ static int pxp_start(struct xe_pxp *pxp, u8 type) drm_err(&pxp->xe->drm, "PXP termination failed before start\n"); mutex_lock(&pxp->mutex); pxp->status = XE_PXP_ERROR; + complete_all(&pxp->termination); goto out_unlock; } From 4fed244954c2dc9aafa333d08f66b14345225e03 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:21 -0700 Subject: [PATCH 394/712] drm/xe/pxp: Remove incorrect handling of impossible state during suspend The default case of the PXP suspend switch is incorrectly exiting without releasing the lock. However, this case is impossible to hit because we're switching on an enum and all the valid enum values have their own cases. Therefore, we can just get rid of the default case and rely on the compiler to warn us if a new enum value is added and we forget to add it to the switch. Fixes: 51462211f4a9 ("drm/xe/pxp: add PXP PM support") Signed-off-by: Daniele Ceraolo Spurio Cc: Alan Previn Teres Alexis Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Link: https://patch.msgid.link/20260324153718.3155504-8-daniele.ceraolospurio@intel.com (cherry picked from commit f1b5a77fc9b6a90cd9a5e3db9d4c73ae1edfcfac) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 782281dbe9e0..fa82d606953e 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -871,11 +871,6 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) pxp->key_instance++; needs_queue_inval = true; break; - default: - drm_err(&pxp->xe->drm, "unexpected state during PXP suspend: %u", - pxp->status); - ret = -EIO; - goto out; } /* @@ -900,7 +895,6 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) pxp->last_suspend_key_instance = pxp->key_instance; -out: return ret; } From 76903b2057c8677c2c006e87fede15f496555dc0 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:22 -0700 Subject: [PATCH 395/712] drm/xe/pxp: Clear restart flag in pxp_start after jumping back If we don't clear the flag we'll keep jumping back at the beginning of the function once we reach the end. Fixes: ccd3c6820a90 ("drm/xe/pxp: Decouple queue addition from PXP start") Signed-off-by: Daniele Ceraolo Spurio Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Link: https://patch.msgid.link/20260324153718.3155504-9-daniele.ceraolospurio@intel.com (cherry picked from commit 0850ec7bb2459602351639dccf7a68a03c9d1ee0) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index fa82d606953e..088a1ab75f70 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -512,7 +512,7 @@ static int __exec_queue_add(struct xe_pxp *pxp, struct xe_exec_queue *q) static int pxp_start(struct xe_pxp *pxp, u8 type) { int ret = 0; - bool restart = false; + bool restart; if (!xe_pxp_is_enabled(pxp)) return -ENODEV; @@ -541,6 +541,8 @@ static int pxp_start(struct xe_pxp *pxp, u8 type) msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) return -ETIMEDOUT; + restart = false; + mutex_lock(&pxp->mutex); /* If PXP is not already active, turn it on */ From e3fb579872a8d9cf264c52710d5839de3afa6fc1 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:23 -0700 Subject: [PATCH 396/712] drm/xe/pxp: Don't allow PXP on older PTL GSC FWs On PTL, older GSC FWs have a bug that can cause them to crash during PXP invalidation events, which leads to a complete loss of power management on the media GT. Therefore, we can't use PXP on FWs that have this bug, which was fixed in PTL GSC build 1396. Fixes: b1dcec9bd8a1 ("drm/xe/ptl: Enable PXP for PTL") Signed-off-by: Daniele Ceraolo Spurio Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Acked-by: Rodrigo Vivi Link: https://patch.msgid.link/20260324153718.3155504-10-daniele.ceraolospurio@intel.com (cherry picked from commit 6eb04caaa972934c9b6cea0e0c29e466bf9a346f) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 088a1ab75f70..872217f30375 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -380,6 +380,18 @@ int xe_pxp_init(struct xe_device *xe) return 0; } + /* + * On PTL, older GSC FWs have a bug that can cause them to crash during + * PXP invalidation events, which leads to a complete loss of power + * management on the media GT. Therefore, we can't use PXP on FWs that + * have this bug, which was fixed in PTL GSC build 1396. + */ + if (xe->info.platform == XE_PANTHERLAKE && + gt->uc.gsc.fw.versions.found[XE_UC_FW_VER_RELEASE].build < 1396) { + drm_info(&xe->drm, "PXP requires PTL GSC build 1396 or newer\n"); + return 0; + } + pxp = drmm_kzalloc(&xe->drm, sizeof(struct xe_pxp), GFP_KERNEL); if (!pxp) { err = -ENOMEM; From bce7cd6db2386e7a2b49ad3c09df0beabddfa3c4 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 26 Feb 2026 17:52:25 -0800 Subject: [PATCH 397/712] drm/xe: Disable garbage collector work item on SVM close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an SVM is closed, the garbage collector work item must be stopped synchronously and any future queuing must be prevented. Replace flush_work() with disable_work_sync() to ensure both conditions are met. Fixes: 63f6e480d115 ("drm/xe: Add SVM garbage collector") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Thomas Hellström Link: https://patch.msgid.link/20260227015225.3081787-1-matthew.brost@intel.com (cherry picked from commit 2247feb9badca5a4774df9a437bfc44fba4f22de) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index ca67d0bdbfac..c6b92d4ea6f4 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -903,7 +903,7 @@ int xe_svm_init(struct xe_vm *vm) void xe_svm_close(struct xe_vm *vm) { xe_assert(vm->xe, xe_vm_is_closed(vm)); - flush_work(&vm->svm.garbage_collector.work); + disable_work_sync(&vm->svm.garbage_collector.work); xe_svm_put_pagemaps(vm); drm_pagemap_release_owner(&vm->svm.peer); } From 56b7432b7e8e6ae1b289cb405d16db4150ef193b Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 26 Mar 2026 14:01:15 -0700 Subject: [PATCH 398/712] drm/xe: Avoid memory allocations in xe_device_declare_wedged() xe_device_declare_wedged() runs in the DMA-fence signaling path, where GFP_KERNEL memory allocations are not allowed. However, registering xe_device_wedged_fini via drmm_add_action_or_reset() triggers a GFP_KERNEL allocation. Fix this by deferring the registration of xe_device_wedged_fini until late in the driver load sequence. Additionally, drop the wedged PM reference only if the device is actually wedged in xe_device_wedged_fini. Fixes: 452bca0edbd0 ("drm/xe: Don't suspend device upon wedge") Signed-off-by: Matthew Brost Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260326210116.202585-2-matthew.brost@intel.com (cherry picked from commit b08ceb443866808b881b12d4183008d214d816c1) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_device.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 52ee24e9cd3a..3eb06b27db7e 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -837,6 +837,14 @@ static void detect_preproduction_hw(struct xe_device *xe) } } +static void xe_device_wedged_fini(struct drm_device *drm, void *arg) +{ + struct xe_device *xe = arg; + + if (atomic_read(&xe->wedged.flag)) + xe_pm_runtime_put(xe); +} + int xe_device_probe(struct xe_device *xe) { struct xe_tile *tile; @@ -1013,6 +1021,10 @@ int xe_device_probe(struct xe_device *xe) detect_preproduction_hw(xe); + err = drmm_add_action_or_reset(&xe->drm, xe_device_wedged_fini, xe); + if (err) + goto err_unregister_display; + return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); err_unregister_display: @@ -1216,13 +1228,6 @@ u64 xe_device_uncanonicalize_addr(struct xe_device *xe, u64 address) return address & GENMASK_ULL(xe->info.va_bits - 1, 0); } -static void xe_device_wedged_fini(struct drm_device *drm, void *arg) -{ - struct xe_device *xe = arg; - - xe_pm_runtime_put(xe); -} - /** * DOC: Xe Device Wedging * @@ -1300,15 +1305,9 @@ void xe_device_declare_wedged(struct xe_device *xe) return; } - xe_pm_runtime_get_noresume(xe); - - if (drmm_add_action_or_reset(&xe->drm, xe_device_wedged_fini, xe)) { - drm_err(&xe->drm, "Failed to register xe_device_wedged_fini clean-up. Although device is wedged.\n"); - return; - } - if (!atomic_xchg(&xe->wedged.flag, 1)) { xe->needs_flr_on_fini = true; + xe_pm_runtime_get_noresume(xe); drm_err(&xe->drm, "CRITICAL: Xe has declared device %s as wedged.\n" "IOCTLs and executions are blocked. Only a rebind may clear the failure\n" From 27c299698464c515c5cd97b4fcf1a0e38600b2ac Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 30 Mar 2026 17:51:33 +0800 Subject: [PATCH 399/712] ASoC: amd: yc: Add DMI quirk for ASUS Vivobook Pro 16X OLED M7601RM Add a DMI quirk for the ASUS Vivobook Pro 16X OLED M7601RM fixing the issue where the internal microphone was not detected. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221288 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260330095133.81786-1-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 6f1c105ca77e..87d6aeb78807 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -731,6 +731,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7VE"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "M7601RM"), + } + }, {} }; From 8ec017cf31299c4b6287ebe27afe81c986aeef88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gilson=20Marquato=20J=C3=BAnior?= Date: Mon, 30 Mar 2026 02:43:48 +0100 Subject: [PATCH 400/712] ASoC: amd: yc: Add DMI entry for HP Laptop 15-fc0xxx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP Laptop 15-fc0xxx (subsystem ID 0x103c8dc9) has an internal DMIC connected to the AMD ACP6x audio coprocessor. Add a DMI quirk entry so the internal microphone is properly detected on this model. Tested on HP Laptop 15-fc0237ns with Fedora 43 (kernel 6.19.9). Signed-off-by: Gilson Marquato Júnior Link: https://patch.msgid.link/20260330-hp-15-fc0xxx-dmic-v2-v1-1-6dd6f53a1917@hotmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 87d6aeb78807..aa6200933182 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -45,6 +45,13 @@ static struct snd_soc_card acp6x_card = { }; static const struct dmi_system_id yc_acp_quirk_table[] = { + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15-fc0xxx"), + } + }, { .driver_data = &acp6x_card, .matches = { From 679b771ea05ad0f8eeae83e14a91b8f4f39510c4 Mon Sep 17 00:00:00 2001 From: Justin Chen Date: Wed, 18 Mar 2026 11:57:07 -0700 Subject: [PATCH 401/712] usb: ehci-brcm: fix sleep during atomic echi_brcm_wait_for_sof() gets called after disabling interrupts in ehci_brcm_hub_control(). Use the atomic version of poll_timeout to fix the warning. Fixes: 9df231511bd6 ("usb: ehci: Add new EHCI driver for Broadcom STB SoC's") Cc: stable Signed-off-by: Justin Chen Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/20260318185707.2588431-1-justin.chen@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-brcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 888e8f6670d2..5e3156f94cc6 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -31,8 +31,8 @@ static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay) int res; /* Wait for next microframe (every 125 usecs) */ - res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val, - val != frame_idx, 1, 130); + res = readl_relaxed_poll_timeout_atomic(&ehci->regs->frame_index, + val, val != frame_idx, 1, 130); if (res) ehci_err(ehci, "Error waiting for SOF\n"); udelay(delay); From dd36014ec6042f424ef51b923e607772f7502ee7 Mon Sep 17 00:00:00 2001 From: Miao Li Date: Thu, 19 Mar 2026 13:39:27 +0800 Subject: [PATCH 402/712] usb: quirks: add DELAY_INIT quirk for another Silicon Motion flash drive Another Silicon Motion flash drive also randomly work incorrectly (lsusb does not list the device) on Huawei hisi platforms during 500 reboot cycles, and the DELAY_INIT quirk fixes this issue. Signed-off-by: Miao Li Cc: stable Link: https://patch.msgid.link/20260319053927.264840-1-limiao870622@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 5523a8e29021..26fed25da26e 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -401,6 +401,7 @@ static const struct usb_device_id usb_quirk_list[] = { /* Silicon Motion Flash Drive */ { USB_DEVICE(0x090c, 0x1000), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x090c, 0x2000), .driver_info = USB_QUIRK_DELAY_INIT }, /* Sound Devices USBPre2 */ { USB_DEVICE(0x0926, 0x0202), .driver_info = From eba2936bbe6b752a31725a9eb5c674ecbf21ee7d Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Fri, 20 Mar 2026 14:54:27 +0800 Subject: [PATCH 403/712] usb: gadget: uvc: fix NULL pointer dereference during unbind race Commit b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown") introduced two stages of synchronization waits totaling 1500ms in uvc_function_unbind() to prevent several types of kernel panics. However, this timing-based approach is insufficient during power management (PM) transitions. When the PM subsystem starts freezing user space processes, the wait_event_interruptible_timeout() is aborted early, which allows the unbind thread to proceed and nullify the gadget pointer (cdev->gadget = NULL): [ 814.123447][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind() [ 814.178583][ T3173] PM: suspend entry (deep) [ 814.192487][ T3173] Freezing user space processes [ 814.197668][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind no clean disconnect, wait for release When the PM subsystem resumes or aborts the suspend and tasks are restarted, the V4L2 release path is executed and attempts to access the already nullified gadget pointer, triggering a kernel panic: [ 814.292597][ C0] PM: pm_system_irq_wakeup: 479 triggered dhdpcie_host_wake [ 814.386727][ T3173] Restarting tasks ... [ 814.403522][ T4558] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030 [ 814.404021][ T4558] pc : usb_gadget_deactivate+0x14/0xf4 [ 814.404031][ T4558] lr : usb_function_deactivate+0x54/0x94 [ 814.404078][ T4558] Call trace: [ 814.404080][ T4558] usb_gadget_deactivate+0x14/0xf4 [ 814.404083][ T4558] usb_function_deactivate+0x54/0x94 [ 814.404087][ T4558] uvc_function_disconnect+0x1c/0x5c [ 814.404092][ T4558] uvc_v4l2_release+0x44/0xac [ 814.404095][ T4558] v4l2_release+0xcc/0x130 Address the race condition and NULL pointer dereference by: 1. State Synchronization (flag + mutex) Introduce a 'func_unbound' flag in struct uvc_device. This allows uvc_function_disconnect() to safely skip accessing the nullified cdev->gadget pointer. As suggested by Alan Stern, this flag is protected by a new mutex (uvc->lock) to ensure proper memory ordering and prevent instruction reordering or speculative loads. This mutex is also used to protect 'func_connected' for consistent state management. 2. Explicit Synchronization (completion) Use a completion to synchronize uvc_function_unbind() with the uvc_vdev_release() callback. This prevents Use-After-Free (UAF) by ensuring struct uvc_device is freed after all video device resources are released. Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown") Cc: stable Suggested-by: Alan Stern Suggested-by: Greg Kroah-Hartman Signed-off-by: Jimmy Hu Link: https://patch.msgid.link/20260320065427.1374555-1-hhhuuu@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 39 ++++++++++++++++++++++++-- drivers/usb/gadget/function/uvc.h | 3 ++ drivers/usb/gadget/function/uvc_v4l2.c | 5 +++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 494fdbc4e85b..8d404d88391c 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -413,6 +413,12 @@ uvc_function_disconnect(struct uvc_device *uvc) { int ret; + guard(mutex)(&uvc->lock); + if (uvc->func_unbound) { + dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n"); + return; + } + if ((ret = usb_function_deactivate(&uvc->func)) < 0) uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret); } @@ -431,6 +437,15 @@ static ssize_t function_name_show(struct device *dev, static DEVICE_ATTR_RO(function_name); +static void uvc_vdev_release(struct video_device *vdev) +{ + struct uvc_device *uvc = video_get_drvdata(vdev); + + /* Signal uvc_function_unbind() that the video device has been released */ + if (uvc->vdev_release_done) + complete(uvc->vdev_release_done); +} + static int uvc_register_video(struct uvc_device *uvc) { @@ -443,7 +458,7 @@ uvc_register_video(struct uvc_device *uvc) uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev; uvc->vdev.fops = &uvc_v4l2_fops; uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops; - uvc->vdev.release = video_device_release_empty; + uvc->vdev.release = uvc_vdev_release; uvc->vdev.vfl_dir = VFL_DIR_TX; uvc->vdev.lock = &uvc->video.mutex; uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; @@ -659,6 +674,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) int ret = -EINVAL; uvcg_info(f, "%s()\n", __func__); + scoped_guard(mutex, &uvc->lock) + uvc->func_unbound = false; opts = fi_to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ @@ -988,12 +1005,19 @@ static void uvc_free(struct usb_function *f) static void uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) { + DECLARE_COMPLETION_ONSTACK(vdev_release_done); struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); struct uvc_video *video = &uvc->video; long wait_ret = 1; + bool connected; uvcg_info(f, "%s()\n", __func__); + scoped_guard(mutex, &uvc->lock) { + uvc->func_unbound = true; + uvc->vdev_release_done = &vdev_release_done; + connected = uvc->func_connected; + } kthread_cancel_work_sync(&video->hw_submit); @@ -1006,7 +1030,7 @@ static void uvc_function_unbind(struct usb_configuration *c, * though the video device removal uevent. Allow some time for the * application to close out before things get deleted. */ - if (uvc->func_connected) { + if (connected) { uvcg_dbg(f, "waiting for clean disconnect\n"); wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue, uvc->func_connected == false, msecs_to_jiffies(500)); @@ -1017,7 +1041,10 @@ static void uvc_function_unbind(struct usb_configuration *c, video_unregister_device(&uvc->vdev); v4l2_device_unregister(&uvc->v4l2_dev); - if (uvc->func_connected) { + scoped_guard(mutex, &uvc->lock) + connected = uvc->func_connected; + + if (connected) { /* * Wait for the release to occur to ensure there are no longer any * pending operations that may cause panics when resources are cleaned @@ -1029,6 +1056,10 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret); } + /* Wait for the video device to be released */ + wait_for_completion(&vdev_release_done); + uvc->vdev_release_done = NULL; + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); @@ -1047,6 +1078,8 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); mutex_init(&uvc->video.mutex); + mutex_init(&uvc->lock); + uvc->func_unbound = true; uvc->state = UVC_STATE_DISCONNECTED; init_waitqueue_head(&uvc->func_connected_queue); opts = fi_to_f_uvc_opts(fi); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 676419a04976..7abfdd5e1eef 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -155,6 +155,9 @@ struct uvc_device { enum uvc_state state; struct usb_function func; struct uvc_video video; + struct completion *vdev_release_done; + struct mutex lock; /* protects func_unbound and func_connected */ + bool func_unbound; bool func_connected; wait_queue_head_t func_connected_queue; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index ed48d38498fb..514e5930b9ca 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -574,6 +574,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) return -EINVAL; + guard(mutex)(&uvc->lock); + if (sub->type == UVC_EVENT_SETUP && uvc->func_connected) return -EBUSY; @@ -595,7 +597,8 @@ static void uvc_v4l2_disable(struct uvc_device *uvc) uvc_function_disconnect(uvc); uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); - uvc->func_connected = false; + scoped_guard(mutex, &uvc->lock) + uvc->func_connected = false; wake_up_interruptible(&uvc->func_connected_queue); } From 9bb4b5ed7f8c4f95cc556bdf042b0ba2fa13557a Mon Sep 17 00:00:00 2001 From: Juno Choi Date: Tue, 24 Mar 2026 10:49:10 +0900 Subject: [PATCH 404/712] usb: dwc2: gadget: Fix spin_lock/unlock mismatch in dwc2_hsotg_udc_stop() dwc2_gadget_exit_clock_gating() internally calls call_gadget() macro, which expects hsotg->lock to be held since it does spin_unlock/spin_lock around the gadget driver callback invocation. However, dwc2_hsotg_udc_stop() calls dwc2_gadget_exit_clock_gating() without holding the lock. This leads to: - spin_unlock on a lock that is not held (undefined behavior) - The lock remaining held after dwc2_gadget_exit_clock_gating() returns, causing a deadlock when spin_lock_irqsave() is called later in the same function. Fix this by acquiring hsotg->lock before calling dwc2_gadget_exit_clock_gating() and releasing it afterwards, which satisfies the locking requirement of the call_gadget() macro. Fixes: af076a41f8a2 ("usb: dwc2: also exit clock_gating when stopping udc while suspended") Cc: stable Signed-off-by: Juno Choi Link: https://patch.msgid.link/20260324014910.2798425-1-juno.choi@lge.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/gadget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index d216e26c787b..c8b02c27d27d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4607,7 +4607,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) /* Exit clock gating when driver is stopped. */ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended && !hsotg->params.no_clock_gating) { + spin_lock_irqsave(&hsotg->lock, flags); dwc2_gadget_exit_clock_gating(hsotg, 0); + spin_unlock_irqrestore(&hsotg->lock, flags); } /* all endpoints should be shutdown */ From b65d4ca1d1059ace899170d1dfd02639ce971d2d Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Tue, 24 Mar 2026 10:29:03 +0000 Subject: [PATCH 405/712] usb: typec: Remove alt->adev.dev.class assignment The typec plug alternate mode is already registered as part of the bus. When both class and bus are set for a device, device_add() attempts to create the "subsystem" symlink in the device's sysfs directory twice, once for the bus and once for the class. This results in a duplicate filename error during registration, causing the alternate mode registration to fail with warnings: cannot create duplicate filename '/devices/pci0000:00/0000:00:1f.0/ PNP0C09:00/GOOG0004:00/cros-ec-dev.1.auto/cros_ec_ucsi.3.auto/typec/ port1/port1-cable/port1-plug0/port1-plug0.0/subsystem' typec port0-plug0: failed to register alternate mode (-17) cros_ec_ucsi.3.auto: failed to registers svid 0x8087 mode 1 Cc: stable Fixes: 67ab45426215 ("usb: typec: Set the bus also for the port and plug altmodes") Tested-by: Madhu M Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260324102903.1416210-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 831430909471..0977581ad1b6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -686,10 +686,6 @@ typec_register_altmode(struct device *parent, alt->adev.dev.bus = &typec_bus; - /* Plug alt modes need a class to generate udev events. */ - if (is_typec_plug(parent)) - alt->adev.dev.class = &typec_class; - ret = device_register(&alt->adev.dev); if (ret) { dev_err(parent, "failed to register alternate mode (%d)\n", From 3b8ae9817686efb3ea789ca9d4efdff2ce9c1c04 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Tue, 24 Mar 2026 10:30:12 +0000 Subject: [PATCH 406/712] usb: typec: thunderbolt: Set enter_vdo during initialization In the current implementation, if a cable's alternate mode enter operation is not supported, the tbt->plug[TYPEC_PLUG_SOP_P] pointer is cleared by the time tbt_enter_mode() is called. This prevents the driver from identifying the cable's VDO. As a result, the Thunderbolt connection falls back to the default TBT_CABLE_USB3_PASSIVE speed, even if the cable supports higher speeds. To ensure the correct VDO value is used during mode entry, calculate and store the enter_vdo earlier during the initialization phase in tbt_ready(). Cc: stable Fixes: 100e25738659 ("usb: typec: Add driver for Thunderbolt 3 Alternate Mode") Tested-by: Madhu M Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260324103012.1417616-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/thunderbolt.c | 44 ++++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c index c4c5da6154da..32250b94262a 100644 --- a/drivers/usb/typec/altmodes/thunderbolt.c +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -39,28 +39,7 @@ static bool tbt_ready(struct typec_altmode *alt); static int tbt_enter_mode(struct tbt_altmode *tbt) { - struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P]; - u32 vdo; - - vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1); - vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0; - vdo |= TBT_MODE; - - if (plug) { - if (typec_cable_is_active(tbt->cable)) - vdo |= TBT_ENTER_MODE_ACTIVE_CABLE; - - vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo)); - vdo |= plug->vdo & TBT_CABLE_ROUNDED; - vdo |= plug->vdo & TBT_CABLE_OPTICAL; - vdo |= plug->vdo & TBT_CABLE_RETIMER; - vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING; - } else { - vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE); - } - - tbt->enter_vdo = vdo; - return typec_altmode_enter(tbt->alt, &vdo); + return typec_altmode_enter(tbt->alt, &tbt->enter_vdo); } static void tbt_altmode_work(struct work_struct *work) @@ -337,6 +316,7 @@ static bool tbt_ready(struct typec_altmode *alt) { struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); struct typec_altmode *plug; + u32 vdo; if (tbt->cable) return true; @@ -364,6 +344,26 @@ static bool tbt_ready(struct typec_altmode *alt) tbt->plug[i] = plug; } + vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1); + vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0; + vdo |= TBT_MODE; + plug = tbt->plug[TYPEC_PLUG_SOP_P]; + + if (plug) { + if (typec_cable_is_active(tbt->cable)) + vdo |= TBT_ENTER_MODE_ACTIVE_CABLE; + + vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo)); + vdo |= plug->vdo & TBT_CABLE_ROUNDED; + vdo |= plug->vdo & TBT_CABLE_OPTICAL; + vdo |= plug->vdo & TBT_CABLE_RETIMER; + vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING; + } else { + vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE); + } + + tbt->enter_vdo = vdo; + return true; } From 269c26464dcf8b54b0dd9c333721c30ee44ae297 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 30 Mar 2026 14:35:18 +0800 Subject: [PATCH 407/712] dt-bindings: connector: add pd-disable dependency When Power Delivery is not supported, the source is unable to obtain the current capability from the Source PDO. As a result, typec-power-opmode needs to be added to advertise such capability. Acked-by: Conor Dooley Cc: stable Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260330063518.719345-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/connector/usb-connector.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 11e40d225b9f..d97b29e49bf5 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -301,6 +301,7 @@ properties: maxItems: 4 dependencies: + pd-disable: [typec-power-opmode] sink-vdos-v1: [ sink-vdos ] sink-vdos: [ sink-vdos-v1 ] From caa27923aacd8a5869207842f2ab1657c6c0c7bc Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:44 +0800 Subject: [PATCH 408/712] usb: gadget: f_subset: Fix unbalanced refcnt in geth_free geth_alloc() increments the reference count, but geth_free() fails to decrement it. This prevents the configuration of attributes via configfs after unlinking the function. Decrement the reference count in geth_free() to ensure proper cleanup. Fixes: 02832e56f88a ("usb: gadget: f_subset: add configfs support") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-1-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_subset.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 076072386e5e..74dc6da5c767 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -6,6 +6,7 @@ * Copyright (C) 2008 Nokia Corporation */ +#include #include #include #include @@ -449,8 +450,13 @@ static struct usb_function_instance *geth_alloc_inst(void) static void geth_free(struct usb_function *f) { struct f_gether *eth; + struct f_gether_opts *opts; + + opts = container_of(f->fi, struct f_gether_opts, func_inst); eth = func_to_geth(f); + scoped_guard(mutex, &opts->lock) + opts->refcnt--; kfree(eth); } From 8d8c68b1fc06ece60cf43e1306ff0f4ac121547e Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:45 +0800 Subject: [PATCH 409/712] usb: gadget: f_rndis: Protect RNDIS options with mutex The class/subclass/protocol options are suspectible to race conditions as they can be accessed concurrently through configfs. Use existing mutex to protect these options. This issue was identified during code inspection. Fixes: 73517cf49bd4 ("usb: gadget: add RNDIS configfs options for class/subclass/protocol") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-2-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_rndis.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 8b11d8d6d89c..521b4619d6be 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -11,6 +11,7 @@ /* #define VERBOSE_DEBUG */ +#include #include #include #include @@ -678,9 +679,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) return -ENOMEM; } - rndis_iad_descriptor.bFunctionClass = rndis_opts->class; - rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; - rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; + scoped_guard(mutex, &rndis_opts->lock) { + rndis_iad_descriptor.bFunctionClass = rndis_opts->class; + rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; + rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; + } /* * in drivers/usb/gadget/configfs.c:configfs_composite_bind() From 57f531df3618b26df053798a96aa5ade44e00e1a Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:46 +0800 Subject: [PATCH 410/712] usb: gadget: u_ncm: Add kernel-doc comments for struct f_ncm_opts Provide kernel-doc descriptions for the fields in struct f_ncm_opts to improve code readability and maintainability. Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-3-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ncm.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index b1f3db8b68c1..ce2f6358688a 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -15,6 +15,20 @@ #include +/** + * struct f_ncm_opts - NCM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the NCM function. + * @bind_count: Tracks the number of configurations the NCM function is + * bound to, preventing double-registration of the @net device. + * @ncm_interf_group: ConfigFS group for NCM interface. + * @ncm_os_desc: USB OS descriptor for NCM. + * @ncm_ext_compat_id: Extended compatibility ID. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + * @max_segment_size: Maximum segment size. + */ struct f_ncm_opts { struct usb_function_instance func_inst; struct net_device *net; @@ -23,12 +37,7 @@ struct f_ncm_opts { struct config_group *ncm_interf_group; struct usb_os_desc ncm_os_desc; char ncm_ext_compat_id[16]; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ + struct mutex lock; int refcnt; From b2cc4fae67a51f60d81d6af2678696accb07c656 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:47 +0800 Subject: [PATCH 411/712] usb: gadget: f_ecm: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the bound flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: fee562a6450b ("usb: gadget: f_ecm: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-4-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ecm.c | 35 +++++++++++++++++++---------- drivers/usb/gadget/function/u_ecm.h | 21 ++++++++++++----- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index e0c02121374e..e495bac4efeb 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -681,6 +681,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_ecm_opts *ecm_opts; + struct net_device *net __free(detach_gadget) = NULL; struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_ecm(cdev->gadget)) @@ -688,18 +689,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); - mutex_lock(&ecm_opts->lock); + scoped_guard(mutex, &ecm_opts->lock) + if (ecm_opts->bind_count == 0 && !ecm_opts->bound) { + if (!device_is_registered(&ecm_opts->net->dev)) { + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + } else + status = gether_attach_gadget(ecm_opts->net, cdev->gadget); - gether_set_gadget(ecm_opts->net, cdev->gadget); - - if (!ecm_opts->bound) { - status = gether_register_netdev(ecm_opts->net); - ecm_opts->bound = true; - } - - mutex_unlock(&ecm_opts->lock); - if (status) - return status; + if (status) + return status; + net = ecm_opts->net; + } ecm_string_defs[1].s = ecm->ethaddr; @@ -790,6 +791,9 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->notify_req = no_free_ptr(request); + ecm_opts->bind_count++; + retain_and_null_ptr(net); + DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n", ecm->port.in_ep->name, ecm->port.out_ep->name, ecm->notify->name); @@ -836,7 +840,7 @@ static void ecm_free_inst(struct usb_function_instance *f) struct f_ecm_opts *opts; opts = container_of(f, struct f_ecm_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -906,9 +910,12 @@ static void ecm_free(struct usb_function *f) static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ecm *ecm = func_to_ecm(f); + struct f_ecm_opts *ecm_opts; DBG(c->cdev, "ecm unbind\n"); + ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); + usb_free_all_descriptors(f); if (atomic_read(&ecm->notify_count)) { @@ -918,6 +925,10 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); + + ecm_opts->bind_count--; + if (ecm_opts->bind_count == 0 && !ecm_opts->bound) + gether_detach_gadget(ecm_opts->net); } static struct usb_function *ecm_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h index 77cfb89932be..7f666b9dea02 100644 --- a/drivers/usb/gadget/function/u_ecm.h +++ b/drivers/usb/gadget/function/u_ecm.h @@ -15,17 +15,26 @@ #include +/** + * struct f_ecm_opts - ECM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the ECM function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the ECM function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the ECM function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_ecm_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; + int bind_count; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; From d9270c9a8118c1535409db926ac1e2545dc97b81 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:48 +0800 Subject: [PATCH 412/712] usb: gadget: f_eem: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the bound flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: b29002a15794 ("usb: gadget: f_eem: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-5-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_eem.c | 59 +++++++++++++++-------------- drivers/usb/gadget/function/u_eem.h | 21 +++++++--- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index 0142a0e487ee..ac37d7c1d168 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -7,6 +7,7 @@ * Copyright (C) 2009 EF Johnson Technologies */ +#include #include #include #include @@ -251,24 +252,22 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_eem_opts *eem_opts; + struct net_device *net __free(detach_gadget) = NULL; eem_opts = container_of(f->fi, struct f_eem_opts, func_inst); - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to eem_opts->bound access - */ - if (!eem_opts->bound) { - mutex_lock(&eem_opts->lock); - gether_set_gadget(eem_opts->net, cdev->gadget); - status = gether_register_netdev(eem_opts->net); - mutex_unlock(&eem_opts->lock); - if (status) - return status; - eem_opts->bound = true; - } + + scoped_guard(mutex, &eem_opts->lock) + if (eem_opts->bind_count == 0 && !eem_opts->bound) { + if (!device_is_registered(&eem_opts->net->dev)) { + gether_set_gadget(eem_opts->net, cdev->gadget); + status = gether_register_netdev(eem_opts->net); + } else + status = gether_attach_gadget(eem_opts->net, cdev->gadget); + + if (status) + return status; + net = eem_opts->net; + } us = usb_gstrings_attach(cdev, eem_strings, ARRAY_SIZE(eem_string_defs)); @@ -279,21 +278,19 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; eem->ctrl_id = status; eem_intf.bInterfaceNumber = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc); if (!ep) - goto fail; + return -ENODEV; eem->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc); if (!ep) - goto fail; + return -ENODEV; eem->port.out_ep = ep; /* support all relevant hardware speeds... we expect that when @@ -309,16 +306,14 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function, eem_ss_function, eem_ss_function); if (status) - goto fail; + return status; + + eem_opts->bind_count++; + retain_and_null_ptr(net); DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n", eem->port.in_ep->name, eem->port.out_ep->name); return 0; - -fail: - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req) @@ -597,7 +592,7 @@ static void eem_free_inst(struct usb_function_instance *f) struct f_eem_opts *opts; opts = container_of(f, struct f_eem_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -640,9 +635,17 @@ static void eem_free(struct usb_function *f) static void eem_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_eem_opts *opts; + DBG(c->cdev, "eem unbind\n"); + opts = container_of(f->fi, struct f_eem_opts, func_inst); + usb_free_all_descriptors(f); + + opts->bind_count--; + if (opts->bind_count == 0 && !opts->bound) + gether_detach_gadget(opts->net); } static struct usb_function *eem_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h index 3bd85dfcd71c..78ef55815219 100644 --- a/drivers/usb/gadget/function/u_eem.h +++ b/drivers/usb/gadget/function/u_eem.h @@ -15,17 +15,26 @@ #include +/** + * struct f_eem_opts - EEM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the EEM function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the EEM function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the EEM function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_eem_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; + int bind_count; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; From 06524cd1c9011bee141a87e43ab878641ed3652b Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:49 +0800 Subject: [PATCH 413/712] usb: gadget: f_subset: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the bound flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: 8cedba7c73af ("usb: gadget: f_subset: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-6-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_subset.c | 57 +++++++++++++------------- drivers/usb/gadget/function/u_gether.h | 22 ++++++---- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 74dc6da5c767..6e3265b8a3a0 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -299,25 +299,22 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_gether_opts *gether_opts; + struct net_device *net __free(detach_gadget) = NULL; gether_opts = container_of(f->fi, struct f_gether_opts, func_inst); - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to gether_opts->bound access - */ - if (!gether_opts->bound) { - mutex_lock(&gether_opts->lock); - gether_set_gadget(gether_opts->net, cdev->gadget); - status = gether_register_netdev(gether_opts->net); - mutex_unlock(&gether_opts->lock); - if (status) - return status; - gether_opts->bound = true; - } + scoped_guard(mutex, &gether_opts->lock) + if (gether_opts->bind_count == 0 && !gether_opts->bound) { + if (!device_is_registered(&gether_opts->net->dev)) { + gether_set_gadget(gether_opts->net, cdev->gadget); + status = gether_register_netdev(gether_opts->net); + } else + status = gether_attach_gadget(gether_opts->net, cdev->gadget); + + if (status) + return status; + net = gether_opts->net; + } us = usb_gstrings_attach(cdev, geth_strings, ARRAY_SIZE(geth_string_defs)); @@ -330,20 +327,18 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; subset_data_intf.bInterfaceNumber = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc); if (!ep) - goto fail; + return -ENODEV; geth->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc); if (!ep) - goto fail; + return -ENODEV; geth->port.out_ep = ep; /* support all relevant hardware speeds... we expect that when @@ -361,21 +356,19 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function, ss_eth_function, ss_eth_function); if (status) - goto fail; + return status; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt(). */ + gether_opts->bind_count++; + retain_and_null_ptr(net); + DBG(cdev, "CDC Subset: IN/%s OUT/%s\n", geth->port.in_ep->name, geth->port.out_ep->name); return 0; - -fail: - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item) @@ -418,7 +411,7 @@ static void geth_free_inst(struct usb_function_instance *f) struct f_gether_opts *opts; opts = container_of(f, struct f_gether_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -462,8 +455,16 @@ static void geth_free(struct usb_function *f) static void geth_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_gether_opts *opts; + + opts = container_of(f->fi, struct f_gether_opts, func_inst); + geth_string_defs[0].id = 0; usb_free_all_descriptors(f); + + opts->bind_count--; + if (opts->bind_count == 0 && !opts->bound) + gether_detach_gadget(opts->net); } static struct usb_function *geth_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h index 2f7a373ed449..e7b6b51f69c1 100644 --- a/drivers/usb/gadget/function/u_gether.h +++ b/drivers/usb/gadget/function/u_gether.h @@ -15,17 +15,25 @@ #include +/** + * struct f_gether_opts - subset function options + * @func_inst: USB function instance. + * @net: The net_device associated with the subset function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the subset function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the subset function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_gether_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; - - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ + int bind_count; struct mutex lock; int refcnt; }; From e367599529dc42578545a7f85fde517b35b3cda7 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:50 +0800 Subject: [PATCH 414/712] usb: gadget: f_rndis: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the borrowed_net flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: f466c6353819 ("usb: gadget: f_rndis: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-7-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_rndis.c | 42 ++++++++++++++++----------- drivers/usb/gadget/function/u_rndis.h | 31 +++++++++++++++----- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 521b4619d6be..7de1c5f8e326 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -666,6 +666,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) struct f_rndis_opts *rndis_opts; struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; + struct net_device *net __free(detach_gadget) = NULL; struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_rndis(c)) @@ -683,21 +684,18 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_iad_descriptor.bFunctionClass = rndis_opts->class; rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; - } - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to rndis_opts->bound access - */ - if (!rndis_opts->bound) { - gether_set_gadget(rndis_opts->net, cdev->gadget); - status = gether_register_netdev(rndis_opts->net); - if (status) - return status; - rndis_opts->bound = true; + if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) { + if (!device_is_registered(&rndis_opts->net->dev)) { + gether_set_gadget(rndis_opts->net, cdev->gadget); + status = gether_register_netdev(rndis_opts->net); + } else + status = gether_attach_gadget(rndis_opts->net, cdev->gadget); + + if (status) + return status; + net = rndis_opts->net; + } } us = usb_gstrings_attach(cdev, rndis_strings, @@ -796,6 +794,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) } rndis->notify_req = no_free_ptr(request); + rndis_opts->bind_count++; + retain_and_null_ptr(net); + /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt(). @@ -812,11 +813,11 @@ void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) struct f_rndis_opts *opts; opts = container_of(f, struct f_rndis_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); - opts->borrowed_net = opts->bound = true; + opts->borrowed_net = true; opts->net = net; } EXPORT_SYMBOL_GPL(rndis_borrow_net); @@ -874,7 +875,7 @@ static void rndis_free_inst(struct usb_function_instance *f) opts = container_of(f, struct f_rndis_opts, func_inst); if (!opts->borrowed_net) { - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -943,6 +944,9 @@ static void rndis_free(struct usb_function *f) static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis *rndis = func_to_rndis(f); + struct f_rndis_opts *rndis_opts; + + rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); kfree(f->os_desc_table); f->os_desc_n = 0; @@ -950,6 +954,10 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); + + rndis_opts->bind_count--; + if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) + gether_detach_gadget(rndis_opts->net); } static struct usb_function *rndis_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index a8c409b2f52f..4e64619714dc 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -15,12 +15,34 @@ #include +/** + * struct f_rndis_opts - RNDIS function options + * @func_inst: USB function instance. + * @vendor_id: Vendor ID. + * @manufacturer: Manufacturer string. + * @net: The net_device associated with the RNDIS function. + * @bind_count: Tracks the number of configurations the RNDIS function is + * bound to, preventing double-registration of the @net device. + * @borrowed_net: True if the net_device is shared and pre-registered during + * the legacy composite driver's bind phase (e.g., multi.c). + * If false, the RNDIS function will register the net_device + * during its own bind phase. + * @rndis_interf_group: ConfigFS group for RNDIS interface. + * @rndis_os_desc: USB OS descriptor for RNDIS. + * @rndis_ext_compat_id: Extended compatibility ID. + * @class: USB class. + * @subclass: USB subclass. + * @protocol: USB protocol. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_rndis_opts { struct usb_function_instance func_inst; u32 vendor_id; const char *manufacturer; struct net_device *net; - bool bound; + int bind_count; bool borrowed_net; struct config_group *rndis_interf_group; @@ -30,13 +52,6 @@ struct f_rndis_opts { u8 class; u8 subclass; u8 protocol; - - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; From 40014493cece72a0be5672cd86763e53fb3ec613 Mon Sep 17 00:00:00 2001 From: Liav Mordouch Date: Fri, 27 Mar 2026 20:02:04 +0300 Subject: [PATCH 415/712] vt: discard stale unicode buffer on alt screen exit after resize When enter_alt_screen() saves vc_uni_lines into vc_saved_uni_lines and sets vc_uni_lines to NULL, a subsequent console resize via vc_do_resize() skips reallocating the unicode buffer because vc_uni_lines is NULL. However, vc_saved_uni_lines still points to the old buffer allocated for the original dimensions. When leave_alt_screen() later restores vc_saved_uni_lines, the buffer dimensions no longer match vc_rows/vc_cols. Any operation that iterates over the unicode buffer using the current dimensions (e.g. csi_J clearing the screen) will access memory out of bounds, causing a kernel oops: BUG: unable to handle page fault for address: 0x0000002000000020 RIP: 0010:csi_J+0x133/0x2d0 The faulting address 0x0000002000000020 is two adjacent u32 space characters (0x20) interpreted as a pointer, read from the row data area past the end of the 25-entry pointer array in a buffer allocated for 80x25 but accessed with 240x67 dimensions. Fix this by checking whether the console dimensions changed while in the alternate screen. If they did, free the stale saved buffer instead of restoring it. The unicode screen will be lazily rebuilt via vc_uniscr_check() when next needed. Fixes: 5eb608319bb5 ("vt: save/restore unicode screen buffer for alternate screen") Cc: stable Tested-by: Liav Mordouch Signed-off-by: Liav Mordouch Reviewed-by: Nicolas Pitre Link: https://patch.msgid.link/20260327170204.29706-1-liavmordouch@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index e2df99e3d458..99f15b3e9544 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1901,6 +1901,7 @@ static void leave_alt_screen(struct vc_data *vc) unsigned int rows = min(vc->vc_saved_rows, vc->vc_rows); unsigned int cols = min(vc->vc_saved_cols, vc->vc_cols); u16 *src, *dest; + bool uni_lines_stale; if (vc->vc_saved_screen == NULL) return; /* Not inside an alt-screen */ @@ -1909,7 +1910,18 @@ static void leave_alt_screen(struct vc_data *vc) dest = ((u16 *)vc->vc_origin) + r * vc->vc_cols; memcpy(dest, src, 2 * cols); } - vc_uniscr_set(vc, vc->vc_saved_uni_lines); + /* + * If the console was resized while in the alternate screen, + * vc_saved_uni_lines was allocated for the old dimensions. + * Restoring it would cause out-of-bounds accesses. Discard it + * and let the unicode screen be lazily rebuilt. + */ + uni_lines_stale = vc->vc_saved_rows != vc->vc_rows || + vc->vc_saved_cols != vc->vc_cols; + if (uni_lines_stale) + vc_uniscr_free(vc->vc_saved_uni_lines); + else + vc_uniscr_set(vc, vc->vc_saved_uni_lines); vc->vc_saved_uni_lines = NULL; restore_cur(vc); /* Update the entire screen */ From 3ddbea7542ae529c1a88ef9a8b1ce169126211f6 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 27 Mar 2026 23:09:47 -0400 Subject: [PATCH 416/712] vt: resize saved unicode buffer on alt screen exit after resize Instead of discarding the saved unicode buffer when the console was resized while in the alternate screen, resize it to the current dimensions using vc_uniscr_copy_area() to preserve its content. This properly restores the unicode screen on alt screen exit rather than lazily rebuilding it from a lossy reverse glyph translation. On allocation failure the stale buffer is freed and vc_uni_lines is set to NULL so it gets lazily rebuilt via vc_uniscr_check() when next needed. Fixes: 40014493cece ("vt: discard stale unicode buffer on alt screen exit after resize") Cc: stable Signed-off-by: Nicolas Pitre Link: https://patch.msgid.link/3nsr334n-079q-125n-7807-n4nq818758ns@syhkavp.arg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 99f15b3e9544..b4b19157f05c 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1901,7 +1901,6 @@ static void leave_alt_screen(struct vc_data *vc) unsigned int rows = min(vc->vc_saved_rows, vc->vc_rows); unsigned int cols = min(vc->vc_saved_cols, vc->vc_cols); u16 *src, *dest; - bool uni_lines_stale; if (vc->vc_saved_screen == NULL) return; /* Not inside an alt-screen */ @@ -1912,16 +1911,23 @@ static void leave_alt_screen(struct vc_data *vc) } /* * If the console was resized while in the alternate screen, - * vc_saved_uni_lines was allocated for the old dimensions. - * Restoring it would cause out-of-bounds accesses. Discard it - * and let the unicode screen be lazily rebuilt. + * resize the saved unicode buffer to the current dimensions. + * On allocation failure new_uniscr is NULL, causing the old + * buffer to be freed and vc_uni_lines to be lazily rebuilt + * via vc_uniscr_check() when next needed. */ - uni_lines_stale = vc->vc_saved_rows != vc->vc_rows || - vc->vc_saved_cols != vc->vc_cols; - if (uni_lines_stale) + if (vc->vc_saved_uni_lines && + (vc->vc_saved_rows != vc->vc_rows || + vc->vc_saved_cols != vc->vc_cols)) { + u32 **new_uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); + + if (new_uniscr) + vc_uniscr_copy_area(new_uniscr, vc->vc_cols, vc->vc_rows, + vc->vc_saved_uni_lines, cols, 0, rows); vc_uniscr_free(vc->vc_saved_uni_lines); - else - vc_uniscr_set(vc, vc->vc_saved_uni_lines); + vc->vc_saved_uni_lines = new_uniscr; + } + vc_uniscr_set(vc, vc->vc_saved_uni_lines); vc->vc_saved_uni_lines = NULL; restore_cur(vc); /* Update the entire screen */ From ea31be8a2c8c99eac198f3b7f2dc770111f2b182 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 30 Mar 2026 18:22:20 +0200 Subject: [PATCH 417/712] ALSA: hda/realtek: Add quirk for Samsung Book2 Pro 360 (NP950QED) There is another Book2 Pro model (NP950QED) that seems equipped with the same speaker module as the non-360 model, which requires ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS quirk. Reported-by: Throw Link: https://patch.msgid.link/20260330162249.147665-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c14046e09aa4..766d7bb2d4f5 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7409,6 +7409,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc188, "Samsung Galaxy Book Flex (NT950QCT-A38A)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc189, "Samsung Galaxy Book Flex (NT950QCG-X716)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc18a, "Samsung Galaxy Book Ion (NP930XCJ-K01US)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc1ac, "Samsung Galaxy Book2 Pro 360 (NP950QED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), SND_PCI_QUIRK(0x144d, 0xc1a3, "Samsung Galaxy Book Pro (NP935XDB-KC1SE)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc1a4, "Samsung Galaxy Book Pro 360 (NT935QBD)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP), From 2e8b1a1d12ae3338efeb1c3de3eb4e9324b87a28 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Mon, 30 Mar 2026 11:12:07 +0200 Subject: [PATCH 418/712] rtla: Fix build without libbpf header rtla supports building without libbpf. However, BPF actions patchset [1] adds an include of bpf/libbpf.h into timerlat_bpf.h, which breaks build on systems that don't have libbpf headers installed. This is a leftover from a draft version of the patchset where timerlat_bpf_set_action() (which takes a struct bpf_program * argument) was defined in the header. timerlat_bpf.c already includes bpf/libbpf.h via timerlat.skel.h when libbpf is present. Remove the redundant include to fix build on systems without libbpf headers. [1] https://lore.kernel.org/linux-trace-kernel/20251126144205.331954-1-tglozar@redhat.com/T/ Cc: John Kacur Cc: Luis Goncalves Cc: Crystal Wood Cc: Costa Shulyupin Link: https://patch.msgid.link/20260330091207.16184-1-tglozar@redhat.com Reported-by: Steven Rostedt (Google) Closes: https://lore.kernel.org/linux-trace-kernel/20260329122202.65a8b575@robin/ Fixes: 8cd0f08ac72e ("rtla/timerlat: Support tail call from BPF program") Signed-off-by: Tomas Glozar Reviewed-by: Wander Lairson Costa Signed-off-by: Steven Rostedt (Google) --- tools/tracing/rtla/src/timerlat_bpf.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/tracing/rtla/src/timerlat_bpf.h b/tools/tracing/rtla/src/timerlat_bpf.h index 169abeaf4363..f7c5675737fe 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.h +++ b/tools/tracing/rtla/src/timerlat_bpf.h @@ -12,7 +12,6 @@ enum summary_field { }; #ifndef __bpf__ -#include #ifdef HAVE_BPF_SKEL int timerlat_bpf_init(struct timerlat_params *params); int timerlat_bpf_attach(void); From b9eff9732cb0f86a68c9d1592a98ceab47c01e95 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 27 Mar 2026 02:43:54 +0000 Subject: [PATCH 419/712] ASoC: soc-core: call missing INIT_LIST_HEAD() for card_aux_list Component has "card_aux_list" which is added/deled in bind/unbind aux dev function (A), and used in for_each_card_auxs() loop (B). static void soc_unbind_aux_dev(...) { ... for_each_card_auxs_safe(...) { ... (A) list_del(&component->card_aux_list); } ^^^^^^^^^^^^^ } static int soc_bind_aux_dev(...) { ... for_each_card_pre_auxs(...) { ... (A) list_add(&component->card_aux_list, ...); } ^^^^^^^^^^^^^ ... } #define for_each_card_auxs(card, component) \ (B) list_for_each_entry(component, ..., card_aux_list) ^^^^^^^^^^^^^ But it has been used without calling INIT_LIST_HEAD(). > git grep card_aux_list sound/soc sound/soc/soc-core.c: list_del(&component->card_aux_list); sound/soc/soc-core.c: list_add(&component->card_aux_list, ...); call missing INIT_LIST_HEAD() for it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87341mxa8l.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 573693e21780..ff6eb6bfc63b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2859,6 +2859,7 @@ int snd_soc_component_initialize(struct snd_soc_component *component, INIT_LIST_HEAD(&component->dobj_list); INIT_LIST_HEAD(&component->card_list); INIT_LIST_HEAD(&component->list); + INIT_LIST_HEAD(&component->card_aux_list); mutex_init(&component->io_mutex); if (!component->name) { From ca34ee6d0307a0b4e52c870dfc1bb8a3c3eb956e Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Mon, 30 Mar 2026 15:56:40 +0000 Subject: [PATCH 420/712] hwmon: (tps53679) Fix device ID comparison and printing in tps53676_identify() tps53676_identify() uses strncmp() to compare the device ID buffer against a byte sequence containing embedded non-printable bytes (\x53\x67\x60). strncmp() is semantically wrong for binary data comparison; use memcmp() instead. Additionally, the buffer from i2c_smbus_read_block_data() is not NUL-terminated, so printing it with "%s" in the error path is undefined behavior and may read past the buffer. Use "%*ph" to hex-dump the actual bytes returned. Per the datasheet, the expected device ID is the 6-byte sequence 54 49 53 67 60 00 ("TI\x53\x67\x60\x00"), so compare all 6 bytes including the trailing NUL. Fixes: cb3d37b59012 ("hwmon: (pmbus/tps53679) Add support for TI TPS53676") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260330155618.77403-1-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/tps53679.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 3bca543817a6..249974c13aa3 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -175,8 +175,8 @@ static int tps53676_identify(struct i2c_client *client, ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); if (ret < 0) return ret; - if (strncmp("TI\x53\x67\x60", buf, 5)) { - dev_err(&client->dev, "Unexpected device ID: %s\n", buf); + if (ret != 6 || memcmp(buf, "TI\x53\x67\x60\x00", 6)) { + dev_err(&client->dev, "Unexpected device ID: %*ph\n", ret, buf); return -ENODEV; } From b0dc7e7c56573e7a52080f25f3179a45f3dd7e6f Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 22 Mar 2026 22:28:45 +0800 Subject: [PATCH 421/712] spi: amlogic: spifc-a4: unregister ECC engine on probe failure and remove() callback aml_sfc_probe() registers the on-host NAND ECC engine, but teardown was missing from both probe unwind and remove-time cleanup. Add a devm cleanup action after successful registration so nand_ecc_unregister_on_host_hw_engine() runs automatically on probe failures and during device removal. Fixes: 4670db6f32e9 ("spi: amlogic: add driver for Amlogic SPI Flash Controller") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260322-spifc-a4-v1-1-2dc5ebcbe0a9@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-amlogic-spifc-a4.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/spi/spi-amlogic-spifc-a4.c b/drivers/spi/spi-amlogic-spifc-a4.c index 1aabafa36e48..16346f50c7d9 100644 --- a/drivers/spi/spi-amlogic-spifc-a4.c +++ b/drivers/spi/spi-amlogic-spifc-a4.c @@ -1066,6 +1066,13 @@ static const struct nand_ecc_engine_ops aml_sfc_ecc_engine_ops = { .finish_io_req = aml_sfc_ecc_finish_io_req, }; +static void aml_sfc_unregister_ecc_engine(void *data) +{ + struct nand_ecc_engine *eng = data; + + nand_ecc_unregister_on_host_hw_engine(eng); +} + static int aml_sfc_clk_init(struct aml_sfc *sfc) { sfc->gate_clk = devm_clk_get_enabled(sfc->dev, "gate"); @@ -1149,6 +1156,11 @@ static int aml_sfc_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "failed to register Aml host ecc engine.\n"); + ret = devm_add_action_or_reset(dev, aml_sfc_unregister_ecc_engine, + &sfc->ecc_eng); + if (ret) + return dev_err_probe(dev, ret, "failed to add ECC unregister action\n"); + ret = of_property_read_u32(np, "amlogic,rx-adj", &val); if (!ret) sfc->rx_adj = val; From de1ef4ffd70e1d15f0bf584fd22b1f28cbd5e2ec Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 24 Mar 2026 17:39:02 +0800 Subject: [PATCH 422/712] drm/amdgpu: validate doorbell_offset in user queue creation amdgpu_userq_get_doorbell_index() passes the user-provided doorbell_offset to amdgpu_doorbell_index_on_bar() without bounds checking. An arbitrarily large doorbell_offset can cause the calculated doorbell index to fall outside the allocated doorbell BO, potentially corrupting kernel doorbell space. Validate that doorbell_offset falls within the doorbell BO before computing the BAR index, using u64 arithmetic to prevent overflow. Fixes: f09c1e6077ab ("drm/amdgpu: generate doorbell index for userqueue") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 7f64b783954a..0d8f6bfc8d1d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -600,6 +600,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, goto unpin_bo; } + /* Validate doorbell_offset is within the doorbell BO */ + if ((u64)db_info->doorbell_offset * db_size + db_size > + amdgpu_bo_size(db_obj->obj)) { + r = -EINVAL; + goto unpin_bo; + } + index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, db_info->doorbell_offset, db_size); drm_dbg_driver(adev_to_drm(uq_mgr->adev), From de95eda05f193e051bc7689805f152d075157bd0 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Tue, 24 Mar 2026 20:28:54 +0530 Subject: [PATCH 423/712] drm/amdgpu/userq: amdgpu_userq_vm_validate does not need userq mutex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_userq_vm_validate function does not need userq_mutex and exec lock is good enough to locking all bos and updating the eviction fence. Also since we only need userq_mutex for amdgpu_userq_restore_all so move the locks in the function itself. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 0d8f6bfc8d1d..2a463f5644ac 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1004,6 +1004,7 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) unsigned long queue_id; int ret = 0, r; + mutex_lock(&uq_mgr->userq_mutex); /* Resume all the queues for this process */ xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { @@ -1019,6 +1020,7 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) ret = r; } + mutex_unlock(&uq_mgr->userq_mutex); if (ret) drm_file_err(uq_mgr->file, "Failed to map all the queues\n"); @@ -1222,23 +1224,21 @@ static void amdgpu_userq_restore_worker(struct work_struct *work) struct dma_fence *ev_fence; int ret; - mutex_lock(&uq_mgr->userq_mutex); ev_fence = amdgpu_evf_mgr_get_fence(&fpriv->evf_mgr); if (!dma_fence_is_signaled(ev_fence)) - goto unlock; + goto put_fence; ret = amdgpu_userq_vm_validate(uq_mgr); if (ret) { drm_file_err(uq_mgr->file, "Failed to validate BOs to restore\n"); - goto unlock; + goto put_fence; } ret = amdgpu_userq_restore_all(uq_mgr); if (ret) drm_file_err(uq_mgr->file, "Failed to restore all queues\n"); -unlock: - mutex_unlock(&uq_mgr->userq_mutex); +put_fence: dma_fence_put(ev_fence); } From 557fa5a453c9ccb49a22f30a7ad0545573d434b7 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Tue, 24 Mar 2026 15:28:59 +0800 Subject: [PATCH 424/712] drm/amdgpu: guard atom_context in devcoredump VBIOS dump During GPU reset coredump generation, amdgpu_devcoredump_fw_info() unconditionally dereferences adev->mode_info.atom_context to print VBIOS fields. On reset/teardown paths this pointer can be NULL, causing a kernel page fault from the deferred coredump workqueue. Fix by checking ctx before printing VBIOS fields: if ctx is valid, print full VBIOS information as before; This prevents NULL-dereference crashes while preserving coredump output. Observed page fault log: [ 667.933329] RIP: 0010:amdgpu_devcoredump_format+0x780/0xc00 [amdgpu] [ 667.941517] amdgpu 0002:01:00.0: Dumping IP State [ 667.949660] Code: 8d 57 74 48 c7 c6 01 65 9f c2 48 8d 7d 98 e8 97 96 7a ff 49 8d 97 b4 00 00 00 48 c7 c6 18 65 9f c2 48 8d 7d 98 e8 80 96 7a ff <41> 8b 97 f4 00 00 00 48 c7 c6 2f 65 9f c2 48 8d 7d 98 e8 69 96 7a [ 667.949666] RSP: 0018:ffffc9002302bd50 EFLAGS: 00010246 [ 667.949673] RAX: 0000000000000000 RBX: ffff888110600000 RCX: 0000000000000000 [ 667.949676] RDX: 000000000000a9b5 RSI: 0000000000000405 RDI: 000000000000a999 [ 667.949680] RBP: ffffc9002302be00 R08: ffffffffc09c3084 R09: ffffffffc09c3085 [ 667.949684] R10: 0000000000000000 R11: 0000000000000004 R12: 00000000000048e0 [ 667.993908] amdgpu 0002:01:00.0: Dumping IP State Completed [ 667.994229] R13: 0000000000000025 R14: 000000000000000c R15: 0000000000000000 [ 667.994233] FS: 0000000000000000(0000) GS:ffff88c44c2c9000(0000) knlGS:0000000000000000 [ 668.000076] amdgpu 0002:01:00.0: [drm] AMDGPU device coredump file has been created [ 668.008025] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 668.008030] CR2: 00000000000000f4 CR3: 000000011195f001 CR4: 0000000000770ef0 [ 668.008035] PKRU: 55555554 [ 668.008040] Call Trace: [ 668.008045] [ 668.016010] amdgpu 0002:01:00.0: [drm] Check your /sys/class/drm/card16/device/devcoredump/data [ 668.023967] ? srso_alias_return_thunk+0x5/0xfbef5 [ 668.023988] ? __pfx___drm_printfn_coredump+0x10/0x10 [drm] [ 668.031950] amdgpu 0003:01:00.0: Dumping IP State [ 668.038159] ? __pfx___drm_puts_coredump+0x10/0x10 [drm] [ 668.083017] amdgpu 0003:01:00.0: Dumping IP State Completed [ 668.083824] amdgpu_devcoredump_deferred_work+0x26/0xc0 [amdgpu] [ 668.086163] amdgpu 0003:01:00.0: [drm] AMDGPU device coredump file has been created [ 668.095863] process_scheduled_works+0xa6/0x420 [ 668.095880] worker_thread+0x12a/0x270 [ 668.101223] amdgpu 0003:01:00.0: [drm] Check your /sys/class/drm/card24/device/devcoredump/data [ 668.107441] kthread+0x10d/0x230 [ 668.107451] ? __pfx_worker_thread+0x10/0x10 [ 668.107458] ? __pfx_kthread+0x10/0x10 [ 668.112709] amdgpu 0000:01:00.0: ring vcn_unified_1 timeout, signaled seq=9, emitted seq=10 [ 668.118630] ret_from_fork+0x17c/0x1f0 [ 668.118640] ? __pfx_kthread+0x10/0x10 [ 668.118647] ret_from_fork_asm+0x1a/0x30 Reviewed-by: Lijo Lazar Suggested-by: Lijo Lazar Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 2b54a67437c2..28198f3a6e0d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -192,12 +192,16 @@ static void amdgpu_devcoredump_fw_info(struct amdgpu_device *adev, drm_printf(p, "VPE feature version: %u, fw version: 0x%08x\n", adev->vpe.feature_version, adev->vpe.fw_version); - drm_printf(p, "\nVBIOS Information\n"); - drm_printf(p, "vbios name : %s\n", ctx->name); - drm_printf(p, "vbios pn : %s\n", ctx->vbios_pn); - drm_printf(p, "vbios version : %d\n", ctx->version); - drm_printf(p, "vbios ver_str : %s\n", ctx->vbios_ver_str); - drm_printf(p, "vbios date : %s\n", ctx->date); + if (adev->bios) { + drm_printf(p, "\nVBIOS Information\n"); + drm_printf(p, "vbios name : %s\n", ctx->name); + drm_printf(p, "vbios pn : %s\n", ctx->vbios_pn); + drm_printf(p, "vbios version : %d\n", ctx->version); + drm_printf(p, "vbios ver_str : %s\n", ctx->vbios_ver_str); + drm_printf(p, "vbios date : %s\n", ctx->date); + }else { + drm_printf(p, "\nVBIOS Information: NA\n"); + } } static ssize_t From bf89091035e38baf0bba277450f54904e134f113 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Tue, 24 Mar 2026 17:31:43 +0800 Subject: [PATCH 425/712] drm/amdgpu: flush coredump work before HW teardown In amdgpu_device_fini_hw(), deferred coredump formatting work may still be pending when hardware and IP components are being torn down. Since the work may access device registers and memory that will be freed or powered off, it must be completed before proceeding. Add a flush_work() call for adev->coredump_work, guarded by CONFIG_DEV_COREDUMP, to ensure any pending coredump work finishes before the device enters the early IP fini stage. This avoids potential use-after-free or accessing hardware resources that are no longer available. Reviewed-by: Lijo Lazar Suggested-by: Lijo Lazar Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 9 +++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 1 + 3 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 28198f3a6e0d..fddf4e1252bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -35,6 +35,9 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, void amdgpu_coredump_init(struct amdgpu_device *adev) { } +void amdgpu_coredump_fini(struct amdgpu_device *adev) +{ +} #else #define AMDGPU_CORE_DUMP_SIZE_MAX (256 * 1024 * 1024) @@ -440,4 +443,10 @@ void amdgpu_coredump_init(struct amdgpu_device *adev) { INIT_WORK(&adev->coredump_work, amdgpu_devcoredump_deferred_work); } + +void amdgpu_coredump_fini(struct amdgpu_device *adev) +{ + /* Finish deferred coredump formatting before HW/IP teardown. */ + flush_work(&adev->coredump_work); +} #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h index b3582d0b4ca4..f8f2f4df129b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h @@ -50,4 +50,5 @@ struct amdgpu_coredump_info { void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, bool vram_lost, struct amdgpu_job *job); void amdgpu_coredump_init(struct amdgpu_device *adev); +void amdgpu_coredump_fini(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index a7038f039b10..9c936519bb2b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4225,6 +4225,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev) if (pci_dev_is_disconnected(adev->pdev)) amdgpu_amdkfd_device_fini_sw(adev); + amdgpu_coredump_fini(adev); amdgpu_device_ip_fini_early(adev); amdgpu_irq_fini_hw(adev); From ea56aa2625708eaf96f310032391ff37746310ef Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Mon, 23 Mar 2026 16:07:02 +0800 Subject: [PATCH 426/712] drm/amdgpu: fix the idr allocation flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the IDR allocation flags by using atomic GFP flags in non‑sleepable contexts to avoid the __might_sleep() complaint. 268.290239] [drm] Initialized amdgpu 3.64.0 for 0000:03:00.0 on minor 0 [ 268.294900] BUG: sleeping function called from invalid context at ./include/linux/sched/mm.h:323 [ 268.295355] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1744, name: modprobe [ 268.295705] preempt_count: 1, expected: 0 [ 268.295886] RCU nest depth: 0, expected: 0 [ 268.296072] 2 locks held by modprobe/1744: [ 268.296077] #0: ffff8c3a44abd1b8 (&dev->mutex){....}-{4:4}, at: __driver_attach+0xe4/0x210 [ 268.296100] #1: ffffffffc1a6ea78 (amdgpu_pasid_idr_lock){+.+.}-{3:3}, at: amdgpu_pasid_alloc+0x26/0xe0 [amdgpu] [ 268.296494] CPU: 12 UID: 0 PID: 1744 Comm: modprobe Tainted: G U OE 6.19.0-custom #16 PREEMPT(voluntary) [ 268.296498] Tainted: [U]=USER, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE [ 268.296499] Hardware name: AMD Majolica-RN/Majolica-RN, BIOS RMJ1009A 06/13/2021 [ 268.296501] Call Trace: Fixes: 8f1de51f49be ("drm/amdgpu: prevent immediate PASID reuse case") Signed-off-by: Prike Liang Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index d88523568b62..569c5a89ff10 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -68,8 +68,11 @@ int amdgpu_pasid_alloc(unsigned int bits) return -EINVAL; spin_lock(&amdgpu_pasid_idr_lock); + /* TODO: Need to replace the idr with an xarry, and then + * handle the internal locking with ATOMIC safe paths. + */ pasid = idr_alloc_cyclic(&amdgpu_pasid_idr, NULL, 1, - 1U << bits, GFP_KERNEL); + 1U << bits, GFP_ATOMIC); spin_unlock(&amdgpu_pasid_idr_lock); if (pasid >= 0) From 3cfe23b6b04515b07b61c48f8b769c13d5439c9b Mon Sep 17 00:00:00 2001 From: Asad Kamal Date: Wed, 25 Mar 2026 17:20:41 +0800 Subject: [PATCH 427/712] drm/amd/pm: Use str_enabled_disabled in amdgpu_pm sysfs Coccinelle flags hand-rolled "enabled"/"disabled" strings; use the shared str_enabled_disabled() helper from string_choices.h for npm_status and thermal throttling logging sysfs text. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603251434.zIN2QYWn-lkp@intel.com/ Signed-off-by: Asad Kamal Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index dbf3ae2f5e13..62b0b1ef0d10 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #define MAX_NUM_OF_FEATURES_PER_SUBSET 8 @@ -1592,7 +1593,7 @@ static ssize_t amdgpu_get_thermal_throttling_logging(struct device *dev, return sysfs_emit(buf, "%s: thermal throttling logging %s, with interval %d seconds\n", adev_to_drm(adev)->unique, - atomic_read(&adev->throttling_logging_enabled) ? "enabled" : "disabled", + str_enabled_disabled(atomic_read(&adev->throttling_logging_enabled)), adev->throttling_logging_rs.interval / HZ + 1); } @@ -2235,7 +2236,7 @@ static ssize_t amdgpu_show_npm_status(struct device *dev, if (r) return r; - return sysfs_emit(buf, "%s\n", npower ? "enabled" : "disabled"); + return sysfs_emit(buf, "%s\n", str_enabled_disabled(npower)); } /** From b01cd158a2f5230b137396c5f8cda3fc780abbc2 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:35 +0530 Subject: [PATCH 428/712] drm/amdkfd: Align expected_queue_size to PAGE_SIZE The AQL queue size can be 4K, but the minimum buffer object (BO) allocation size is PAGE_SIZE. On systems with a page size larger than 4K, the expected queue size does not match the allocated BO size, causing queue creation to fail. Align the expected queue size to PAGE_SIZE so that it matches the allocated BO size and allows queue creation to succeed. Reviewed-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index bbe869ceae3f..e1a922bb2ab7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -249,10 +249,10 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope topo_dev->node_props.gfx_target_version < 90000) /* metadata_queue_size not supported on GFX7/GFX8 */ expected_queue_size = - properties->queue_size / 2; + PAGE_ALIGN(properties->queue_size / 2); else expected_queue_size = - properties->queue_size + properties->metadata_queue_size; + PAGE_ALIGN(properties->queue_size + properties->metadata_queue_size); vm = drm_priv_to_vm(pdd->drm_priv); err = amdgpu_bo_reserve(vm->root.bo, false); From 998d6781410de1c4b787fdbf6c56e851ea7fa553 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:38 +0530 Subject: [PATCH 429/712] drm/amd: Fix MQD and control stack alignment for non-4K MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For gfxV9, due to a hardware bug ("based on the comments in the code here [1]"), the control stack of a user-mode compute queue must be allocated immediately after the page boundary of its regular MQD buffer. To handle this, we allocate an enlarged MQD buffer where the first page is used as the MQD and the remaining pages store the control stack. Although these regions share the same BO, they require different memory types: the MQD must be UC (uncached), while the control stack must be NC (non-coherent), matching the behavior when the control stack is allocated in user space. This logic works correctly on systems where the CPU page size matches the GPU page size (4K). However, the current implementation aligns both the MQD and the control stack to the CPU PAGE_SIZE. On systems with a larger CPU page size, the entire first CPU page is marked UC—even though that page may contain multiple GPU pages. The GPU treats the second 4K GPU page inside that CPU page as part of the control stack, but it is incorrectly mapped as UC. This patch fixes the issue by aligning both the MQD and control stack sizes to the GPU page size (4K). The first 4K page is correctly marked as UC for the MQD, and the remaining GPU pages are marked NC for the control stack. This ensures proper memory type assignment on systems with larger CPU page sizes. [1]: https://elixir.bootlin.com/linux/v6.18/source/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c#L118 Acked-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c | 44 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 16 ++----- .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 23 ++++++---- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index e2d32c29668a..bc772ca3dab7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -403,6 +403,50 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, drm_dev_exit(idx); } +/** + * amdgpu_gart_map_gfx9_mqd - map mqd and ctrl_stack dma_addresses into GART entries + * + * @adev: amdgpu_device pointer + * @offset: offset into the GPU's gart aperture + * @pages: number of pages to bind + * @dma_addr: DMA addresses of pages + * @flags: page table entry flags + * + * Map the MQD and control stack addresses into GART entries with the correct + * memory types on gfxv9. The MQD occupies the first 4KB and is followed by + * the control stack. The MQD uses UC (uncached) memory, while the control stack + * uses NC (non-coherent) memory. + */ +void amdgpu_gart_map_gfx9_mqd(struct amdgpu_device *adev, uint64_t offset, + int pages, dma_addr_t *dma_addr, uint64_t flags) +{ + uint64_t page_base; + unsigned int i, j, t; + int idx; + uint64_t ctrl_flags = AMDGPU_PTE_MTYPE_VG10(flags, AMDGPU_MTYPE_NC); + void *dst; + + if (!adev->gart.ptr) + return; + + if (!drm_dev_enter(adev_to_drm(adev), &idx)) + return; + + t = offset / AMDGPU_GPU_PAGE_SIZE; + dst = adev->gart.ptr; + for (i = 0; i < pages; i++) { + page_base = dma_addr[i]; + for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) { + if ((i == 0) && (j == 0)) + amdgpu_gmc_set_pte_pde(adev, dst, t, page_base, flags); + else + amdgpu_gmc_set_pte_pde(adev, dst, t, page_base, ctrl_flags); + page_base += AMDGPU_GPU_PAGE_SIZE; + } + } + drm_dev_exit(idx); +} + /** * amdgpu_gart_bind - bind pages into the gart page table * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h index d3118275ddae..6ebd2da32ea6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h @@ -62,6 +62,8 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset, void amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset, int pages, dma_addr_t *dma_addr, uint64_t flags, void *dst); +void amdgpu_gart_map_gfx9_mqd(struct amdgpu_device *adev, uint64_t offset, + int pages, dma_addr_t *dma_addr, uint64_t flags); void amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset, int pages, dma_addr_t *dma_addr, uint64_t flags); void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index b4ab309bf08a..4e0469938e90 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -854,25 +854,15 @@ static void amdgpu_ttm_gart_bind_gfx9_mqd(struct amdgpu_device *adev, int num_xcc = max(1U, adev->gfx.num_xcc_per_xcp); uint64_t page_idx, pages_per_xcc; int i; - uint64_t ctrl_flags = AMDGPU_PTE_MTYPE_VG10(flags, AMDGPU_MTYPE_NC); pages_per_xcc = total_pages; do_div(pages_per_xcc, num_xcc); for (i = 0, page_idx = 0; i < num_xcc; i++, page_idx += pages_per_xcc) { - /* MQD page: use default flags */ - amdgpu_gart_bind(adev, + amdgpu_gart_map_gfx9_mqd(adev, gtt->offset + (page_idx << PAGE_SHIFT), - 1, >t->ttm.dma_address[page_idx], flags); - /* - * Ctrl pages - modify the memory type to NC (ctrl_flags) from - * the second page of the BO onward. - */ - amdgpu_gart_bind(adev, - gtt->offset + ((page_idx + 1) << PAGE_SHIFT), - pages_per_xcc - 1, - >t->ttm.dma_address[page_idx + 1], - ctrl_flags); + pages_per_xcc, >t->ttm.dma_address[page_idx], + flags); } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index 979ae94ac966..e8f97de9d6e4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -42,9 +42,16 @@ static uint64_t mqd_stride_v9(struct mqd_manager *mm, struct queue_properties *q) { if (mm->dev->kfd->cwsr_enabled && - q->type == KFD_QUEUE_TYPE_COMPUTE) - return ALIGN(q->ctl_stack_size, PAGE_SIZE) + - ALIGN(sizeof(struct v9_mqd), PAGE_SIZE); + q->type == KFD_QUEUE_TYPE_COMPUTE) { + + /* On gfxv9, the MQD resides in the first 4K page, + * followed by the control stack. Align both to + * AMDGPU_GPU_PAGE_SIZE to maintain the required 4K boundary. + */ + + return ALIGN(ALIGN(q->ctl_stack_size, AMDGPU_GPU_PAGE_SIZE) + + ALIGN(sizeof(struct v9_mqd), AMDGPU_GPU_PAGE_SIZE), PAGE_SIZE); + } return mm->mqd_size; } @@ -150,8 +157,8 @@ static struct kfd_mem_obj *allocate_mqd(struct mqd_manager *mm, if (!mqd_mem_obj) return NULL; retval = amdgpu_amdkfd_alloc_kernel_mem(node->adev, - (ALIGN(q->ctl_stack_size, PAGE_SIZE) + - ALIGN(sizeof(struct v9_mqd), PAGE_SIZE)) * + (ALIGN(ALIGN(q->ctl_stack_size, AMDGPU_GPU_PAGE_SIZE) + + ALIGN(sizeof(struct v9_mqd), AMDGPU_GPU_PAGE_SIZE), PAGE_SIZE)) * NUM_XCC(node->xcc_mask), mqd_on_vram(node->adev) ? AMDGPU_GEM_DOMAIN_VRAM : AMDGPU_GEM_DOMAIN_GTT, @@ -359,7 +366,7 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, struct kfd_context_save_area_header header; /* Control stack is located one page after MQD. */ - void *mqd_ctl_stack = (void *)((uintptr_t)mqd + PAGE_SIZE); + void *mqd_ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); @@ -399,7 +406,7 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi { struct v9_mqd *m; /* Control stack is located one page after MQD. */ - void *ctl_stack = (void *)((uintptr_t)mqd + PAGE_SIZE); + void *ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); @@ -445,7 +452,7 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd, *gart_addr = addr; /* Control stack is located one page after MQD. */ - ctl_stack = (void *)((uintptr_t)*mqd + PAGE_SIZE); + ctl_stack = (void *)((uintptr_t)*mqd + AMDGPU_GPU_PAGE_SIZE); memcpy(ctl_stack, ctl_stack_src, ctl_stack_size); m->cp_hqd_pq_doorbell_control = From 27f5ff9e4a4150d7cf8b4085aedd3b77ddcc5d08 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Sat, 14 Mar 2026 23:33:53 +0800 Subject: [PATCH 430/712] drm/amdgpu/userq: fix memory leak in MQD creation error paths In mes_userq_mqd_create(), the memdup_user() allocations for IP-specific MQD structs are not freed when subsequent VA validation fails. The goto free_mqd label only cleans up the MQD BO object and userq_props. Fix by adding kfree() before each goto free_mqd on VA validation failure in the COMPUTE, GFX, and SDMA branches. Fixes: 9e46b8bb0539 ("drm/amdgpu: validate userq buffer virtual address and size") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Reviewed-by: Prike Liang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 8c74894254f7..faac21ee5739 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -324,8 +324,10 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va, 2048); - if (r) + if (r) { + kfree(compute_mqd); goto free_mqd; + } userq_props->eop_gpu_addr = compute_mqd->eop_va; userq_props->hqd_pipe_priority = AMDGPU_GFX_PIPE_PRIO_NORMAL; @@ -365,12 +367,16 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, shadow_info.shadow_size); - if (r) + if (r) { + kfree(mqd_gfx_v11); goto free_mqd; + } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va, shadow_info.csa_size); - if (r) + if (r) { + kfree(mqd_gfx_v11); goto free_mqd; + } kfree(mqd_gfx_v11); } else if (queue->queue_type == AMDGPU_HW_IP_DMA) { @@ -390,8 +396,10 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, } r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va, 32); - if (r) + if (r) { + kfree(mqd_sdma_v11); goto free_mqd; + } userq_props->csa_addr = mqd_sdma_v11->csa_va; kfree(mqd_sdma_v11); From 415cb193bb9736f0e830286c72a6fa8eb2a9cc5c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Mar 2026 14:18:55 -1000 Subject: [PATCH 431/712] sched_ext: Fix SCX_KICK_WAIT deadlock by deferring wait to balance callback SCX_KICK_WAIT busy-waits in kick_cpus_irq_workfn() using smp_cond_load_acquire() until the target CPU's kick_sync advances. Because the irq_work runs in hardirq context, the waiting CPU cannot reschedule and its own kick_sync never advances. If multiple CPUs form a wait cycle, all CPUs deadlock. Replace the busy-wait in kick_cpus_irq_workfn() with resched_curr() to force the CPU through do_pick_task_scx(), which queues a balance callback to perform the wait. The balance callback drops the rq lock and enables IRQs following the sched_core_balance() pattern, so the CPU can process IPIs while waiting. The local CPU's kick_sync is advanced on entry to do_pick_task_scx() and continuously during the wait, ensuring any CPU that starts waiting for us sees the advancement and cannot form cyclic dependencies. Fixes: 90e55164dad4 ("sched_ext: Implement SCX_KICK_WAIT") Cc: stable@vger.kernel.org # v6.12+ Reported-by: Christian Loehle Link: https://lore.kernel.org/r/20260316100249.1651641-1-christian.loehle@arm.com Signed-off-by: Tejun Heo Tested-by: Christian Loehle --- kernel/sched/ext.c | 95 ++++++++++++++++++++++++++++++++------------ kernel/sched/sched.h | 3 ++ 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 26a6ac2f8826..d5bdcdb3f700 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -2404,7 +2404,7 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, { struct scx_sched *sch = scx_root; - /* see kick_cpus_irq_workfn() */ + /* see kick_sync_wait_bal_cb() */ smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); update_curr_scx(rq); @@ -2447,6 +2447,48 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, switch_class(rq, next); } +static void kick_sync_wait_bal_cb(struct rq *rq) +{ + struct scx_kick_syncs __rcu *ks = __this_cpu_read(scx_kick_syncs); + unsigned long *ksyncs = rcu_dereference_sched(ks)->syncs; + bool waited; + s32 cpu; + + /* + * Drop rq lock and enable IRQs while waiting. IRQs must be enabled + * — a target CPU may be waiting for us to process an IPI (e.g. TLB + * flush) while we wait for its kick_sync to advance. + * + * Also, keep advancing our own kick_sync so that new kick_sync waits + * targeting us, which can start after we drop the lock, cannot form + * cyclic dependencies. + */ +retry: + waited = false; + for_each_cpu(cpu, rq->scx.cpus_to_sync) { + /* + * smp_load_acquire() pairs with smp_store_release() on + * kick_sync updates on the target CPUs. + */ + if (cpu == cpu_of(rq) || + smp_load_acquire(&cpu_rq(cpu)->scx.kick_sync) != ksyncs[cpu]) { + cpumask_clear_cpu(cpu, rq->scx.cpus_to_sync); + continue; + } + + raw_spin_rq_unlock_irq(rq); + while (READ_ONCE(cpu_rq(cpu)->scx.kick_sync) == ksyncs[cpu]) { + smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); + cpu_relax(); + } + raw_spin_rq_lock_irq(rq); + waited = true; + } + + if (waited) + goto retry; +} + static struct task_struct *first_local_task(struct rq *rq) { return list_first_entry_or_null(&rq->scx.local_dsq.list, @@ -2460,7 +2502,7 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) bool keep_prev; struct task_struct *p; - /* see kick_cpus_irq_workfn() */ + /* see kick_sync_wait_bal_cb() */ smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); rq_modified_begin(rq, &ext_sched_class); @@ -2470,6 +2512,17 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) rq_repin_lock(rq, rf); maybe_queue_balance_callback(rq); + /* + * Defer to a balance callback which can drop rq lock and enable + * IRQs. Waiting directly in the pick path would deadlock against + * CPUs sending us IPIs (e.g. TLB flushes) while we wait for them. + */ + if (unlikely(rq->scx.kick_sync_pending)) { + rq->scx.kick_sync_pending = false; + queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb, + kick_sync_wait_bal_cb); + } + /* * If any higher-priority sched class enqueued a runnable task on * this rq during balance_one(), abort and return RETRY_TASK, so @@ -4713,6 +4766,9 @@ static void scx_dump_state(struct scx_exit_info *ei, size_t dump_len) if (!cpumask_empty(rq->scx.cpus_to_wait)) dump_line(&ns, " cpus_to_wait : %*pb", cpumask_pr_args(rq->scx.cpus_to_wait)); + if (!cpumask_empty(rq->scx.cpus_to_sync)) + dump_line(&ns, " cpus_to_sync : %*pb", + cpumask_pr_args(rq->scx.cpus_to_sync)); used = seq_buf_used(&ns); if (SCX_HAS_OP(sch, dump_cpu)) { @@ -5610,11 +5666,11 @@ static bool kick_one_cpu(s32 cpu, struct rq *this_rq, unsigned long *ksyncs) if (cpumask_test_cpu(cpu, this_scx->cpus_to_wait)) { if (cur_class == &ext_sched_class) { + cpumask_set_cpu(cpu, this_scx->cpus_to_sync); ksyncs[cpu] = rq->scx.kick_sync; should_wait = true; - } else { - cpumask_clear_cpu(cpu, this_scx->cpus_to_wait); } + cpumask_clear_cpu(cpu, this_scx->cpus_to_wait); } resched_curr(rq); @@ -5669,27 +5725,15 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) cpumask_clear_cpu(cpu, this_scx->cpus_to_kick_if_idle); } - if (!should_wait) - return; - - for_each_cpu(cpu, this_scx->cpus_to_wait) { - unsigned long *wait_kick_sync = &cpu_rq(cpu)->scx.kick_sync; - - /* - * Busy-wait until the task running at the time of kicking is no - * longer running. This can be used to implement e.g. core - * scheduling. - * - * smp_cond_load_acquire() pairs with store_releases in - * pick_task_scx() and put_prev_task_scx(). The former breaks - * the wait if SCX's scheduling path is entered even if the same - * task is picked subsequently. The latter is necessary to break - * the wait when $cpu is taken by a higher sched class. - */ - if (cpu != cpu_of(this_rq)) - smp_cond_load_acquire(wait_kick_sync, VAL != ksyncs[cpu]); - - cpumask_clear_cpu(cpu, this_scx->cpus_to_wait); + /* + * Can't wait in hardirq — kick_sync can't advance, deadlocking if + * CPUs wait for each other. Defer to kick_sync_wait_bal_cb(). + */ + if (should_wait) { + raw_spin_rq_lock(this_rq); + this_scx->kick_sync_pending = true; + resched_curr(this_rq); + raw_spin_rq_unlock(this_rq); } } @@ -5794,6 +5838,7 @@ void __init init_sched_ext_class(void) BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_kick_if_idle, GFP_KERNEL, n)); BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_preempt, GFP_KERNEL, n)); BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_wait, GFP_KERNEL, n)); + BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_sync, GFP_KERNEL, n)); rq->scx.deferred_irq_work = IRQ_WORK_INIT_HARD(deferred_irq_workfn); rq->scx.kick_cpus_irq_work = IRQ_WORK_INIT_HARD(kick_cpus_irq_workfn); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 43bbf0693cca..1ef9ba480f51 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -805,9 +805,12 @@ struct scx_rq { cpumask_var_t cpus_to_kick_if_idle; cpumask_var_t cpus_to_preempt; cpumask_var_t cpus_to_wait; + cpumask_var_t cpus_to_sync; + bool kick_sync_pending; unsigned long kick_sync; local_t reenq_local_deferred; struct balance_callback deferred_bal_cb; + struct balance_callback kick_sync_bal_cb; struct irq_work deferred_irq_work; struct irq_work kick_cpus_irq_work; struct scx_dispatch_q bypass_dsq; From 5c36fd7fc6a84f36bebbef72fe29bec0682a6589 Mon Sep 17 00:00:00 2001 From: Gangliang Xie Date: Tue, 17 Mar 2026 15:31:48 +0800 Subject: [PATCH 432/712] drm/amdgpu: reset ras eeprom table when it is invalid reset ras eeprom table when it is invalid Signed-off-by: Gangliang Xie Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c index 44fba4b6aa92..cdf4909592d2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c @@ -1558,6 +1558,8 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control) unsigned char buf[RAS_TABLE_HEADER_SIZE] = { 0 }; struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr; struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); + int dev_var = adev->pdev->device & 0xF; + uint32_t vram_type = adev->gmc.vram_type; int res; if (amdgpu_ras_smu_eeprom_supported(adev)) @@ -1597,6 +1599,12 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control) return amdgpu_ras_eeprom_reset_table(control); } + if (!(adev->flags & AMD_IS_APU) && (dev_var == 0x5) && + (vram_type == AMDGPU_VRAM_TYPE_HBM3E) && + (hdr->version < RAS_TABLE_VER_V3)) { + return amdgpu_ras_eeprom_reset_table(control); + } + switch (hdr->version) { case RAS_TABLE_VER_V2_1: case RAS_TABLE_VER_V3: From 090d34f0f0285124452373225bcc520a31e305e4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Mar 2026 14:18:56 -1000 Subject: [PATCH 433/712] selftests/sched_ext: Add cyclic SCX_KICK_WAIT stress test Add a test that creates a 3-CPU kick_wait cycle (A->B->C->A). A BPF scheduler kicks the next CPU in the ring with SCX_KICK_WAIT on every enqueue while userspace workers generate continuous scheduling churn via sched_yield(). Without the preceding fix, this hangs the machine within seconds. Signed-off-by: Tejun Heo Reviewed-by: Christian Loehle Tested-by: Christian Loehle --- tools/testing/selftests/sched_ext/Makefile | 1 + .../sched_ext/cyclic_kick_wait.bpf.c | 68 ++++++ .../selftests/sched_ext/cyclic_kick_wait.c | 194 ++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c create mode 100644 tools/testing/selftests/sched_ext/cyclic_kick_wait.c diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/selftests/sched_ext/Makefile index 006300ac6dff..1c9ca328cca1 100644 --- a/tools/testing/selftests/sched_ext/Makefile +++ b/tools/testing/selftests/sched_ext/Makefile @@ -188,6 +188,7 @@ auto-test-targets := \ rt_stall \ test_example \ total_bw \ + cyclic_kick_wait \ testcase-targets := $(addsuffix .o,$(addprefix $(SCXOBJ_DIR)/,$(auto-test-targets))) diff --git a/tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c b/tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c new file mode 100644 index 000000000000..cb34d3335917 --- /dev/null +++ b/tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Stress concurrent SCX_KICK_WAIT calls to reproduce wait-cycle deadlock. + * + * Three CPUs are designated from userspace. Every enqueue from one of the + * three CPUs kicks the next CPU in the ring with SCX_KICK_WAIT, creating a + * persistent A -> B -> C -> A wait cycle pressure. + */ +#include + +char _license[] SEC("license") = "GPL"; + +const volatile s32 test_cpu_a; +const volatile s32 test_cpu_b; +const volatile s32 test_cpu_c; + +u64 nr_enqueues; +u64 nr_wait_kicks; + +UEI_DEFINE(uei); + +static s32 target_cpu(s32 cpu) +{ + if (cpu == test_cpu_a) + return test_cpu_b; + if (cpu == test_cpu_b) + return test_cpu_c; + if (cpu == test_cpu_c) + return test_cpu_a; + return -1; +} + +void BPF_STRUCT_OPS(cyclic_kick_wait_enqueue, struct task_struct *p, + u64 enq_flags) +{ + s32 this_cpu = bpf_get_smp_processor_id(); + s32 tgt; + + __sync_fetch_and_add(&nr_enqueues, 1); + + if (p->flags & PF_KTHREAD) { + scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, SCX_SLICE_INF, + enq_flags | SCX_ENQ_PREEMPT); + return; + } + + scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags); + + tgt = target_cpu(this_cpu); + if (tgt < 0 || tgt == this_cpu) + return; + + __sync_fetch_and_add(&nr_wait_kicks, 1); + scx_bpf_kick_cpu(tgt, SCX_KICK_WAIT); +} + +void BPF_STRUCT_OPS(cyclic_kick_wait_exit, struct scx_exit_info *ei) +{ + UEI_RECORD(uei, ei); +} + +SEC(".struct_ops.link") +struct sched_ext_ops cyclic_kick_wait_ops = { + .enqueue = cyclic_kick_wait_enqueue, + .exit = cyclic_kick_wait_exit, + .name = "cyclic_kick_wait", + .timeout_ms = 1000U, +}; diff --git a/tools/testing/selftests/sched_ext/cyclic_kick_wait.c b/tools/testing/selftests/sched_ext/cyclic_kick_wait.c new file mode 100644 index 000000000000..c2e5aa9de715 --- /dev/null +++ b/tools/testing/selftests/sched_ext/cyclic_kick_wait.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Test SCX_KICK_WAIT forward progress under cyclic wait pressure. + * + * SCX_KICK_WAIT busy-waits until the target CPU enters the scheduling path. + * If multiple CPUs form a wait cycle (A waits for B, B waits for C, C waits + * for A), all CPUs deadlock unless the implementation breaks the cycle. + * + * This test creates that scenario: three CPUs are arranged in a ring. The BPF + * scheduler's ops.enqueue() kicks the next CPU in the ring with SCX_KICK_WAIT + * on every enqueue. Userspace pins 4 worker threads per CPU that loop calling + * sched_yield(), generating a steady stream of enqueues and thus sustained + * A->B->C->A kick_wait cycle pressure. The test passes if the system remains + * responsive for 5 seconds without the scheduler being killed by the watchdog. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scx_test.h" +#include "cyclic_kick_wait.bpf.skel.h" + +#define WORKERS_PER_CPU 4 +#define NR_TEST_CPUS 3 +#define NR_WORKERS (NR_TEST_CPUS * WORKERS_PER_CPU) + +struct worker_ctx { + pthread_t tid; + int cpu; + volatile bool stop; + volatile __u64 iters; + bool started; +}; + +static void *worker_fn(void *arg) +{ + struct worker_ctx *worker = arg; + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(worker->cpu, &mask); + + if (sched_setaffinity(0, sizeof(mask), &mask)) + return (void *)(uintptr_t)errno; + + while (!worker->stop) { + sched_yield(); + worker->iters++; + } + + return NULL; +} + +static int join_worker(struct worker_ctx *worker) +{ + void *ret; + struct timespec ts; + int err; + + if (!worker->started) + return 0; + + if (clock_gettime(CLOCK_REALTIME, &ts)) + return -errno; + + ts.tv_sec += 2; + err = pthread_timedjoin_np(worker->tid, &ret, &ts); + if (err == ETIMEDOUT) + pthread_detach(worker->tid); + if (err) + return -err; + + if ((uintptr_t)ret) + return -(int)(uintptr_t)ret; + + return 0; +} + +static enum scx_test_status setup(void **ctx) +{ + struct cyclic_kick_wait *skel; + + skel = cyclic_kick_wait__open(); + SCX_FAIL_IF(!skel, "Failed to open skel"); + SCX_ENUM_INIT(skel); + + *ctx = skel; + return SCX_TEST_PASS; +} + +static enum scx_test_status run(void *ctx) +{ + struct cyclic_kick_wait *skel = ctx; + struct worker_ctx workers[NR_WORKERS] = {}; + struct bpf_link *link = NULL; + enum scx_test_status status = SCX_TEST_PASS; + int test_cpus[NR_TEST_CPUS]; + int nr_cpus = 0; + cpu_set_t mask; + int ret, i; + + if (sched_getaffinity(0, sizeof(mask), &mask)) { + SCX_ERR("Failed to get affinity (%d)", errno); + return SCX_TEST_FAIL; + } + + for (i = 0; i < CPU_SETSIZE; i++) { + if (CPU_ISSET(i, &mask)) + test_cpus[nr_cpus++] = i; + if (nr_cpus == NR_TEST_CPUS) + break; + } + + if (nr_cpus < NR_TEST_CPUS) + return SCX_TEST_SKIP; + + skel->rodata->test_cpu_a = test_cpus[0]; + skel->rodata->test_cpu_b = test_cpus[1]; + skel->rodata->test_cpu_c = test_cpus[2]; + + if (cyclic_kick_wait__load(skel)) { + SCX_ERR("Failed to load skel"); + return SCX_TEST_FAIL; + } + + link = bpf_map__attach_struct_ops(skel->maps.cyclic_kick_wait_ops); + if (!link) { + SCX_ERR("Failed to attach scheduler"); + return SCX_TEST_FAIL; + } + + for (i = 0; i < NR_WORKERS; i++) + workers[i].cpu = test_cpus[i / WORKERS_PER_CPU]; + + for (i = 0; i < NR_WORKERS; i++) { + ret = pthread_create(&workers[i].tid, NULL, worker_fn, &workers[i]); + if (ret) { + SCX_ERR("Failed to create worker thread %d (%d)", i, ret); + status = SCX_TEST_FAIL; + goto out; + } + workers[i].started = true; + } + + sleep(5); + + if (skel->data->uei.kind != EXIT_KIND(SCX_EXIT_NONE)) { + SCX_ERR("Scheduler exited unexpectedly (kind=%llu code=%lld)", + (unsigned long long)skel->data->uei.kind, + (long long)skel->data->uei.exit_code); + status = SCX_TEST_FAIL; + } + +out: + for (i = 0; i < NR_WORKERS; i++) + workers[i].stop = true; + + for (i = 0; i < NR_WORKERS; i++) { + ret = join_worker(&workers[i]); + if (ret && status == SCX_TEST_PASS) { + SCX_ERR("Failed to join worker thread %d (%d)", i, ret); + status = SCX_TEST_FAIL; + } + } + + if (link) + bpf_link__destroy(link); + + return status; +} + +static void cleanup(void *ctx) +{ + struct cyclic_kick_wait *skel = ctx; + + cyclic_kick_wait__destroy(skel); +} + +struct scx_test cyclic_kick_wait = { + .name = "cyclic_kick_wait", + .description = "Verify SCX_KICK_WAIT forward progress under a 3-CPU wait cycle", + .setup = setup, + .run = run, + .cleanup = cleanup, +}; +REGISTER_SCX_TEST(&cyclic_kick_wait) From 4eaf5d2c31f9075171f5dfc3bed9b285a1810726 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Thu, 26 Mar 2026 13:39:05 +0530 Subject: [PATCH 434/712] drm/amdgpu/userq: Fix the code alignment for readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the code alignment for if condition and also provide a line space between multiline if condition and next statement. Signed-off-by: Sunil Khatri Acked-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 2a463f5644ac..366728ed03e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1461,17 +1461,19 @@ int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev, if (!adev->userq_halt_for_enforce_isolation) dev_warn(adev->dev, "userq scheduling already started!\n"); + adev->userq_halt_for_enforce_isolation = false; + xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { uqm = queue->userq_mgr; mutex_lock(&uqm->userq_mutex); - if (((queue->queue_type == AMDGPU_HW_IP_GFX) || - (queue->queue_type == AMDGPU_HW_IP_COMPUTE)) && - (queue->xcp_id == idx)) { + if (((queue->queue_type == AMDGPU_HW_IP_GFX) || + (queue->queue_type == AMDGPU_HW_IP_COMPUTE)) && + (queue->xcp_id == idx)) { r = amdgpu_userq_restore_helper(queue); if (r) ret = r; - } + } mutex_unlock(&uqm->userq_mutex); } From 31b8de5e55666f26ea7ece5f412b83eab3f56dbb Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Thu, 26 Mar 2026 17:51:28 +0530 Subject: [PATCH 435/712] drm/amdgpu: Change AMDGPU_VA_RESERVED_TRAP_SIZE to 64KB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, AMDGPU_VA_RESERVED_TRAP_SIZE is hardcoded to 8KB, while KFD_CWSR_TBA_TMA_SIZE is defined as 2 * PAGE_SIZE. On systems with 4K pages, both values match (8KB), so allocation and reserved space are consistent. However, on 64K page-size systems, KFD_CWSR_TBA_TMA_SIZE becomes 128KB, while the reserved trap area remains 8KB. This mismatch causes the kernel to crash when running rocminfo or rccl unit tests. Kernel attempted to read user page (2) - exploit attempt? (uid: 1001) BUG: Kernel NULL pointer dereference on read at 0x00000002 Faulting instruction address: 0xc0000000002c8a64 Oops: Kernel access of bad area, sig: 11 [#1] LE PAGE_SIZE=64K MMU=Radix SMP NR_CPUS=2048 NUMA pSeries CPU: 34 UID: 1001 PID: 9379 Comm: rocminfo Tainted: G E 6.19.0-rc4-amdgpu-00320-gf23176405700 #56 VOLUNTARY Tainted: [E]=UNSIGNED_MODULE Hardware name: IBM,9105-42A POWER10 (architected) 0x800200 0xf000006 of:IBM,FW1060.30 (ML1060_896) hv:phyp pSeries NIP: c0000000002c8a64 LR: c00000000125dbc8 CTR: c00000000125e730 REGS: c0000001e0957580 TRAP: 0300 Tainted: G E MSR: 8000000000009033 CR: 24008268 XER: 00000036 CFAR: c00000000125dbc4 DAR: 0000000000000002 DSISR: 40000000 IRQMASK: 1 GPR00: c00000000125d908 c0000001e0957820 c0000000016e8100 c00000013d814540 GPR04: 0000000000000002 c00000013d814550 0000000000000045 0000000000000000 GPR08: c00000013444d000 c00000013d814538 c00000013d814538 0000000084002268 GPR12: c00000000125e730 c000007e2ffd5f00 ffffffffffffffff 0000000000020000 GPR16: 0000000000000000 0000000000000002 c00000015f653000 0000000000000000 GPR20: c000000138662400 c00000013d814540 0000000000000000 c00000013d814500 GPR24: 0000000000000000 0000000000000002 c0000001e0957888 c0000001e0957878 GPR28: c00000013d814548 0000000000000000 c00000013d814540 c0000001e0957888 NIP [c0000000002c8a64] __mutex_add_waiter+0x24/0xc0 LR [c00000000125dbc8] __mutex_lock.constprop.0+0x318/0xd00 Call Trace: 0xc0000001e0957890 (unreliable) __mutex_lock.constprop.0+0x58/0xd00 amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu+0x6fc/0xb60 [amdgpu] kfd_process_alloc_gpuvm+0x54/0x1f0 [amdgpu] kfd_process_device_init_cwsr_dgpu+0xa4/0x1a0 [amdgpu] kfd_process_device_init_vm+0xd8/0x2e0 [amdgpu] kfd_ioctl_acquire_vm+0xd0/0x130 [amdgpu] kfd_ioctl+0x514/0x670 [amdgpu] sys_ioctl+0x134/0x180 system_call_exception+0x114/0x300 system_call_vectored_common+0x15c/0x2ec This patch changes AMDGPU_VA_RESERVED_TRAP_SIZE to 64 KB and KFD_CWSR_TBA_TMA_SIZE to the AMD GPU page size. This means we reserve 64 KB for the trap in the address space, but only allocate 8 KB within it. With this approach, the allocation size never exceeds the reserved area. Fixes: 34a1de0f7935 ("drm/amdkfd: Relocate TBA/TMA to opposite side of VM hole") Reviewed-by: Christian König Suggested-by: Felix Kuehling Suggested-by: Christian König Signed-off-by: Donet Tom Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 641fe91b4f03..3b32f41c3655 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -173,7 +173,7 @@ struct amdgpu_bo_vm; #define AMDGPU_VA_RESERVED_SEQ64_SIZE (2ULL << 20) #define AMDGPU_VA_RESERVED_SEQ64_START(adev) (AMDGPU_VA_RESERVED_CSA_START(adev) \ - AMDGPU_VA_RESERVED_SEQ64_SIZE) -#define AMDGPU_VA_RESERVED_TRAP_SIZE (2ULL << 12) +#define AMDGPU_VA_RESERVED_TRAP_SIZE (1ULL << 16) #define AMDGPU_VA_RESERVED_TRAP_START(adev) (AMDGPU_VA_RESERVED_SEQ64_START(adev) \ - AMDGPU_VA_RESERVED_TRAP_SIZE) #define AMDGPU_VA_RESERVED_BOTTOM (1ULL << 16) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 85f2bc3fbf85..fa025bea9b4f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -102,8 +102,8 @@ * The first chunk is the TBA used for the CWSR ISA code. The second * chunk is used as TMA for user-mode trap handler setup in daisy-chain mode. */ -#define KFD_CWSR_TBA_TMA_SIZE (PAGE_SIZE * 2) -#define KFD_CWSR_TMA_OFFSET (PAGE_SIZE + 2048) +#define KFD_CWSR_TBA_TMA_SIZE (AMDGPU_GPU_PAGE_SIZE * 2) +#define KFD_CWSR_TMA_OFFSET (AMDGPU_GPU_PAGE_SIZE + 2048) #define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE \ (KFD_MAX_NUM_OF_PROCESSES * \ From 622363757b2286dd2c2984b0d80255cbb35a0495 Mon Sep 17 00:00:00 2001 From: Jihed Chaibi Date: Tue, 24 Mar 2026 22:09:09 +0100 Subject: [PATCH 436/712] ASoC: ep93xx: Fix unchecked clk_prepare_enable() and add rollback on failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ep93xx_i2s_enable() calls clk_prepare_enable() on three clocks in sequence (mclk, sclk, lrclk) without checking the return value of any of them. If an intermediate enable fails, the clocks that were already enabled are never rolled back, leaking them until the next disable cycle — which may never come if the stream never started cleanly. Change ep93xx_i2s_enable() from void to int. Add error checking after each clk_prepare_enable() call and unwind already-enabled clocks on failure. Propagate the error through ep93xx_i2s_startup() and ep93xx_i2s_resume(), both of which already return int. Signed-off-by: Jihed Chaibi Fixes: f4ff6b56bc8a ("ASoC: cirrus: i2s: Prepare clock before using it") Link: https://patch.msgid.link/20260324210909.45494-1-jihed.chaibi.dev@gmail.com Signed-off-by: Mark Brown --- sound/soc/cirrus/ep93xx-i2s.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index cca01c03f048..5dba741594fa 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -91,16 +91,28 @@ static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info, return __raw_readl(info->regs + reg); } -static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) +static int ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) { unsigned base_reg; + int err; if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { /* Enable clocks */ - clk_prepare_enable(info->mclk); - clk_prepare_enable(info->sclk); - clk_prepare_enable(info->lrclk); + err = clk_prepare_enable(info->mclk); + if (err) + return err; + err = clk_prepare_enable(info->sclk); + if (err) { + clk_disable_unprepare(info->mclk); + return err; + } + err = clk_prepare_enable(info->lrclk); + if (err) { + clk_disable_unprepare(info->sclk); + clk_disable_unprepare(info->mclk); + return err; + } /* Enable i2s */ ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1); @@ -119,6 +131,8 @@ static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, EP93XX_I2S_TXCTRL_TXEMPTY_LVL | EP93XX_I2S_TXCTRL_TXUFIE); + + return 0; } static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) @@ -195,9 +209,7 @@ static int ep93xx_i2s_startup(struct snd_pcm_substream *substream, { struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); - ep93xx_i2s_enable(info, substream->stream); - - return 0; + return ep93xx_i2s_enable(info, substream->stream); } static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream, @@ -373,14 +385,16 @@ static int ep93xx_i2s_suspend(struct snd_soc_component *component) static int ep93xx_i2s_resume(struct snd_soc_component *component) { struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component); + int err; if (!snd_soc_component_active(component)) return 0; - ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); - ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE); + err = ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); + if (err) + return err; - return 0; + return ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE); } #else #define ep93xx_i2s_suspend NULL From 12fa1fd6dffff4eed15f1414eb7474127b2c5a24 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 24 Mar 2026 16:51:30 +0800 Subject: [PATCH 437/712] drm/amd/display: bios_parser: fix GPIO I2C line off-by-one get_gpio_i2c_info() computes the number of GPIO I2C assignment records present in the BIOS table and then uses bfI2C_LineMux as an array index into header->asGPIO_Info[]. The current check only rejects values strictly larger than the record count, so an index equal to count still falls through and reaches the fixed table one element past the end. Reject indices at or above the number of available records before using them as an array index. Signed-off-by: Pengpeng Hou Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/bios/bios_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index f947f82013c6..578ed0666438 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -1963,7 +1963,7 @@ static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, count = (le16_to_cpu(header->sHeader.usStructureSize) - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_I2C_ASSIGMENT); - if (count < record->sucI2cId.bfI2C_LineMux) + if (count <= record->sucI2cId.bfI2C_LineMux) return BP_RESULT_BADBIOSTABLE; /* get the GPIO_I2C_INFO */ From e6d5acad7112b5987afd1e583c84b14017c7ff4a Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Wed, 31 Dec 2025 15:19:22 -0500 Subject: [PATCH 438/712] drm/amd/display: Remove check for DC_DMCUB_ENABLE on DCN42 [why] DCN without DMCUB is not a supported configuration on DCN42. [how] Remove the DC_DMCUB_ENABLE fuse register check and remove the corresponding entries in the DCN42 DMUB register list. Reviewed-by: Nicholas Kazlauskas Signed-off-by: Gabe Teeger Signed-off-by: Matthew Stewart Signed-off-by: Roman Li Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dmub/src/dmub_dcn42.c | 8 +- .../gpu/drm/amd/display/dmub/src/dmub_dcn42.h | 136 +++++++++++++++++- 2 files changed, 137 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c index 7833a4fb7fbf..f687359b7d83 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c @@ -321,11 +321,9 @@ void dmub_dcn42_set_outbox1_rptr(struct dmub_srv *dmub, uint32_t rptr_offset) bool dmub_dcn42_is_supported(struct dmub_srv *dmub) { - uint32_t supported = 0; - - REG_GET(CC_DC_PIPE_DIS, DC_DMCUB_ENABLE, &supported); - - return supported; + // DCN without DMUB is not a supported configuration; safe to assume that it is always + // present. + return true; } union dmub_fw_boot_options dmub_dcn42_get_fw_boot_option(struct dmub_srv *dmub) diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.h index a49d88ab0455..c53f7691d1a8 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.h +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.h @@ -34,7 +34,101 @@ struct dmub_srv; /* DCN42 register definitions. */ #define DMUB_DCN42_REGS() \ - DMUB_DCN35_REGS() \ + DMUB_SR(DMCUB_CNTL) \ + DMUB_SR(DMCUB_CNTL2) \ + DMUB_SR(DMCUB_SEC_CNTL) \ + DMUB_SR(DMCUB_INBOX0_SIZE) \ + DMUB_SR(DMCUB_INBOX0_RPTR) \ + DMUB_SR(DMCUB_INBOX0_WPTR) \ + DMUB_SR(DMCUB_INBOX1_BASE_ADDRESS) \ + DMUB_SR(DMCUB_INBOX1_SIZE) \ + DMUB_SR(DMCUB_INBOX1_RPTR) \ + DMUB_SR(DMCUB_INBOX1_WPTR) \ + DMUB_SR(DMCUB_OUTBOX0_BASE_ADDRESS) \ + DMUB_SR(DMCUB_OUTBOX0_SIZE) \ + DMUB_SR(DMCUB_OUTBOX0_RPTR) \ + DMUB_SR(DMCUB_OUTBOX0_WPTR) \ + DMUB_SR(DMCUB_OUTBOX1_BASE_ADDRESS) \ + DMUB_SR(DMCUB_OUTBOX1_SIZE) \ + DMUB_SR(DMCUB_OUTBOX1_RPTR) \ + DMUB_SR(DMCUB_OUTBOX1_WPTR) \ + DMUB_SR(DMCUB_REGION3_CW0_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW1_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW2_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW3_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW4_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW5_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW6_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW7_OFFSET) \ + DMUB_SR(DMCUB_REGION3_CW0_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW1_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW2_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW3_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW4_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW5_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW6_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW7_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION3_CW0_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW1_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW2_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW3_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW4_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW5_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW6_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW7_BASE_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW0_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW1_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW2_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW3_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW4_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW5_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW6_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION3_CW7_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION4_OFFSET) \ + DMUB_SR(DMCUB_REGION4_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION4_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION5_OFFSET) \ + DMUB_SR(DMCUB_REGION5_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION5_TOP_ADDRESS) \ + DMUB_SR(DMCUB_REGION6_OFFSET) \ + DMUB_SR(DMCUB_REGION6_OFFSET_HIGH) \ + DMUB_SR(DMCUB_REGION6_TOP_ADDRESS) \ + DMUB_SR(DMCUB_SCRATCH0) \ + DMUB_SR(DMCUB_SCRATCH1) \ + DMUB_SR(DMCUB_SCRATCH2) \ + DMUB_SR(DMCUB_SCRATCH3) \ + DMUB_SR(DMCUB_SCRATCH4) \ + DMUB_SR(DMCUB_SCRATCH5) \ + DMUB_SR(DMCUB_SCRATCH6) \ + DMUB_SR(DMCUB_SCRATCH7) \ + DMUB_SR(DMCUB_SCRATCH8) \ + DMUB_SR(DMCUB_SCRATCH9) \ + DMUB_SR(DMCUB_SCRATCH10) \ + DMUB_SR(DMCUB_SCRATCH11) \ + DMUB_SR(DMCUB_SCRATCH12) \ + DMUB_SR(DMCUB_SCRATCH13) \ + DMUB_SR(DMCUB_SCRATCH14) \ + DMUB_SR(DMCUB_SCRATCH15) \ + DMUB_SR(DMCUB_SCRATCH16) \ + DMUB_SR(DMCUB_SCRATCH17) \ + DMUB_SR(DMCUB_SCRATCH18) \ + DMUB_SR(DMCUB_SCRATCH19) \ + DMUB_SR(DMCUB_SCRATCH20) \ + DMUB_SR(DMCUB_SCRATCH21) \ + DMUB_SR(DMCUB_GPINT_DATAIN0) \ + DMUB_SR(DMCUB_GPINT_DATAIN1) \ + DMUB_SR(DMCUB_GPINT_DATAOUT) \ + DMUB_SR(MMHUBBUB_SOFT_RESET) \ + DMUB_SR(DCN_VM_FB_LOCATION_BASE) \ + DMUB_SR(DCN_VM_FB_OFFSET) \ + DMUB_SR(DMCUB_TIMER_CURRENT) \ + DMUB_SR(DMCUB_INST_FETCH_FAULT_ADDR) \ + DMUB_SR(DMCUB_UNDEFINED_ADDRESS_FAULT_ADDR) \ + DMUB_SR(DMCUB_DATA_WRITE_FAULT_ADDR) \ + DMUB_SR(DMCUB_REGION3_TMR_AXI_SPACE) \ + DMUB_SR(DMCUB_INTERRUPT_ENABLE) \ + DMUB_SR(DMCUB_INTERRUPT_ACK) \ + DMUB_SR(DMU_CLK_CNTL) \ DMUB_SR(DMCUB_INTERRUPT_STATUS) \ DMUB_SR(DMCUB_REG_INBOX0_RDY) \ DMUB_SR(DMCUB_REG_INBOX0_MSG0) \ @@ -59,7 +153,45 @@ struct dmub_srv; DMUB_SR(HOST_INTERRUPT_CSR) #define DMUB_DCN42_FIELDS() \ - DMUB_DCN35_FIELDS() \ + DMUB_SF(DMCUB_CNTL, DMCUB_ENABLE) \ + DMUB_SF(DMCUB_CNTL, DMCUB_TRACEPORT_EN) \ + DMUB_SF(DMCUB_CNTL2, DMCUB_SOFT_RESET) \ + DMUB_SF(DMCUB_SEC_CNTL, DMCUB_SEC_RESET) \ + DMUB_SF(DMCUB_SEC_CNTL, DMCUB_MEM_UNIT_ID) \ + DMUB_SF(DMCUB_SEC_CNTL, DMCUB_SEC_RESET_STATUS) \ + DMUB_SF(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW1_TOP_ADDRESS, DMCUB_REGION3_CW1_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW1_TOP_ADDRESS, DMCUB_REGION3_CW1_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW2_TOP_ADDRESS, DMCUB_REGION3_CW2_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW2_TOP_ADDRESS, DMCUB_REGION3_CW2_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW3_TOP_ADDRESS, DMCUB_REGION3_CW3_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW3_TOP_ADDRESS, DMCUB_REGION3_CW3_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW4_TOP_ADDRESS, DMCUB_REGION3_CW4_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW4_TOP_ADDRESS, DMCUB_REGION3_CW4_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW5_TOP_ADDRESS, DMCUB_REGION3_CW5_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW5_TOP_ADDRESS, DMCUB_REGION3_CW5_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE) \ + DMUB_SF(DMCUB_REGION3_CW7_TOP_ADDRESS, DMCUB_REGION3_CW7_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION3_CW7_TOP_ADDRESS, DMCUB_REGION3_CW7_ENABLE) \ + DMUB_SF(DMCUB_REGION4_TOP_ADDRESS, DMCUB_REGION4_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION4_TOP_ADDRESS, DMCUB_REGION4_ENABLE) \ + DMUB_SF(DMCUB_REGION5_TOP_ADDRESS, DMCUB_REGION5_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION5_TOP_ADDRESS, DMCUB_REGION5_ENABLE) \ + DMUB_SF(DMCUB_REGION6_TOP_ADDRESS, DMCUB_REGION6_TOP_ADDRESS) \ + DMUB_SF(DMCUB_REGION6_TOP_ADDRESS, DMCUB_REGION6_ENABLE) \ + DMUB_SF(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET) \ + DMUB_SF(DCN_VM_FB_LOCATION_BASE, FB_BASE) \ + DMUB_SF(DCN_VM_FB_OFFSET, FB_OFFSET) \ + DMUB_SF(DMCUB_INBOX0_WPTR, DMCUB_INBOX0_WPTR) \ + DMUB_SF(DMCUB_REGION3_TMR_AXI_SPACE, DMCUB_REGION3_TMR_AXI_SPACE) \ + DMUB_SF(DMCUB_INTERRUPT_ENABLE, DMCUB_GPINT_IH_INT_EN) \ + DMUB_SF(DMCUB_INTERRUPT_ACK, DMCUB_GPINT_IH_INT_ACK) \ + DMUB_SF(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS) \ + DMUB_SF(DMU_CLK_CNTL, LONO_DISPCLK_GATE_DISABLE) \ + DMUB_SF(DMU_CLK_CNTL, LONO_SOCCLK_GATE_DISABLE) \ + DMUB_SF(DMU_CLK_CNTL, LONO_DMCUBCLK_GATE_DISABLE) \ DMUB_SF(DMCUB_INTERRUPT_STATUS, DMCUB_REG_OUTBOX0_RSP_INT_STAT) \ DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_ACK) \ DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_STAT) \ From 06bc20d26f38dcb12cc8d92fa599a6b7fc58d2a3 Mon Sep 17 00:00:00 2001 From: Roman Li Date: Fri, 20 Mar 2026 17:02:33 -0400 Subject: [PATCH 439/712] drm/amd/display: Enable Replay support for dcn42 Add DCN4.2 to the list that supports Panel Replay feature. Reviewed-by: Alex Hung Signed-off-by: Roman Li Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 205a7fab1037..3e87219e2aa4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -5581,6 +5581,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) case IP_VERSION(3, 5, 0): case IP_VERSION(3, 5, 1): case IP_VERSION(3, 6, 0): + case IP_VERSION(4, 2, 0): replay_feature_enabled = true; break; From b96150a70696582e1e49dcdefb2d101c109610d7 Mon Sep 17 00:00:00 2001 From: Wayne Lin Date: Thu, 5 Feb 2026 15:36:18 +0800 Subject: [PATCH 440/712] drm/amd/display: Should support p-state under dcn21 [Why] Under DCN21, observe flip_done timeout issue while running 3D benchmark under MPO case. Timeout is caused by driver fails validate_bandwidth() during atomic_commit_tail but passes atomic_check. Under further analysis, indicates the delta of atomic_check and atomic_commit_tail are dc->current_state->bw_ctx.dml.soc.sr_exit_time_us and dc->current_state->bw_ctx.dml.soc.sr_enter_plus_exit_time_us. We set validate_mode as DC_VALIDATE_MODE_ONLY while calling dc_validate_global_state() at atomic_check, but set mode as DC_VALIDATE_MODE_AND_PROGRAMMING during atomic_commit_tail. If dc_validate_mode set as DC_VALIDATE_MODE_ONLY, validate_bandwidth() will skip the wm and dlg calculation. During commit_tail, validate_bandwidth() is called with dc_validate_mode set as DC_VALIDATE_MODE_AND_PROGRAMMING and dc_state->bw_ctx.dml.soc.sr_exit_time_us might get modified after the wm_calculation and stored into dc->current_state. Which means dc->current_state->bw_ctx.dml.soc.sr_exit_time_us might not aligned with the one stored in dm_state->context. That causes duplicated dm_state->context not aligned with dc->current_state, and might have bandwidth validation pass in atomic_check and fail in commit_tail later. [How] When the issue occurs, it fails dml_get_voltage_level() with the condition dm_allow_self_refresh_and_mclk_switch but pass with the condition dm_allow_self_refresh. However, we should support p-state. So we should not pass validate_bandwidth by allowing self refresh only. Change the policy under DCN21. Reviewed-by: Nicholas Kazlauskas Signed-off-by: Wayne Lin Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.c | 2 +- .../dc/resource/dcn21/dcn21_resource.c | 30 +++++++++++-------- .../dc/resource/dcn21/dcn21_resource.h | 3 +- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c index 7aaf13bbd4e4..ae34982b1b1c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c @@ -2335,7 +2335,7 @@ bool dcn21_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, /*Unsafe due to current pipe merge and split logic*/ ASSERT(context != dc->current_state); - out = dcn21_fast_validate_bw(dc, context, pipes, &pipe_cnt, pipe_split_from, &vlevel, validate_mode); + out = dcn21_fast_validate_bw(dc, context, pipes, &pipe_cnt, pipe_split_from, &vlevel, validate_mode, false); if (pipe_cnt == 0) goto validate_out; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c index a1a529cabb93..f7f75604ef33 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c @@ -772,7 +772,8 @@ bool dcn21_fast_validate_bw(struct dc *dc, int *pipe_cnt_out, int *pipe_split_from, int *vlevel_out, - enum dc_validate_mode validate_mode) + enum dc_validate_mode validate_mode, + bool allow_self_refresh_only) { bool out = false; int split[MAX_PIPES] = { 0 }; @@ -803,18 +804,23 @@ bool dcn21_fast_validate_bw(struct dc *dc, vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt); if (vlevel > context->bw_ctx.dml.soc.num_states) { - /* - * If mode is unsupported or there's still no p-state support then - * fall back to favoring voltage. - * - * We don't actually support prefetch mode 2, so require that we - * at least support prefetch mode 1. - */ - context->bw_ctx.dml.soc.allow_dram_self_refresh_or_dram_clock_change_in_vblank = - dm_allow_self_refresh; - vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt); - if (vlevel > context->bw_ctx.dml.soc.num_states) + + if (allow_self_refresh_only) { + /* + * If mode is unsupported or there's still no p-state support then + * fall back to favoring voltage. + * + * We don't actually support prefetch mode 2, so require that we + * at least support prefetch mode 1. + */ + context->bw_ctx.dml.soc.allow_dram_self_refresh_or_dram_clock_change_in_vblank = + dm_allow_self_refresh; + vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt); + if (vlevel > context->bw_ctx.dml.soc.num_states) + goto validate_fail; + } else { goto validate_fail; + } } vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, vlevel, split, merge); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.h index a017fd9854d1..23d3a36872bb 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.h @@ -51,6 +51,7 @@ bool dcn21_fast_validate_bw( int *pipe_cnt_out, int *pipe_split_from, int *vlevel_out, - enum dc_validate_mode validate_mode); + enum dc_validate_mode validate_mode, + bool allow_self_refresh_only); #endif /* _DCN21_RESOURCE_H_ */ From a808615c2f6cf3d67b30414b6626f1d139029077 Mon Sep 17 00:00:00 2001 From: Gaghik Khachatrian Date: Wed, 11 Mar 2026 13:23:52 -0400 Subject: [PATCH 441/712] drm/amd/display: Fix Compiler Warning - unused func parameters Handling unused function parameter due to cause compiler warning Reviewed-by: Clayton King Reviewed-by: Dillon Varone Signed-off-by: Gaghik Khachatrian Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 7dac3f35f0e8..8bbf71a8c7a8 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -3141,6 +3141,9 @@ static struct surface_update_descriptor check_update_state_and_surfaces_for_stre const int surface_count, const struct dc_stream_update *stream_update) { + (void)check_config; + (void)stream_update; + const struct dc_state *context = dc->current_state; struct surface_update_descriptor overall_type = { UPDATE_TYPE_ADDR_ONLY, LOCK_DESCRIPTOR_NONE}; From ee212b0208a18831d2b537865da56708c17af90d Mon Sep 17 00:00:00 2001 From: Nicholas Kazlauskas Date: Fri, 13 Mar 2026 16:25:25 -0400 Subject: [PATCH 442/712] drm/amd/display: Avoid turning off the PHY when OTG is running for DVI [Why] The OTG's virtual pixel clock source for DVI comes from the PHY. If the signal type is DVI then the OTG can become stuck on pre DCN401 ASIC when DPMS off occurs because the OTG remains running but the PHY transmitter is disabled. [How] There exists logic to keep track of the OTG running refcount on the link to determine if the link needs to go to PLL_EN instead of TX_EN but the logic only checks for HDMI TMDS on older ASIC. DVI is still a TMDS signal type so the constraint should also apply. Replace the checks for dc_is_hdmi_tmds_signal with dc_is_tmds_signal to cover both HDMI and DVI for the symclk refcount workaround. Reviewed-by: Dillon Varone Reviewed-by: Charlene Liu Signed-off-by: Nicholas Kazlauskas Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 4 ++-- drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 4 ++-- drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c | 2 +- drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index 8a17cc036399..d2025779d036 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -1568,7 +1568,7 @@ static enum dc_status dce110_enable_stream_timing( return DC_ERROR_UNEXPECTED; } - if (dc_is_hdmi_tmds_signal(stream->signal)) { + if (dc_is_tmds_signal(stream->signal)) { stream->link->phy_state.symclk_ref_cnts.otg = 1; if (stream->link->phy_state.symclk_state == SYMCLK_OFF_TX_OFF) stream->link->phy_state.symclk_state = SYMCLK_ON_TX_OFF; @@ -2418,7 +2418,7 @@ static void dce110_reset_hw_ctx_wrap( BREAK_TO_DEBUGGER(); } pipe_ctx_old->stream_res.tg->funcs->disable_crtc(pipe_ctx_old->stream_res.tg); - if (dc_is_hdmi_tmds_signal(pipe_ctx_old->stream->signal)) + if (dc_is_tmds_signal(pipe_ctx_old->stream->signal)) pipe_ctx_old->stream->link->phy_state.symclk_ref_cnts.otg = 0; pipe_ctx_old->plane_res.mi->funcs->free_mem_input( pipe_ctx_old->plane_res.mi, dc->current_state->stream_count); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 307e8f8060e6..a673ab0803a8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -893,7 +893,7 @@ enum dc_status dcn20_enable_stream_timing( dccg->funcs->set_dtbclk_dto(dccg, &dto_params); } - if (dc_is_hdmi_tmds_signal(stream->signal)) { + if (dc_is_tmds_signal(stream->signal)) { stream->link->phy_state.symclk_ref_cnts.otg = 1; if (stream->link->phy_state.symclk_state == SYMCLK_OFF_TX_OFF) stream->link->phy_state.symclk_state = SYMCLK_ON_TX_OFF; @@ -2856,7 +2856,7 @@ void dcn20_reset_back_end_for_pipe( * the case where the same symclk is shared across multiple otg * instances */ - if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal)) + if (dc_is_tmds_signal(pipe_ctx->stream->signal)) link->phy_state.symclk_ref_cnts.otg = 0; if (link->phy_state.symclk_state == SYMCLK_ON_TX_OFF) { link_hwss->disable_link_output(link, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c index 94f63fd54e3e..1fba44aecdd3 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c @@ -548,7 +548,7 @@ static void dcn31_reset_back_end_for_pipe( * the case where the same symclk is shared across multiple otg * instances */ - if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal)) + if (dc_is_tmds_signal(pipe_ctx->stream->signal)) link->phy_state.symclk_ref_cnts.otg = 0; if (pipe_ctx->top_pipe == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index a72284c3fa1c..ca611b3c22d3 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -1938,7 +1938,7 @@ void dcn401_reset_back_end_for_pipe( * the case where the same symclk is shared across multiple otg * instances */ - if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal)) + if (dc_is_tmds_signal(pipe_ctx->stream->signal)) link->phy_state.symclk_ref_cnts.otg = 0; if (link->phy_state.symclk_state == SYMCLK_ON_TX_OFF) { link_hwss->disable_link_output(link, From 1f991ceb2c0bd37214387a645a1d3a260d423f7d Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Mon, 16 Mar 2026 10:41:27 -0400 Subject: [PATCH 443/712] drm/amd/display: Fix bounds checking in dml2_0 clock table array [Why] Multiple locations in dml2_0 used num_clk_values-1 as array index without checking if num_clk_values > 0. When num_clk_values is 0, this results in accessing array index -1, which wraps to 255 for unsigned types, causing out-of-bounds memory access and potential crashes. [How] Add proper bounds checking using ternary operators to guard all num_clk_values-1 array accesses. When num_clk_values is 0, return 0 as fallback value instead of accessing invalid memory. This prevents buffer overflows while maintaining backward compatibility and provides sensible default behavior for empty clock tables. Reviewed-by: Dillon Varone Signed-off-by: Gabe Teeger Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c | 20 +++++++++++++++++++ .../dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c | 9 ++++++--- .../dml21/src/dml2_mcg/dml2_mcg_dcn42.c | 9 ++++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c index d17e59d684fd..ab0b4a4b5d65 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c @@ -279,6 +279,26 @@ static bool round_up_and_copy_to_next_dpm(unsigned long min_value, unsigned long bool result = false; int index = 0; + /* Guard against empty clock tables (e.g. DTBCLK on DCN42B where the + * clock is tied off and num_clk_values == 0). Without this check the + * else-if branch below would evaluate + * clk_values_khz[num_clk_values - 1] with num_clk_values == 0, which + * wraps the unsigned char index to 255 — a 235-element out-of-bounds + * read on an array of DML_MAX_CLK_TABLE_SIZE (20) entries. + * + * Semantic: if the clock doesn't exist on this ASIC but no frequency + * is required (min_value == 0), the request is trivially satisfied. + * If a non-zero frequency is required but the clock is absent, the + * configuration is unsupportable. + */ + if (clock_table->num_clk_values == 0) { + if (min_value == 0) { + *rounded_value = 0; + return true; + } + return false; + } + if (clock_table->num_clk_values > 2) { while (index < clock_table->num_clk_values && clock_table->clk_values_khz[index] < min_value) index++; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c index a265f254152c..eab13e1c96fd 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c @@ -178,9 +178,12 @@ static bool build_min_clock_table(const struct dml2_soc_bb *soc_bb, struct dml2_ min_table->max_clocks_khz.dispclk = soc_bb->clk_table.dispclk.clk_values_khz[soc_bb->clk_table.dispclk.num_clk_values - 1]; min_table->max_clocks_khz.dppclk = soc_bb->clk_table.dppclk.clk_values_khz[soc_bb->clk_table.dppclk.num_clk_values - 1]; - min_table->max_clocks_khz.dscclk = soc_bb->clk_table.dscclk.clk_values_khz[soc_bb->clk_table.dscclk.num_clk_values - 1]; - min_table->max_clocks_khz.dtbclk = soc_bb->clk_table.dtbclk.clk_values_khz[soc_bb->clk_table.dtbclk.num_clk_values - 1]; - min_table->max_clocks_khz.phyclk = soc_bb->clk_table.phyclk.clk_values_khz[soc_bb->clk_table.phyclk.num_clk_values - 1]; + min_table->max_clocks_khz.dscclk = (soc_bb->clk_table.dscclk.num_clk_values > 0) ? + soc_bb->clk_table.dscclk.clk_values_khz[soc_bb->clk_table.dscclk.num_clk_values - 1] : 0; + min_table->max_clocks_khz.dtbclk = (soc_bb->clk_table.dtbclk.num_clk_values > 0) ? + soc_bb->clk_table.dtbclk.clk_values_khz[soc_bb->clk_table.dtbclk.num_clk_values - 1] : 0; + min_table->max_clocks_khz.phyclk = (soc_bb->clk_table.phyclk.num_clk_values > 0) ? + soc_bb->clk_table.phyclk.clk_values_khz[soc_bb->clk_table.phyclk.num_clk_values - 1] : 0; min_table->max_ss_clocks_khz.dispclk = (unsigned int)((double)min_table->max_clocks_khz.dispclk / (1.0 + soc_bb->dcn_downspread_percent / 100.0)); min_table->max_ss_clocks_khz.dppclk = (unsigned int)((double)min_table->max_clocks_khz.dppclk / (1.0 + soc_bb->dcn_downspread_percent / 100.0)); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn42.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn42.c index 1f67cbc2c236..3eaeff39ee79 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn42.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn42.c @@ -54,9 +54,12 @@ static bool build_min_clock_table(const struct dml2_soc_bb *soc_bb, struct dml2_ min_table->max_clocks_khz.dispclk = soc_bb->clk_table.dispclk.clk_values_khz[soc_bb->clk_table.dispclk.num_clk_values - 1]; min_table->max_clocks_khz.dppclk = soc_bb->clk_table.dppclk.clk_values_khz[soc_bb->clk_table.dppclk.num_clk_values - 1]; - min_table->max_clocks_khz.dscclk = soc_bb->clk_table.dscclk.clk_values_khz[soc_bb->clk_table.dscclk.num_clk_values - 1]; - min_table->max_clocks_khz.dtbclk = soc_bb->clk_table.dtbclk.clk_values_khz[soc_bb->clk_table.dtbclk.num_clk_values - 1]; - min_table->max_clocks_khz.phyclk = soc_bb->clk_table.phyclk.clk_values_khz[soc_bb->clk_table.phyclk.num_clk_values - 1]; + min_table->max_clocks_khz.dscclk = (soc_bb->clk_table.dscclk.num_clk_values > 0) ? + soc_bb->clk_table.dscclk.clk_values_khz[soc_bb->clk_table.dscclk.num_clk_values - 1] : 0; + min_table->max_clocks_khz.dtbclk = (soc_bb->clk_table.dtbclk.num_clk_values > 0) ? + soc_bb->clk_table.dtbclk.clk_values_khz[soc_bb->clk_table.dtbclk.num_clk_values - 1] : 0; + min_table->max_clocks_khz.phyclk = (soc_bb->clk_table.phyclk.num_clk_values > 0) ? + soc_bb->clk_table.phyclk.clk_values_khz[soc_bb->clk_table.phyclk.num_clk_values - 1] : 0; min_table->max_ss_clocks_khz.dispclk = (unsigned int)((double)min_table->max_clocks_khz.dispclk / (1.0 + soc_bb->dcn_downspread_percent / 100.0)); min_table->max_ss_clocks_khz.dppclk = (unsigned int)((double)min_table->max_clocks_khz.dppclk / (1.0 + soc_bb->dcn_downspread_percent / 100.0)); From 4ae3e16f4b3bf64140f773629b765d605ee079a9 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Mon, 23 Mar 2026 13:02:09 +0530 Subject: [PATCH 444/712] drm/amd/display: Avoid NULL dereference in dc_dmub_srv error paths In dc_dmub_srv_log_diagnostic_data() and dc_dmub_srv_enable_dpia_trace(). Both functions check: if (!dc_dmub_srv || !dc_dmub_srv->dmub) and then call DC_LOG_ERROR() inside that block. DC_LOG_ERROR() uses dc_dmub_srv->ctx internally. So if dc_dmub_srv is NULL, the logging itself can dereference a NULL pointer and cause a crash. Fix this by splitting the checks. First check if dc_dmub_srv is NULL and return immediately. Then check dc_dmub_srv->dmub and log the error only when dc_dmub_srv is valid. Fixes the below: ../display/dc/dc_dmub_srv.c:962 dc_dmub_srv_log_diagnostic_data() error: we previously assumed 'dc_dmub_srv' could be null (see line 961) ../display/dc/dc_dmub_srv.c:1167 dc_dmub_srv_enable_dpia_trace() error: we previously assumed 'dc_dmub_srv' could be null (see line 1166) Fixes: 2631ac1ac328 ("drm/amd/display: add DMUB registers to crash dump diagnostic data.") Fixes: 71ba6b577a35 ("drm/amd/display: Add interface to enable DPIA trace") Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Dan Carpenter Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index e5a222425814..4256ba3e5719 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -958,7 +958,10 @@ void dc_dmub_srv_log_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv) { uint32_t i; - if (!dc_dmub_srv || !dc_dmub_srv->dmub) { + if (!dc_dmub_srv) + return; + + if (!dc_dmub_srv->dmub) { DC_LOG_ERROR("%s: invalid parameters.", __func__); return; } @@ -1163,7 +1166,10 @@ void dc_dmub_srv_enable_dpia_trace(const struct dc *dc) { struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv; - if (!dc_dmub_srv || !dc_dmub_srv->dmub) { + if (!dc_dmub_srv) + return; + + if (!dc_dmub_srv->dmub) { DC_LOG_ERROR("%s: invalid parameters.", __func__); return; } From fbe3a5743a4f34f57f25ab68d701d38d3b24ac3a Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Mon, 23 Mar 2026 10:24:15 +0530 Subject: [PATCH 445/712] drm/amd/display: Add NULL check for integrated_info in clk_mgr_construct clk_mgr_construct() initializes display clock and memory bandwidth settings during driver bring-up. As part of this, the driver selects a watermark table based on the memory type (DDR4, LPDDR4, LPDDR5) from ctx->dc_bios->integrated_info. The display pipeline continuously reads pixel data from memory, processes it (such as scaling, color conversion, and blending), and sends it to the screen. To keep this pipeline running smoothly, the driver must ensure there is enough memory bandwidth and that clocks are increased when needed. Watermark tables define when the GPU should increase clocks to ensure there is enough bandwidth to feed pixel data without underflow. However, ctx->dc_bios->integrated_info is dereferenced without checking for NULL in multiple clk_mgr_construct() implementations. On some platforms, BIOS may not provide this information, and accessing it directly can cause a NULL pointer dereference during initialization. Fix this by adding a NULL check before accessing integrated_info. If integrated_info is not available, the driver safely falls back to default watermark tables. Fixes: ../dcn21/rn_clk_mgr.c:775 rn_clk_mgr_construct() warn: variable dereferenced before check 'ctx->dc_bios->integrated_info' (see line 743) ../dcn301/vg_clk_mgr.c:750 vg_clk_mgr_construct() warn: variable dereferenced before check 'ctx->dc_bios->integrated_info' (see line 736) ../dcn31/dcn31_clk_mgr.c:789 dcn31_clk_mgr_construct() warn: variable dereferenced before check 'ctx->dc_bios->integrated_info' (see line 728) ../dcn314/dcn314_clk_mgr.c:906 dcn314_clk_mgr_construct() warn: variable dereferenced before check 'ctx->dc_bios->integrated_info' (see line 845) ../dcn315/dcn315_clk_mgr.c:716 dcn315_clk_mgr_construct() warn: variable dereferenced before check 'ctx->dc_bios->integrated_info' (see line 655) ../dcn316/dcn316_clk_mgr.c:660 dcn316_clk_mgr_construct() warn: variable dereferenced before check 'ctx->dc_bios->integrated_info' (see line 639) ../dcn35/dcn35_clk_mgr.c:1540 dcn35_clk_mgr_construct() warn: variable dereferenced before check 'ctx->dc_bios->integrated_info' (see line 1467) Fixes: 25879d7b4986 ("drm/amd/display: Clean FPGA code in dc") Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Dan Carpenter Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c | 3 ++- drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c | 7 ++++--- .../gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c | 7 ++++--- .../gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c | 3 ++- .../gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c | 7 ++++--- .../gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c | 7 ++++--- .../gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c | 7 ++++--- 7 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c index e18097f82091..09e83097a623 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c @@ -740,7 +740,8 @@ void rn_clk_mgr_construct( if (clk_mgr->base.dentist_vco_freq_khz == 0) clk_mgr->base.dentist_vco_freq_khz = 3600000; - if (ctx->dc_bios->integrated_info->memory_type == LpDdr4MemType) { + if (ctx->dc_bios->integrated_info && + ctx->dc_bios->integrated_info->memory_type == LpDdr4MemType) { if (clk_mgr->periodic_retraining_disabled) { rn_bw_params.wm_table = lpddr4_wm_table_with_disabled_ppt; } else { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c index 7aee02d56292..57ba7bc4d16e 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c @@ -733,11 +733,12 @@ void vg_clk_mgr_construct( if (clk_mgr->base.base.dentist_vco_freq_khz == 0) clk_mgr->base.base.dentist_vco_freq_khz = 3600000; - if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) { + if (ctx->dc_bios->integrated_info && + ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) vg_bw_params.wm_table = lpddr5_wm_table; - } else { + else vg_bw_params.wm_table = ddr4_wm_table; - } + /* Saved clocks configured at boot for debug purposes */ vg_dump_clk_registers(&clk_mgr->base.base.boot_snapshot, &clk_mgr->base.base, &log_info); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c index 051052bd10c9..44bf48f96183 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c @@ -725,11 +725,12 @@ void dcn31_clk_mgr_construct( /* TODO: Check we get what we expect during bringup */ clk_mgr->base.base.dentist_vco_freq_khz = get_vco_frequency_from_reg(&clk_mgr->base); - if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) { + if (ctx->dc_bios->integrated_info && + ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) dcn31_bw_params.wm_table = lpddr5_wm_table; - } else { + else dcn31_bw_params.wm_table = ddr5_wm_table; - } + /* Saved clocks configured at boot for debug purposes */ dcn31_dump_clk_registers(&clk_mgr->base.base.boot_snapshot, &clk_mgr->base.base, &log_info); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c index 0cb37827a62b..c69ec7a0e0ae 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c @@ -842,7 +842,8 @@ void dcn314_clk_mgr_construct( /* TODO: Check we get what we expect during bringup */ clk_mgr->base.base.dentist_vco_freq_khz = get_vco_frequency_from_reg(&clk_mgr->base); - if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) + if (ctx->dc_bios->integrated_info && + ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) dcn314_bw_params.wm_table = lpddr5_wm_table; else dcn314_bw_params.wm_table = ddr5_wm_table; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c index c49268db85f6..8d6949ad700d 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c @@ -652,11 +652,12 @@ void dcn315_clk_mgr_construct( if (clk_mgr->base.smu_ver > 0) clk_mgr->base.smu_present = true; - if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) { + if (ctx->dc_bios->integrated_info && + ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) dcn315_bw_params.wm_table = lpddr5_wm_table; - } else { + else dcn315_bw_params.wm_table = ddr5_wm_table; - } + /* Saved clocks configured at boot for debug purposes */ dcn315_dump_clk_registers(&clk_mgr->base.base.boot_snapshot, &clk_mgr->base.base, &log_info); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c index 1769b1f26e75..b858e21ca070 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c @@ -636,11 +636,12 @@ void dcn316_clk_mgr_construct( clk_mgr->base.base.dentist_vco_freq_khz = 2500000; /* 2400MHz */ - if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) { + if (ctx->dc_bios->integrated_info && + ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) dcn316_bw_params.wm_table = lpddr5_wm_table; - } else { + else dcn316_bw_params.wm_table = ddr4_wm_table; - } + /* Saved clocks configured at boot for debug purposes */ dcn316_dump_clk_registers(&clk_mgr->base.base.boot_snapshot, &clk_mgr->base.base, &log_info); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c index 6fc524752613..2798088842f4 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c @@ -1464,11 +1464,12 @@ void dcn35_clk_mgr_construct( /* TODO: Check we get what we expect during bringup */ clk_mgr->base.base.dentist_vco_freq_khz = get_vco_frequency_from_reg(&clk_mgr->base); - if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) { + if (ctx->dc_bios->integrated_info && + ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) dcn35_bw_params.wm_table = lpddr5_wm_table; - } else { + else dcn35_bw_params.wm_table = ddr5_wm_table; - } + /* Saved clocks configured at boot for debug purposes */ dcn35_save_clk_registers(&clk_mgr->base.base.boot_snapshot, clk_mgr); From c34cb0d8247458ead7184547220dbc6d285fb4e3 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Mon, 23 Mar 2026 10:08:57 +0530 Subject: [PATCH 446/712] drm/amd/display: Add update_descriptor param info in 'update_planes_and_stream_state' Add missing info for the update_descriptor parameter in update_planes_and_stream_state(). Fixes the below with gcc W=1: ../display/dc/core/dc.c:3630 function parameter 'update_descriptor' not described in 'update_planes_and_stream_state' Fixes: c24bb00cc6cf ("drm/amd/display: Refactor DC update checks") Cc: Nicholas Kazlauskas Cc: Dillon Varone Cc: Chuanyu Tseng Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Dan Carpenter Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 8bbf71a8c7a8..b8d2cac39742 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -3617,6 +3617,7 @@ static void restore_minimal_pipe_split_policy(struct dc *dc, * @surface_count: surface update count * @stream: Corresponding stream to be updated * @stream_update: stream update + * @update_descriptor: describes what plane and stream changes to apply * @new_update_type: [out] determined update type by the function * @new_context: [out] new context allocated and validated if update type is * FULL, reference to current context if update type is less than FULL. From 606f6b171326152ef08d0ef0ad49f52034edca07 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 16 Jan 2026 11:48:11 -0500 Subject: [PATCH 447/712] drm/amd/display: Merge pipes for validate Validation expects to operate on non-split pipes. This is seen in dcn20_fast_validate_bw, which merges pipes for validation. We weren't doing that in the non-fast path which lead to validation failures when operating with 4-to-1 MPC and a writeback connector. Co-developed by Claude Sonnet 4.5 Assisted-by: Claude:claude-sonnet-4.5 Reviewed-by: Nicholas Kazlauskas Signed-off-by: Harry Wentland Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 566517b99a09..90223b7c2fcd 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -1641,6 +1641,8 @@ noinline bool dcn30_internal_validate_bw( if (!pipes) return false; + dcn20_merge_pipes_for_validate(dc, context); + context->bw_ctx.dml.vba.maxMpcComb = 0; context->bw_ctx.dml.vba.VoltageLevel = 0; context->bw_ctx.dml.vba.DRAMClockChangeSupport[0][0] = dm_dram_clock_change_vactive; From 86117c5ab42f21562fedb0a64bffea3ee5fcd477 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Sat, 21 Mar 2026 17:25:14 +0530 Subject: [PATCH 448/712] drm/amd/display: Fix NULL pointer dereference in dcn401_init_hw() dcn401_init_hw() assumes that update_bw_bounding_box() is valid when entering the update path. However, the existing condition: ((!fams2_enable && update_bw_bounding_box) || freq_changed) does not guarantee this, as the freq_changed branch can evaluate to true independently of the callback pointer. This can result in calling update_bw_bounding_box() when it is NULL. Fix this by separating the update condition from the pointer checks and ensuring the callback, dc->clk_mgr, and bw_params are validated before use. Fixes the below: ../dc/hwss/dcn401/dcn401_hwseq.c:367 dcn401_init_hw() error: we previously assumed 'dc->res_pool->funcs->update_bw_bounding_box' could be null (see line 362) Fixes: ca0fb243c3bb ("drm/amd/display: Underflow Seen on DCN401 eGPU") Cc: Daniel Sa Cc: Alvin Lee Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Dan Carpenter Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher --- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index ca611b3c22d3..f53ce9d4d8c8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -143,6 +143,7 @@ void dcn401_init_hw(struct dc *dc) int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; + bool dchub_ref_freq_changed; int current_dchub_ref_freq = 0; if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) { @@ -357,14 +358,18 @@ void dcn401_init_hw(struct dc *dc) dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr; dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0; dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver; + + /* sw and fw FAMS versions must match for support */ dc->debug.fams2_config.bits.enable &= - dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; // sw & fw fams versions must match for support - if ((!dc->debug.fams2_config.bits.enable && dc->res_pool->funcs->update_bw_bounding_box) - || res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq) { + dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; + dchub_ref_freq_changed = + res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq; + if ((!dc->debug.fams2_config.bits.enable || dchub_ref_freq_changed) && + dc->res_pool->funcs->update_bw_bounding_box && + dc->clk_mgr && dc->clk_mgr->bw_params) { /* update bounding box if FAMS2 disabled, or if dchub clk has changed */ - if (dc->clk_mgr) - dc->res_pool->funcs->update_bw_bounding_box(dc, - dc->clk_mgr->bw_params); + dc->res_pool->funcs->update_bw_bounding_box(dc, + dc->clk_mgr->bw_params); } } } From 4c3aeb11d504b68c63a1caeecf5517385e6e7bdb Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 16 Jan 2026 11:47:50 -0500 Subject: [PATCH 449/712] drm/amd/display: Don't set 4to1MPC config dynamically We were previously modifying the global dc->config.enable_4to1MPC dynamically. These variables are meant as global configs, not to by dynamically modified. Modifying them dynamically prevents us from enabling/disabling functionality for debug purposes and can easily lead to bad things since we're not operating on the current state but on DC-wide variables. Instead we should look at the existing split4mpc decision in dcn20_validate_apply_split_flags and make the decision there, if the global config.enable_4to1MPC is set to true for the DCN version we're running. This fixes corruption that is observed when running a new IGT kms_colorop test for color-space-conversion that uses a YUV plane and outputs to a writeback connector. Co-developed by Claude Sonnet 4.5. Assisted-by: Claude:claude-sonnet-4.5 Reviewed-by: Nicholas Kazlauskas Signed-off-by: Harry Wentland Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc.h | 2 +- .../drm/amd/display/dc/dml/dcn314/dcn314_fpu.c | 6 +----- .../gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c | 7 +------ .../drm/amd/display/dc/dml/dcn351/dcn351_fpu.c | 7 +------ .../display/dc/resource/dcn20/dcn20_resource.c | 16 ++++++++++++++-- .../display/dc/resource/dcn31/dcn31_resource.c | 10 +++++----- .../display/dc/resource/dcn314/dcn314_resource.c | 3 +++ .../display/dc/resource/dcn315/dcn315_resource.c | 5 +++-- .../display/dc/resource/dcn316/dcn316_resource.c | 6 ++++-- .../display/dc/resource/dcn35/dcn35_resource.c | 3 +++ .../display/dc/resource/dcn351/dcn351_resource.c | 3 +++ 11 files changed, 39 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 80e217c5a23d..41b5dedb6eff 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -521,7 +521,7 @@ struct dc_config { union allow_lttpr_non_transparent_mode allow_lttpr_non_transparent_mode; bool multi_mon_pp_mclk_switch; bool disable_dmcu; - bool enable_4to1MPC; + bool allow_4to1MPC; bool enable_windowed_mpo_odm; bool forceHBR2CP2520; // Used for switching between test patterns TPS4 and CP2520 uint32_t allow_edp_hotplug_detection; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c index df9d50b9b57c..ab016c294ba7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c @@ -391,13 +391,9 @@ int dcn314_populate_dml_pipes_from_context_fpu(struct dc *dc, struct dc_state *c } context->bw_ctx.dml.ip.det_buffer_size_kbytes = DCN3_14_DEFAULT_DET_SIZE; - dc->config.enable_4to1MPC = false; if (pipe_cnt == 1 && pipe->plane_state && pipe->plane_state->rotation == ROTATION_ANGLE_0 && !dc->debug.disable_z9_mpc) { - if (is_dual_plane(pipe->plane_state->format) - && pipe->plane_state->src_rect.width <= 1920 && pipe->plane_state->src_rect.height <= 1080) { - dc->config.enable_4to1MPC = true; - } else if (!is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 5120) { + if (!is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 5120) { /* Limit to 5k max to avoid forced pipe split when there is not enough detile for swath */ context->bw_ctx.dml.ip.det_buffer_size_kbytes = 192; pipes[0].pipe.src.unbounded_req_mode = true; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c index 8a177d5ae213..6713cd8ba86a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c @@ -528,14 +528,9 @@ int dcn35_populate_dml_pipes_from_context_fpu(struct dc *dc, } context->bw_ctx.dml.ip.det_buffer_size_kbytes = 384;/*per guide*/ - dc->config.enable_4to1MPC = false; if (pipe_cnt == 1 && pipe->plane_state && !dc->debug.disable_z9_mpc) { - if (is_dual_plane(pipe->plane_state->format) - && pipe->plane_state->src_rect.width <= 1920 && - pipe->plane_state->src_rect.height <= 1080) { - dc->config.enable_4to1MPC = true; - } else if (!is_dual_plane(pipe->plane_state->format) && + if (!is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 5120) { /* * Limit to 5k max to avoid forced pipe split when there diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn351/dcn351_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn351/dcn351_fpu.c index 77023b619f1e..73c2aee57f28 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn351/dcn351_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn351/dcn351_fpu.c @@ -561,14 +561,9 @@ int dcn351_populate_dml_pipes_from_context_fpu(struct dc *dc, } context->bw_ctx.dml.ip.det_buffer_size_kbytes = 384;/*per guide*/ - dc->config.enable_4to1MPC = false; if (pipe_cnt == 1 && pipe->plane_state && !dc->debug.disable_z9_mpc) { - if (is_dual_plane(pipe->plane_state->format) - && pipe->plane_state->src_rect.width <= 1920 && - pipe->plane_state->src_rect.height <= 1080) { - dc->config.enable_4to1MPC = true; - } else if (!is_dual_plane(pipe->plane_state->format) && + if (!is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 5120) { /* * Limit to 5k max to avoid forced pipe split when there diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c index 8426d5f9f377..b50a2509463e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c @@ -1814,6 +1814,11 @@ void dcn20_merge_pipes_for_validate( } } +static bool is_dual_plane(enum surface_pixel_format format) +{ + return format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN || format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA; +} + int dcn20_validate_apply_pipe_split_flags( struct dc *dc, struct dc_state *context, @@ -1898,8 +1903,15 @@ int dcn20_validate_apply_pipe_split_flags( for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; int pipe_plane = v->pipe_plane[pipe_idx]; - bool split4mpc = context->stream_count == 1 && plane_count == 1 - && dc->config.enable_4to1MPC && dc->res_pool->pipe_count >= 4; + bool split4mpc = false; + + if (context->stream_count == 1 && plane_count == 1 + && dc->config.allow_4to1MPC && dc->res_pool->pipe_count >= 4 + && !dc->debug.disable_z9_mpc + && pipe->plane_state && is_dual_plane(pipe->plane_state->format) + && pipe->plane_state->src_rect.width <= 1920 + && pipe->plane_state->src_rect.height <= 1080) + split4mpc = true; if (!context->res_ctx.pipe_ctx[i].stream) continue; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 6ce6b2b1f288..428524f2ede6 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -1699,12 +1699,9 @@ int dcn31_populate_dml_pipes_from_context( pipe_cnt++; } context->bw_ctx.dml.ip.det_buffer_size_kbytes = DCN3_1_DEFAULT_DET_SIZE; - dc->config.enable_4to1MPC = false; + if (pipe_cnt == 1 && pipe->plane_state && !dc->debug.disable_z9_mpc) { - if (is_dual_plane(pipe->plane_state->format) - && pipe->plane_state->src_rect.width <= 1920 && pipe->plane_state->src_rect.height <= 1080) { - dc->config.enable_4to1MPC = true; - } else if (!is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 5120) { + if (!is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 5120) { /* Limit to 5k max to avoid forced pipe split when there is not enough detile for swath */ context->bw_ctx.dml.ip.det_buffer_size_kbytes = 192; pipes[0].pipe.src.unbounded_req_mode = true; @@ -1922,6 +1919,9 @@ static bool dcn31_resource_construct( dc->caps.is_apu = true; dc->caps.zstate_support = true; + /* Enable 4to1MPC by default */ + dc->config.allow_4to1MPC = true; + /* Color pipeline capabilities */ dc->caps.color.dpp.dcn_arch = 1; dc->caps.color.dpp.input_lut_shared = 0; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index 66bf7725aeaf..795de8f65117 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -1830,6 +1830,9 @@ static bool dcn314_resource_construct( pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; + + /* Enable 4to1MPC by default */ + dc->config.allow_4to1MPC = true; dc->caps.max_downscale_ratio = 400; dc->caps.i2c_speed_in_khz = 100; dc->caps.i2c_speed_in_khz_hdcp = 100; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index 0e0c52128c55..4db684fbc217 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -1785,11 +1785,9 @@ static int dcn315_populate_dml_pipes_from_context( if (context->bw_ctx.dml.ip.det_buffer_size_kbytes > DCN3_15_MAX_DET_SIZE) context->bw_ctx.dml.ip.det_buffer_size_kbytes = DCN3_15_MAX_DET_SIZE; - dc->config.enable_4to1MPC = false; if (pipe_cnt == 1 && pipe->plane_state && !dc->debug.disable_z9_mpc) { if (is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 1920 && pipe->plane_state->src_rect.height <= 1080) { - dc->config.enable_4to1MPC = true; context->bw_ctx.dml.ip.det_buffer_size_kbytes = (max_usable_det / DCN3_15_CRB_SEGMENT_SIZE_KB / 4) * DCN3_15_CRB_SEGMENT_SIZE_KB; } else if (!is_dual_plane(pipe->plane_state->format) @@ -1870,6 +1868,9 @@ static bool dcn315_resource_construct( *************************************************/ pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; + + /* Enable 4to1MPC by default */ + dc->config.allow_4to1MPC = true; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; dc->caps.i2c_speed_in_khz = 100; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index 63675b53674a..db94141d113f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -1669,11 +1669,9 @@ static int dcn316_populate_dml_pipes_from_context( if (context->bw_ctx.dml.ip.det_buffer_size_kbytes > DCN3_16_MAX_DET_SIZE) context->bw_ctx.dml.ip.det_buffer_size_kbytes = DCN3_16_MAX_DET_SIZE; ASSERT(context->bw_ctx.dml.ip.det_buffer_size_kbytes >= DCN3_16_DEFAULT_DET_SIZE); - dc->config.enable_4to1MPC = false; if (pipe_cnt == 1 && pipe->plane_state && !dc->debug.disable_z9_mpc) { if (is_dual_plane(pipe->plane_state->format) && pipe->plane_state->src_rect.width <= 1920 && pipe->plane_state->src_rect.height <= 1080) { - dc->config.enable_4to1MPC = true; context->bw_ctx.dml.ip.det_buffer_size_kbytes = (max_usable_det / DCN3_16_CRB_SEGMENT_SIZE_KB / 4) * DCN3_16_CRB_SEGMENT_SIZE_KB; } else if (!is_dual_plane(pipe->plane_state->format)) { @@ -1746,6 +1744,10 @@ static bool dcn316_resource_construct( pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; + + /* Enable 4to1MPC by default */ + dc->config.allow_4to1MPC = true; + dc->caps.max_downscale_ratio = 600; dc->caps.i2c_speed_in_khz = 100; dc->caps.i2c_speed_in_khz_hdcp = 5; /*1.5 w/a applied by default*/ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index ff557c4d594e..5118bec38d32 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -1827,6 +1827,9 @@ static bool dcn35_resource_construct( clk_src_regs_init(3, D), clk_src_regs_init(4, E); + /* Enable 4to1MPC by default */ + dc->config.allow_4to1MPC = true; + #undef REG_STRUCT #define REG_STRUCT abm_regs abm_regs_init(0), diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index 0c39d0b17947..b64ad2d9fc2f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -1800,6 +1800,9 @@ static bool dcn351_resource_construct( clk_src_regs_init(3, D), clk_src_regs_init(4, E); + /* Enable 4to1MPC by default */ + dc->config.allow_4to1MPC = true; + #undef REG_STRUCT #define REG_STRUCT abm_regs abm_regs_init(0), From d77ff7331fc34ba84a70914b316a55056d94827d Mon Sep 17 00:00:00 2001 From: Gaghik Khachatrian Date: Mon, 16 Mar 2026 16:05:31 -0400 Subject: [PATCH 450/712] drm/amd/display: Fix Silence Conversion Warnings in Dmub Fix Conversion that might result in a loss of data warnings in dmub/src/: - dmub_dcn20/31/32/35/42/60/401.c: Add ASSERT(value <= 0xFF) and explicit (uint8_t) cast when storing REG_GET results into uint8_t debug struct fields. Add != 0 for bool assignments from uint32_t bitfield reads. - dmub_reg.c: Cast va_arg shift value to uint8_t with ASSERT guard before passing to set_reg_field_value_masks(). - dmub_srv.c: Widen num_pending to uint64_t to match uint64_t arithmetic; use != 0 for bool assignments from unsigned expressions. No functional change intended. Reviewed-by: Dillon Varone Signed-off-by: Gaghik Khachatrian Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dmub/src/dmub_dcn20.c | 18 ++++++++++----- .../gpu/drm/amd/display/dmub/src/dmub_dcn31.c | 21 +++++++++++------ .../gpu/drm/amd/display/dmub/src/dmub_dcn32.c | 15 ++++++++---- .../gpu/drm/amd/display/dmub/src/dmub_dcn35.c | 17 +++++++++----- .../drm/amd/display/dmub/src/dmub_dcn401.c | 21 +++++++++++------ .../gpu/drm/amd/display/dmub/src/dmub_dcn42.c | 23 ++++++++++++------- .../gpu/drm/amd/display/dmub/src/dmub_reg.c | 3 ++- .../gpu/drm/amd/display/dmub/src/dmub_srv.c | 8 +++---- 8 files changed, 82 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c index 54df2147e4dc..73221ca53b7d 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c @@ -460,20 +460,26 @@ void dmub_dcn20_get_diagnostic_data(struct dmub_srv *dmub) dmub->debug.inbox0_size = REG_READ(DMCUB_INBOX0_SIZE); REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_dmub_enabled); - dmub->debug.is_dmcub_enabled = is_dmub_enabled; + ASSERT(is_dmub_enabled <= 0xFF); + dmub->debug.is_dmcub_enabled = (uint8_t)is_dmub_enabled; REG_GET(DMCUB_CNTL, DMCUB_SOFT_RESET, &is_soft_reset); - dmub->debug.is_dmcub_soft_reset = is_soft_reset; + ASSERT(is_soft_reset <= 0xFF); + dmub->debug.is_dmcub_soft_reset = (uint8_t)is_soft_reset; REG_GET(DMCUB_SEC_CNTL, DMCUB_SEC_RESET_STATUS, &is_sec_reset); - dmub->debug.is_dmcub_secure_reset = is_sec_reset; + ASSERT(is_sec_reset <= 0xFF); + dmub->debug.is_dmcub_secure_reset = (uint8_t)is_sec_reset; REG_GET(DMCUB_CNTL, DMCUB_TRACEPORT_EN, &is_traceport_enabled); - dmub->debug.is_traceport_en = is_traceport_enabled; + ASSERT(is_traceport_enabled <= 0xFF); + dmub->debug.is_traceport_en = (uint8_t)is_traceport_enabled; REG_GET(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_ENABLE, &is_cw0_enabled); - dmub->debug.is_cw0_enabled = is_cw0_enabled; + ASSERT(is_cw0_enabled <= 0xFF); + dmub->debug.is_cw0_enabled = (uint8_t)is_cw0_enabled; REG_GET(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, &is_cw6_enabled); - dmub->debug.is_cw6_enabled = is_cw6_enabled; + ASSERT(is_cw6_enabled <= 0xFF); + dmub->debug.is_cw6_enabled = (uint8_t)is_cw6_enabled; } diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c index a0cefc03b21d..244244f3df80 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c @@ -466,25 +466,32 @@ void dmub_dcn31_get_diagnostic_data(struct dmub_srv *dmub) dmub->debug.outbox1_size = REG_READ(DMCUB_OUTBOX1_SIZE); REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_dmub_enabled); - dmub->debug.is_dmcub_enabled = is_dmub_enabled; + ASSERT(is_dmub_enabled <= 0xFF); + dmub->debug.is_dmcub_enabled = (uint8_t)is_dmub_enabled; REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &is_pwait); - dmub->debug.is_pwait = is_pwait; + ASSERT(is_pwait <= 0xFF); + dmub->debug.is_pwait = (uint8_t)is_pwait; REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &is_soft_reset); - dmub->debug.is_dmcub_soft_reset = is_soft_reset; + ASSERT(is_soft_reset <= 0xFF); + dmub->debug.is_dmcub_soft_reset = (uint8_t)is_soft_reset; REG_GET(DMCUB_SEC_CNTL, DMCUB_SEC_RESET_STATUS, &is_sec_reset); - dmub->debug.is_dmcub_secure_reset = is_sec_reset; + ASSERT(is_sec_reset <= 0xFF); + dmub->debug.is_dmcub_secure_reset = (uint8_t)is_sec_reset; REG_GET(DMCUB_CNTL, DMCUB_TRACEPORT_EN, &is_traceport_enabled); - dmub->debug.is_traceport_en = is_traceport_enabled; + ASSERT(is_traceport_enabled <= 0xFF); + dmub->debug.is_traceport_en = (uint8_t)is_traceport_enabled; REG_GET(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_ENABLE, &is_cw0_enabled); - dmub->debug.is_cw0_enabled = is_cw0_enabled; + ASSERT(is_cw0_enabled <= 0xFF); + dmub->debug.is_cw0_enabled = (uint8_t)is_cw0_enabled; REG_GET(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, &is_cw6_enabled); - dmub->debug.is_cw6_enabled = is_cw6_enabled; + ASSERT(is_cw6_enabled <= 0xFF); + dmub->debug.is_cw6_enabled = (uint8_t)is_cw6_enabled; } bool dmub_dcn31_should_detect(struct dmub_srv *dmub) diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c index 2f99a2772599..5d86f649db4b 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c @@ -486,19 +486,24 @@ void dmub_dcn32_get_diagnostic_data(struct dmub_srv *dmub) dmub->debug.outbox1_size = REG_READ(DMCUB_OUTBOX1_SIZE); REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_dmub_enabled); - dmub->debug.is_dmcub_enabled = is_dmub_enabled; + ASSERT(is_dmub_enabled <= 0xFF); + dmub->debug.is_dmcub_enabled = (uint8_t)is_dmub_enabled; REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &is_pwait); - dmub->debug.is_pwait = is_pwait; + ASSERT(is_pwait <= 0xFF); + dmub->debug.is_pwait = (uint8_t)is_pwait; REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &is_soft_reset); - dmub->debug.is_dmcub_soft_reset = is_soft_reset; + ASSERT(is_soft_reset <= 0xFF); + dmub->debug.is_dmcub_soft_reset = (uint8_t)is_soft_reset; REG_GET(DMCUB_CNTL, DMCUB_TRACEPORT_EN, &is_traceport_enabled); - dmub->debug.is_traceport_en = is_traceport_enabled; + ASSERT(is_traceport_enabled <= 0xFF); + dmub->debug.is_traceport_en = (uint8_t)is_traceport_enabled; REG_GET(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, &is_cw6_enabled); - dmub->debug.is_cw6_enabled = is_cw6_enabled; + ASSERT(is_cw6_enabled <= 0xFF); + dmub->debug.is_cw6_enabled = (uint8_t)is_cw6_enabled; dmub->debug.gpint_datain0 = REG_READ(DMCUB_GPINT_DATAIN0); } diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c index 639f9835e5e9..f9b16eb8ef8e 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c @@ -402,7 +402,7 @@ void dmub_dcn35_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmu union dmub_fw_boot_options boot_options = {0}; if (!dmub->dpia_supported) { - dmub->dpia_supported = dmub_dcn35_get_fw_boot_option(dmub).bits.enable_dpia; + dmub->dpia_supported = dmub_dcn35_get_fw_boot_option(dmub).bits.enable_dpia != 0; } boot_options.bits.z10_disable = params->disable_z10; @@ -508,19 +508,24 @@ void dmub_dcn35_get_diagnostic_data(struct dmub_srv *dmub) dmub->debug.outbox1_size = REG_READ(DMCUB_OUTBOX1_SIZE); REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_dmub_enabled); - dmub->debug.is_dmcub_enabled = is_dmub_enabled; + ASSERT(is_dmub_enabled <= 0xFF); + dmub->debug.is_dmcub_enabled = (uint8_t)is_dmub_enabled; REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &is_pwait); - dmub->debug.is_pwait = is_pwait; + ASSERT(is_pwait <= 0xFF); + dmub->debug.is_pwait = (uint8_t)is_pwait; REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &is_soft_reset); - dmub->debug.is_dmcub_soft_reset = is_soft_reset; + ASSERT(is_soft_reset <= 0xFF); + dmub->debug.is_dmcub_soft_reset = (uint8_t)is_soft_reset; REG_GET(DMCUB_CNTL, DMCUB_TRACEPORT_EN, &is_traceport_enabled); - dmub->debug.is_traceport_en = is_traceport_enabled; + ASSERT(is_traceport_enabled <= 0xFF); + dmub->debug.is_traceport_en = (uint8_t)is_traceport_enabled; REG_GET(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, &is_cw6_enabled); - dmub->debug.is_cw6_enabled = is_cw6_enabled; + ASSERT(is_cw6_enabled <= 0xFF); + dmub->debug.is_cw6_enabled = (uint8_t)is_cw6_enabled; dmub->debug.gpint_datain0 = REG_READ(DMCUB_GPINT_DATAIN0); } diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c index 16ed07f0e96d..3d2307d0ce49 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c @@ -473,25 +473,32 @@ void dmub_dcn401_get_diagnostic_data(struct dmub_srv *dmub) dmub->debug.outbox1_size = REG_READ(DMCUB_OUTBOX1_SIZE); REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_dmub_enabled); - dmub->debug.is_dmcub_enabled = is_dmub_enabled; + ASSERT(is_dmub_enabled <= 0xFF); + dmub->debug.is_dmcub_enabled = (uint8_t)is_dmub_enabled; REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &is_pwait); - dmub->debug.is_pwait = is_pwait; + ASSERT(is_pwait <= 0xFF); + dmub->debug.is_pwait = (uint8_t)is_pwait; REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &is_soft_reset); - dmub->debug.is_dmcub_soft_reset = is_soft_reset; + ASSERT(is_soft_reset <= 0xFF); + dmub->debug.is_dmcub_soft_reset = (uint8_t)is_soft_reset; REG_GET(DMCUB_SEC_CNTL, DMCUB_SEC_RESET_STATUS, &is_sec_reset); - dmub->debug.is_dmcub_secure_reset = is_sec_reset; + ASSERT(is_sec_reset <= 0xFF); + dmub->debug.is_dmcub_secure_reset = (uint8_t)is_sec_reset; REG_GET(DMCUB_CNTL, DMCUB_TRACEPORT_EN, &is_traceport_enabled); - dmub->debug.is_traceport_en = is_traceport_enabled; + ASSERT(is_traceport_enabled <= 0xFF); + dmub->debug.is_traceport_en = (uint8_t)is_traceport_enabled; REG_GET(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_ENABLE, &is_cw0_enabled); - dmub->debug.is_cw0_enabled = is_cw0_enabled; + ASSERT(is_cw0_enabled <= 0xFF); + dmub->debug.is_cw0_enabled = (uint8_t)is_cw0_enabled; REG_GET(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, &is_cw6_enabled); - dmub->debug.is_cw6_enabled = is_cw6_enabled; + ASSERT(is_cw6_enabled <= 0xFF); + dmub->debug.is_cw6_enabled = (uint8_t)is_cw6_enabled; dmub->debug.gpint_datain0 = REG_READ(DMCUB_GPINT_DATAIN0); } diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c index f687359b7d83..7b870b831199 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c @@ -41,7 +41,7 @@ void dmub_dcn42_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmu union dmub_fw_boot_options boot_options = {0}; if (!dmub->dpia_supported) { - dmub->dpia_supported = dmub_dcn42_get_fw_boot_option(dmub).bits.enable_dpia; + dmub->dpia_supported = dmub_dcn42_get_fw_boot_option(dmub).bits.enable_dpia != 0; } boot_options.bits.z10_disable = params->disable_z10; @@ -676,25 +676,32 @@ void dmub_dcn42_get_diagnostic_data(struct dmub_srv *dmub) dmub->debug.outbox1_size = REG_READ(DMCUB_OUTBOX1_SIZE); REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_dmub_enabled); - dmub->debug.is_dmcub_enabled = is_dmub_enabled; + ASSERT(is_dmub_enabled <= 0xFF); + dmub->debug.is_dmcub_enabled = (uint8_t)is_dmub_enabled; REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &is_pwait); - dmub->debug.is_pwait = is_pwait; + ASSERT(is_pwait <= 0xFF); + dmub->debug.is_pwait = (uint8_t)is_pwait; REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &is_soft_reset); - dmub->debug.is_dmcub_soft_reset = is_soft_reset; + ASSERT(is_soft_reset <= 0xFF); + dmub->debug.is_dmcub_soft_reset = (uint8_t)is_soft_reset; REG_GET(DMCUB_SEC_CNTL, DMCUB_SEC_RESET_STATUS, &is_sec_reset); - dmub->debug.is_dmcub_secure_reset = is_sec_reset; + ASSERT(is_sec_reset <= 0xFF); + dmub->debug.is_dmcub_secure_reset = (uint8_t)is_sec_reset; REG_GET(DMCUB_CNTL, DMCUB_TRACEPORT_EN, &is_traceport_enabled); - dmub->debug.is_traceport_en = is_traceport_enabled; + ASSERT(is_traceport_enabled <= 0xFF); + dmub->debug.is_traceport_en = (uint8_t)is_traceport_enabled; REG_GET(DMCUB_REGION3_CW0_TOP_ADDRESS, DMCUB_REGION3_CW0_ENABLE, &is_cw0_enabled); - dmub->debug.is_cw0_enabled = is_cw0_enabled; + ASSERT(is_cw0_enabled <= 0xFF); + dmub->debug.is_cw0_enabled = (uint8_t)is_cw0_enabled; REG_GET(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, &is_cw6_enabled); - dmub->debug.is_cw6_enabled = is_cw6_enabled; + ASSERT(is_cw6_enabled <= 0xFF); + dmub->debug.is_cw6_enabled = (uint8_t)is_cw6_enabled; dmub->debug.gpint_datain0 = REG_READ(DMCUB_GPINT_DATAIN0); } diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c index ca0c8a54b635..94f4931d3d44 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c @@ -57,8 +57,9 @@ static void set_reg_field_values(struct dmub_reg_value_masks *field_value_mask, mask = va_arg(ap, uint32_t); field_value = va_arg(ap, uint32_t); + ASSERT(shift <= 0xFF); set_reg_field_value_masks(field_value_mask, field_value, mask, - shift); + (uint8_t)shift); i++; } } diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c index 3bba256a288d..10d23f5f5d94 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c @@ -1034,8 +1034,8 @@ enum dmub_status dmub_srv_wait_for_auto_load(struct dmub_srv *dmub, static void dmub_srv_update_reg_inbox0_status(struct dmub_srv *dmub) { if (dmub->reg_inbox0.is_pending) { - dmub->reg_inbox0.is_pending = dmub->hw_funcs.read_reg_inbox0_rsp_int_status && - !dmub->hw_funcs.read_reg_inbox0_rsp_int_status(dmub); + dmub->reg_inbox0.is_pending = (dmub->hw_funcs.read_reg_inbox0_rsp_int_status && + !dmub->hw_funcs.read_reg_inbox0_rsp_int_status(dmub)) != 0; if (!dmub->reg_inbox0.is_pending) { /* ack the rsp interrupt */ @@ -1320,7 +1320,7 @@ void dmub_srv_set_power_state(struct dmub_srv *dmub, enum dmub_srv_power_state_t enum dmub_status dmub_srv_reg_cmd_execute(struct dmub_srv *dmub, union dmub_rb_cmd *cmd) { - uint32_t num_pending = 0; + uint64_t num_pending = 0; if (!dmub->hw_init) return DMUB_STATUS_INVALID; @@ -1348,7 +1348,7 @@ enum dmub_status dmub_srv_reg_cmd_execute(struct dmub_srv *dmub, union dmub_rb_c dmub->reg_inbox0.num_submitted++; dmub->reg_inbox0.is_pending = true; - dmub->reg_inbox0.is_multi_pending = cmd->cmd_common.header.multi_cmd_pending; + dmub->reg_inbox0.is_multi_pending = cmd->cmd_common.header.multi_cmd_pending != 0; return DMUB_STATUS_OK; } From 8424ee7d524965c2ce245d30f7c778763e8c6f54 Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Wed, 11 Mar 2026 14:34:30 -0400 Subject: [PATCH 451/712] drm/amd/display: eliminate clock manager code duplication [Why] Clock manager contained significant duplicate code between variants with identical logic for functions using only SMU calls or shared registers. This increases maintenance overhead and potential for bugs. [How] Expose clock constants and internal functions in header for sharing. Remove duplicate implementations and update function pointers to use shared functions. Refactor remaining variant-specific functions to use shared constants and helper functions. Add compatibility comments for hardware differences. Reviewed-by: Dmytro Laktyushkin Signed-off-by: Gabe Teeger Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 26 ++++++++++--------- .../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h | 8 +++++- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c index b4c6522e922c..97f182bfc9ca 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c @@ -43,8 +43,6 @@ #define DC_LOGGER_INIT(logger) \ struct dal_logger *dc_logger = logger -#define DCN42_CLKIP_REFCLK 48000 - #undef FN #define FN(reg_name, field_name) \ clk_mgr->clk_mgr_shift->field_name, clk_mgr->clk_mgr_mask->field_name @@ -255,6 +253,10 @@ void dcn42_update_clocks(struct clk_mgr *clk_mgr_base, dcn42_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } + /* Only attempt to enable dtbclk if currently disabled AND new state requests it. + * For dcn42b (no dtbclk hardware), init_clk_states sets dtbclk_en=false and + * new_clocks->dtbclk_en should always be false, so this block never executes. + */ if (!clk_mgr_base->clks.dtbclk_en && new_clocks->dtbclk_en) { int actual_dtbclk = 0; @@ -326,7 +328,7 @@ void dcn42_update_clocks(struct clk_mgr *clk_mgr_base, } /* clock limits are received with MHz precision, divide by 1000 to prevent setting clocks at every call */ - if (!dc->debug.disable_dtb_ref_clk_switch && + if (!dc->debug.disable_dtb_ref_clk_switch && new_clocks->dtbclk_en && should_set_clock(safe_to_lower, new_clocks->ref_dtbclk_khz / 1000, clk_mgr_base->clks.ref_dtbclk_khz / 1000)) { dcn42_update_clocks_update_dtb_dto(clk_mgr, context, new_clocks->ref_dtbclk_khz); @@ -519,7 +521,7 @@ static void init_clk_states(struct clk_mgr *clk_mgr) clk_mgr->clks.zstate_support = DCN_ZSTATE_SUPPORT_UNKNOWN; } -static void dcn42_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr, +void dcn42_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr, struct dcn42_smu_dpm_clks *smu_dpm_clks) { DpmClocks_t_dcn42 *table = smu_dpm_clks->dpm_clks; @@ -842,7 +844,7 @@ static void dcn42_init_clocks_fpga(struct clk_mgr *clk_mgr) } -static void dcn42_update_clocks_fpga(struct clk_mgr *clk_mgr, +void dcn42_update_clocks_fpga(struct clk_mgr *clk_mgr, struct dc_state *context, bool safe_to_lower) { @@ -895,13 +897,13 @@ static void dcn42_update_clocks_fpga(struct clk_mgr *clk_mgr, // Both fclk and ref_dppclk run on the same scemi clock. clk_mgr_int->dccg->ref_dppclk = clk_mgr->clks.fclk_khz; - /* TODO: set dtbclk in correct place */ - clk_mgr->clks.dtbclk_en = true; - dm_set_dcn_clocks(clk_mgr->ctx, &clk_mgr->clks); + if (clk_mgr->clks.dtbclk_en) { + dcn42_update_clocks_update_dtb_dto(clk_mgr_int, context, clk_mgr->clks.ref_dtbclk_khz); + } else { + clk_mgr->clks.ref_dtbclk_khz = 0; + } dcn42_update_clocks_update_dpp_dto(clk_mgr_int, context, safe_to_lower); - - dcn42_update_clocks_update_dtb_dto(clk_mgr_int, context, clk_mgr->clks.ref_dtbclk_khz); } unsigned int dcn42_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type clk_type) @@ -933,7 +935,7 @@ unsigned int dcn42_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type return 0; } -static int dcn42_get_dispclk_from_dentist(struct clk_mgr *clk_mgr_base) +int dcn42_get_dispclk_from_dentist(struct clk_mgr *clk_mgr_base) { struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); uint32_t dispclk_wdivider; @@ -954,7 +956,7 @@ bool dcn42_is_smu_present(struct clk_mgr *clk_mgr_base) return clk_mgr->smu_present; } -static void dcn42_get_smu_clocks(struct clk_mgr_internal *clk_mgr_int) +void dcn42_get_smu_clocks(struct clk_mgr_internal *clk_mgr_int) { struct clk_mgr *clk_mgr_base = &clk_mgr_int->base; struct dcn42_smu_dpm_clks smu_dpm_clks = { 0 }; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h index 5ad027a9edaf..42aea81fb61c 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h @@ -27,6 +27,7 @@ #include "clk_mgr_internal.h" #define NUM_CLOCK_SOURCES 5 +#define DCN42_CLKIP_REFCLK 48000 struct dcn42_watermarks; @@ -71,9 +72,14 @@ void dcn42_set_low_power_state(struct clk_mgr *clk_mgr_base); void dcn42_exit_low_power_state(struct clk_mgr *clk_mgr_base); unsigned int dcn42_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type clk_type); bool dcn42_is_smu_present(struct clk_mgr *clk_mgr_base); +bool dcn42_has_active_display(struct dc *dc, const struct dc_state *context); int dcn42_get_active_display_cnt_wa(struct dc *dc, struct dc_state *context, int *all_active_disps); void dcn42_update_clocks_update_dpp_dto(struct clk_mgr_internal *clk_mgr, struct dc_state *context, bool safe_to_lower); void dcn42_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, struct dc_state *context, int ref_dtbclk_khz); bool dcn42_is_spll_ssc_enabled(struct clk_mgr *clk_mgr_base); -bool dcn42_has_active_display(struct dc *dc, const struct dc_state *context); +struct dcn42_smu_dpm_clks; /* Forward declaration for pointer parameter below */ +void dcn42_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr, struct dcn42_smu_dpm_clks *smu_dpm_clks); +void dcn42_get_smu_clocks(struct clk_mgr_internal *clk_mgr_int); +void dcn42_update_clocks_fpga(struct clk_mgr *clk_mgr, struct dc_state *context, bool safe_to_lower); +int dcn42_get_dispclk_from_dentist(struct clk_mgr *clk_mgr_base); #endif //__DCN42_CLK_MGR_H__ From 26ebcac0566b78a9e6bfb71fb6b3436e0fe251a6 Mon Sep 17 00:00:00 2001 From: Gaghik Khachatrian Date: Thu, 12 Mar 2026 15:42:01 -0400 Subject: [PATCH 452/712] drm/amd/display: Fix Silence signed/unsighed mismatch warning in dc [Why] Implicit signed-to-unsigned conversions caused compiler warnings in DC paths. [How] Added explicit (unsigned int)/(uint32_t) casts for sentinel -1 assignments and IRQ ~MASK initializers, with small cast alignment in logging/DPCD code. Functionality and behavior is unchanged; only type intent is explicit. Reviewed-by: Dillon Varone Signed-off-by: Gaghik Khachatrian Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c | 8 ++++---- .../amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c | 2 +- drivers/gpu/drm/amd/display/dc/core/dc_resource.c | 2 +- .../gpu/drm/amd/display/dc/dce/dce_clock_source.c | 6 +++--- .../gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c | 8 ++++---- drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb_scl.c | 4 ++-- .../gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c | 4 ++-- drivers/gpu/drm/amd/display/dc/dml/dcn10/dcn10_fpu.c | 2 +- .../drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 12 ++++++------ .../amd/display/dc/irq/dce110/irq_service_dce110.c | 10 +++++----- .../amd/display/dc/irq/dce120/irq_service_dce120.c | 2 +- .../drm/amd/display/dc/irq/dce80/irq_service_dce80.c | 10 +++++----- .../drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c | 2 +- .../drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c | 2 +- .../drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c | 2 +- .../drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c | 2 +- .../amd/display/dc/irq/dcn302/irq_service_dcn302.c | 4 ++-- .../amd/display/dc/irq/dcn303/irq_service_dcn303.c | 2 +- .../drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c | 4 ++-- .../amd/display/dc/irq/dcn314/irq_service_dcn314.c | 4 ++-- .../amd/display/dc/irq/dcn315/irq_service_dcn315.c | 4 ++-- .../drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c | 4 ++-- .../drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c | 4 ++-- .../amd/display/dc/irq/dcn351/irq_service_dcn351.c | 4 ++-- .../drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c | 4 ++-- .../amd/display/dc/irq/dcn401/irq_service_dcn401.c | 4 ++-- .../drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c | 4 ++-- drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c | 2 +- .../amd/display/dc/resource/dce100/dce100_resource.c | 4 ++-- .../amd/display/dc/resource/dce112/dce112_resource.c | 2 +- .../amd/display/dc/resource/dce120/dce120_resource.c | 2 +- .../amd/display/dc/resource/dce80/dce80_resource.c | 6 +++--- .../amd/display/dc/resource/dcn10/dcn10_resource.c | 2 +- .../amd/display/dc/resource/dcn20/dcn20_resource.c | 2 +- .../amd/display/dc/resource/dcn21/dcn21_resource.c | 2 +- .../amd/display/dc/resource/dcn30/dcn30_resource.c | 2 +- .../amd/display/dc/resource/dcn301/dcn301_resource.c | 2 +- .../amd/display/dc/resource/dcn302/dcn302_resource.c | 2 +- .../amd/display/dc/resource/dcn303/dcn303_resource.c | 2 +- .../amd/display/dc/resource/dcn31/dcn31_resource.c | 2 +- .../amd/display/dc/resource/dcn314/dcn314_resource.c | 2 +- .../amd/display/dc/resource/dcn315/dcn315_resource.c | 2 +- .../amd/display/dc/resource/dcn316/dcn316_resource.c | 2 +- .../amd/display/dc/resource/dcn32/dcn32_resource.c | 2 +- .../amd/display/dc/resource/dcn321/dcn321_resource.c | 2 +- .../amd/display/dc/resource/dcn35/dcn35_resource.c | 2 +- .../amd/display/dc/resource/dcn351/dcn351_resource.c | 2 +- .../amd/display/dc/resource/dcn36/dcn36_resource.c | 2 +- .../amd/display/dc/resource/dcn401/dcn401_resource.c | 2 +- .../amd/display/dc/resource/dcn42/dcn42_resource.c | 2 +- 50 files changed, 86 insertions(+), 86 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c b/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c index 8c54c02a0e26..f37a43f4172e 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c @@ -2010,10 +2010,10 @@ static void calculate_bandwidth( } /*output link bit per pixel supported*/ for (k = 0; k <= maximum_number_of_surfaces - 1; k++) { - data->output_bpphdmi[k] = bw_def_na; - data->output_bppdp4_lane_hbr[k] = bw_def_na; - data->output_bppdp4_lane_hbr2[k] = bw_def_na; - data->output_bppdp4_lane_hbr3[k] = bw_def_na; + data->output_bpphdmi[k] = (uint32_t)bw_def_na; + data->output_bppdp4_lane_hbr[k] = (uint32_t)bw_def_na; + data->output_bppdp4_lane_hbr2[k] = (uint32_t)bw_def_na; + data->output_bppdp4_lane_hbr3[k] = (uint32_t)bw_def_na; if (data->enable[k]) { data->output_bpphdmi[k] = bw_fixed_to_int(bw_mul(bw_div(bw_min2(bw_int_to_fixed(600), data->max_phyclk), data->pixel_rate[k]), bw_int_to_fixed(24))); if (bw_meq(data->max_phyclk, bw_int_to_fixed(270))) { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c index d50b9440210e..cd4c45516616 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c @@ -92,7 +92,7 @@ static int determine_sclk_from_bounding_box( uint32_t dce110_get_min_vblank_time_us(const struct dc_state *context) { uint8_t j; - uint32_t min_vertical_blank_time = -1; + uint32_t min_vertical_blank_time = (uint32_t)-1; for (j = 0; j < context->stream_count; j++) { struct dc_stream_state *stream = context->streams[j]; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 727bcf08a84f..e95d5b269738 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -5240,7 +5240,7 @@ unsigned int resource_pixel_format_to_bpp(enum surface_pixel_format format) return 64; default: ASSERT_CRITICAL(false); - return -1; + return UINT_MAX; } } static unsigned int get_max_audio_sample_rate(struct audio_mode *modes) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 5722be965422..0791b9144b00 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -610,7 +610,7 @@ static uint32_t dce112_get_pix_clk_dividers( || pix_clk_params->requested_pix_clk_100hz == 0) { DC_LOG_ERROR( "%s: Invalid parameters!!\n", __func__); - return -1; + return (uint32_t)-1; } memset(pll_settings, 0, sizeof(*pll_settings)); @@ -621,7 +621,7 @@ static uint32_t dce112_get_pix_clk_dividers( pll_settings->calculated_pix_clk_100hz = clk_src->ext_clk_khz * 10; pll_settings->actual_pix_clk_100hz = pix_clk_params->requested_pix_clk_100hz; - return -1; + return (uint32_t)-1; } dce112_get_pix_clk_dividers_helper(clk_src, @@ -1376,7 +1376,7 @@ static uint32_t dcn3_get_pix_clk_dividers( || pix_clk_params->requested_pix_clk_100hz == 0) { DC_LOG_ERROR( "%s: Invalid parameters!!\n", __func__); - return -1; + return UINT_MAX; } memset(pll_settings, 0, sizeof(*pll_settings)); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c index dcd2cdfe91eb..c702a30563f9 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c @@ -381,10 +381,10 @@ bool cm_helper_translate_curve_to_hw_format(struct dc_context *ctx, } for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++) - seg_distr[i] = -1; + seg_distr[i] = (uint32_t)-1; for (k = 0; k < MAX_REGIONS_NUMBER; k++) { - if (seg_distr[k] != -1) + if (seg_distr[k] != (uint32_t)-1) hw_points += (1 << seg_distr[k]); } @@ -565,7 +565,7 @@ bool cm_helper_translate_curve_to_degamma_hw_format( for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++) - seg_distr[i] = -1; + seg_distr[i] = (uint32_t)-1; /* 12 segments * segments are from 2^-12 to 0 */ @@ -573,7 +573,7 @@ bool cm_helper_translate_curve_to_degamma_hw_format( seg_distr[i] = 4; for (k = 0; k < MAX_REGIONS_NUMBER; k++) { - if (seg_distr[k] != -1) + if (seg_distr[k] != (uint32_t)-1) hw_points += (1 << seg_distr[k]); } diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb_scl.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb_scl.c index a0d437f0ce2b..f73c5f42ea68 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb_scl.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb_scl.c @@ -746,7 +746,7 @@ bool dwb_program_horz_scalar(struct dcn20_dwbc *dwbc20, src_width, dest_width); if (dc_fixpt_floor(tmp_h_ratio_luma) == 8) - h_ratio_luma = -1; + h_ratio_luma = (uint32_t)-1; else h_ratio_luma = dc_fixpt_u3d19(tmp_h_ratio_luma) << 5; @@ -824,7 +824,7 @@ bool dwb_program_vert_scalar(struct dcn20_dwbc *dwbc20, src_height, dest_height); if (dc_fixpt_floor(tmp_v_ratio_luma) == 8) - v_ratio_luma = -1; + v_ratio_luma = (uint32_t)-1; else v_ratio_luma = dc_fixpt_u3d19(tmp_v_ratio_luma) << 5; diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c index 227aa8672d17..9dbccf58dde5 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c @@ -159,10 +159,10 @@ bool cm3_helper_translate_curve_to_hw_format(struct dc_context *ctx, } for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++) - seg_distr[i] = -1; + seg_distr[i] = (uint32_t)-1; for (k = 0; k < MAX_REGIONS_NUMBER; k++) { - if (seg_distr[k] != -1) + if (seg_distr[k] != (uint32_t)-1) hw_points += (1 << seg_distr[k]); } diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn10/dcn10_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn10/dcn10_fpu.c index c5e84190c17a..5679b79d6f53 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn10/dcn10_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn10/dcn10_fpu.c @@ -76,7 +76,7 @@ struct _vcs_dpi_ip_params_st dcn1_0_ip = { .line_buffer_size_bits = 589824, .max_line_buffer_lines = 12, .IsLineBufferBppFixed = 0, - .LineBufferFixedBpp = -1, + .LineBufferFixedBpp = (unsigned int)-1, .writeback_luma_buffer_size_kbytes = 12, .writeback_chroma_buffer_size_kbytes = 8, .max_num_dpp = 4, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index d2025779d036..e4bd6089026b 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -488,15 +488,15 @@ dce110_translate_regamma_to_hw_format(const struct dc_transfer_func *output_tf, seg_distr[8] = 4; seg_distr[9] = 4; seg_distr[10] = 0; - seg_distr[11] = -1; - seg_distr[12] = -1; - seg_distr[13] = -1; - seg_distr[14] = -1; - seg_distr[15] = -1; + seg_distr[11] = (uint32_t)-1; + seg_distr[12] = (uint32_t)-1; + seg_distr[13] = (uint32_t)-1; + seg_distr[14] = (uint32_t)-1; + seg_distr[15] = (uint32_t)-1; } for (k = 0; k < 16; k++) { - if (seg_distr[k] != -1) + if (seg_distr[k] != (uint32_t)-1) hw_points += (1 << seg_distr[k]); } diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c index 676df39079fc..002b09740fc3 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c @@ -92,7 +92,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { .enable_mask = DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK,\ .enable_value = {\ DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK,\ - ~DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK\ + (uint32_t)~DC_HPD_INT_CONTROL__DC_HPD_INT_EN_MASK\ },\ .ack_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ .ack_mask = DC_HPD_INT_CONTROL__DC_HPD_INT_ACK_MASK,\ @@ -107,7 +107,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { .enable_mask = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK,\ .enable_value = {\ DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK,\ - ~DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK },\ + (uint32_t)~DC_HPD_INT_CONTROL__DC_HPD_RX_INT_EN_MASK },\ .ack_reg = mmHPD ## reg_num ## _DC_HPD_INT_CONTROL,\ .ack_mask = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_ACK_MASK,\ .ack_value = DC_HPD_INT_CONTROL__DC_HPD_RX_INT_ACK_MASK,\ @@ -121,7 +121,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ .enable_value = {\ GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ - ~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK},\ + (uint32_t)~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK},\ .ack_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_STATUS,\ .ack_mask = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ .ack_value = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ @@ -136,7 +136,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ .enable_value = {\ CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ - ~CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK},\ + (uint32_t)~CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK},\ .ack_reg = mmCRTC ## reg_num ## _CRTC_V_UPDATE_INT_STATUS,\ .ack_mask =\ CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ @@ -152,7 +152,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ .enable_value = {\ CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ - ~CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK},\ + (uint32_t)~CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK},\ .ack_reg = mmCRTC ## reg_num ## _CRTC_VERTICAL_INTERRUPT0_CONTROL,\ .ack_mask =\ CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_CLEAR_MASK,\ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c b/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c index b473dae2abbb..dbab6e3737a1 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c @@ -79,7 +79,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c b/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c index b5c5f42cf8f2..3e19dfdd0474 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c @@ -68,7 +68,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { .enable_mask = DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK,\ .enable_value = {\ DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK,\ - ~DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK\ + (uint32_t)~DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK\ },\ .ack_reg = mmDC_HPD ## reg_num ## _INT_CONTROL,\ .ack_mask = DC_HPD1_INT_CONTROL__DC_HPD1_INT_ACK_MASK,\ @@ -83,7 +83,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { .enable_mask = DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_EN_MASK,\ .enable_value = {\ DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_EN_MASK,\ - ~DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_EN_MASK },\ + (uint32_t)~DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_EN_MASK },\ .ack_reg = mmDC_HPD ## reg_num ## _INT_CONTROL,\ .ack_mask = DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_ACK_MASK,\ .ack_value = DC_HPD1_INT_CONTROL__DC_HPD1_RX_INT_ACK_MASK,\ @@ -98,7 +98,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ .enable_value = {\ GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK,\ - ~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK},\ + (uint32_t)~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK},\ .ack_reg = mmDCP ## reg_num ## _GRPH_INTERRUPT_STATUS,\ .ack_mask = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ .ack_value = GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK,\ @@ -113,7 +113,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ .enable_value = {\ CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK,\ - ~CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK},\ + (uint32_t)~CRTC_INTERRUPT_CONTROL__CRTC_V_UPDATE_INT_MSK_MASK},\ .ack_reg = mmCRTC ## reg_num ## _CRTC_V_UPDATE_INT_STATUS,\ .ack_mask =\ CRTC_V_UPDATE_INT_STATUS__CRTC_V_UPDATE_INT_CLEAR_MASK,\ @@ -129,7 +129,7 @@ static struct irq_source_info_funcs vupdate_irq_info_funcs = { CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ .enable_value = {\ CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK,\ - ~CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK},\ + (uint32_t)~CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_INT_ENABLE_MASK},\ .ack_reg = mmCRTC ## reg_num ## _CRTC_VERTICAL_INTERRUPT0_CONTROL,\ .ack_mask =\ CRTC_VERTICAL_INTERRUPT0_CONTROL__CRTC_VERTICAL_INTERRUPT0_CLEAR_MASK,\ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c index ca2e13702fbb..113bd76c95db 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c @@ -176,7 +176,7 @@ static struct irq_source_info_funcs vupdate_no_lock_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c b/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c index 1c4c51abc259..98eedcac1247 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c @@ -179,7 +179,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c b/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c index 9e0881472e38..be02ca2861b3 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c @@ -189,7 +189,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c b/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c index 92bcd35723ca..fe830a55f320 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c @@ -196,7 +196,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c b/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c index 16685d066c1a..d77d51ed5717 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c @@ -180,7 +180,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { .enable_mask = block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ @@ -199,7 +199,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ reg1 ## __ ## mask1 ## _MASK,\ - ~reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI_DMUB(reg2),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c b/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c index 01d83e1922d6..afe3d7d4a56f 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c @@ -123,7 +123,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { .enable_mask = block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c b/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c index 2114c5669e6e..5c86e950adfd 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c @@ -184,7 +184,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ @@ -198,7 +198,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ reg1 ## __ ## mask1 ## _MASK,\ - ~reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI_DMUB(reg2),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c b/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c index 16f158e0fb60..34aa7a004454 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c @@ -186,7 +186,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ @@ -200,7 +200,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ reg1 ## __ ## mask1 ## _MASK,\ - ~reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI_DMUB(reg2),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c b/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c index 8ee03c006ad6..f63990a6c6c4 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c @@ -191,7 +191,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ @@ -205,7 +205,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ reg1 ## __ ## mask1 ## _MASK,\ - ~reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI_DMUB(reg2),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c b/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c index 07e6f7dd6b99..5d4d5ed0589c 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c @@ -195,7 +195,7 @@ static struct irq_source_info_funcs vline2_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ @@ -209,7 +209,7 @@ static struct irq_source_info_funcs vline2_irq_info_funcs = { reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ reg1 ## __ ## mask1 ## _MASK,\ - ~reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI_DMUB(reg2),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c b/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c index 3d28a5007f53..05aeb6ed676e 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c @@ -184,7 +184,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { REG_STRUCT[base + reg_num].enable_value[0] = \ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ REG_STRUCT[base + reg_num].enable_value[1] = \ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK, \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK, \ REG_STRUCT[base + reg_num].ack_reg = SRI(reg2, block, reg_num),\ REG_STRUCT[base + reg_num].ack_mask = \ block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ @@ -198,7 +198,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { REG_STRUCT[base].enable_value[0] = \ reg1 ## __ ## mask1 ## _MASK,\ REG_STRUCT[base].enable_value[1] = \ - ~reg1 ## __ ## mask1 ## _MASK, \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK, \ REG_STRUCT[base].ack_reg = SRI_DMUB(reg2),\ REG_STRUCT[base].ack_mask = \ reg2 ## __ ## mask2 ## _MASK,\ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c b/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c index f716c2590876..9d835b6ffe1c 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c @@ -163,7 +163,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { REG_STRUCT[base + reg_num].enable_value[0] = \ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ REG_STRUCT[base + reg_num].enable_value[1] = \ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK, \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK, \ REG_STRUCT[base + reg_num].ack_reg = SRI(reg2, block, reg_num),\ REG_STRUCT[base + reg_num].ack_mask = \ block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ @@ -177,7 +177,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { REG_STRUCT[base].enable_value[0] = \ reg1 ## __ ## mask1 ## _MASK,\ REG_STRUCT[base].enable_value[1] = \ - ~reg1 ## __ ## mask1 ## _MASK, \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK, \ REG_STRUCT[base].ack_reg = SRI_DMUB(reg2),\ REG_STRUCT[base].ack_mask = \ reg2 ## __ ## mask2 ## _MASK,\ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c b/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c index e718004901cf..3da9f01dd511 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c @@ -162,7 +162,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { REG_STRUCT[base + reg_num].enable_value[0] = \ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ REG_STRUCT[base + reg_num].enable_value[1] = \ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK, \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK, \ REG_STRUCT[base + reg_num].ack_reg = SRI(reg2, block, reg_num),\ REG_STRUCT[base + reg_num].ack_mask = \ block ## reg_num ## _ ## reg2 ## __ ## mask2 ## _MASK,\ @@ -176,7 +176,7 @@ static struct irq_source_info_funcs vline0_irq_info_funcs = { REG_STRUCT[base].enable_value[0] = \ reg1 ## __ ## mask1 ## _MASK,\ REG_STRUCT[base].enable_value[1] = \ - ~reg1 ## __ ## mask1 ## _MASK, \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK, \ REG_STRUCT[base].ack_reg = SRI_DMUB(reg2),\ REG_STRUCT[base].ack_mask = \ reg2 ## __ ## mask2 ## _MASK,\ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c b/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c index 2cde50b2ae22..a12bb3cc4c43 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c @@ -175,7 +175,7 @@ static struct irq_source_info_funcs vline2_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ @@ -189,7 +189,7 @@ static struct irq_source_info_funcs vline2_irq_info_funcs = { reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ reg1 ## __ ## mask1 ## _MASK,\ - ~reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI_DMUB(reg2),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c b/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c index 19e0741c62cd..bdf733d37a76 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c @@ -173,7 +173,7 @@ static struct irq_source_info_funcs vline2_irq_info_funcs = { block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK,\ - ~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~block ## reg_num ## _ ## reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI(reg2, block, reg_num),\ .ack_mask = \ @@ -187,7 +187,7 @@ static struct irq_source_info_funcs vline2_irq_info_funcs = { reg1 ## __ ## mask1 ## _MASK,\ .enable_value = {\ reg1 ## __ ## mask1 ## _MASK,\ - ~reg1 ## __ ## mask1 ## _MASK \ + (uint32_t)~reg1 ## __ ## mask1 ## _MASK \ },\ .ack_reg = SRI_DMUB(reg2),\ .ack_mask = \ diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c index 6bfd2c1294e5..8b398b9a2b6b 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c @@ -1428,7 +1428,7 @@ uint32_t mpcc3_acquire_rmu(struct mpc *mpc, int mpcc_id, int rmu_idx) } //no vacant RMU units or invalid parameters acquire_post_bldn_3dlut - return -1; + return (uint32_t)-1; } static int mpcc3_release_rmu(struct mpc *mpc, int mpcc_id) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c index fdcf8db6be50..d83a6bed2ee0 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c @@ -1039,7 +1039,7 @@ static bool dce100_resource_construct( pool->base.res_cap = &res_cap; pool->base.funcs = &dce100_res_pool_funcs; - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; bp = ctx->dc_bios; @@ -1111,7 +1111,7 @@ static bool dce100_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = res_cap.num_timing_generator; pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 200; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c index b7051bfd4326..85af37c9d922 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c @@ -1240,7 +1240,7 @@ static bool dce112_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 200; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c index 7ee70f7b3aa7..7d5c7dacaf05 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c @@ -1081,7 +1081,7 @@ static bool dce120_resource_construct( /* TODO: Fill more data from GreenlandAsicCapability.cpp */ pool->base.pipe_count = res_cap.num_timing_generator; pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; dc->caps.max_downscale_ratio = 200; dc->caps.i2c_speed_in_khz = 100; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c index 89927727a0d9..fb18312554c7 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c @@ -934,7 +934,7 @@ static bool dce80_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = res_cap.num_timing_generator; pool->base.timing_generator_count = res_cap.num_timing_generator; dc->caps.max_downscale_ratio = 200; @@ -1137,7 +1137,7 @@ static bool dce81_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = res_cap_81.num_timing_generator; pool->base.timing_generator_count = res_cap_81.num_timing_generator; dc->caps.max_downscale_ratio = 200; @@ -1337,7 +1337,7 @@ static bool dce83_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = res_cap_83.num_timing_generator; pool->base.timing_generator_count = res_cap_83.num_timing_generator; dc->caps.max_downscale_ratio = 200; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c index 44178e915bdc..cd4d703e1018 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c @@ -1346,7 +1346,7 @@ static bool dcn10_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; /* max pipe num for ASIC before check pipe fuses */ pool->base.pipe_count = pool->base.res_cap->num_timing_generator; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c index b50a2509463e..5ba67e3c2f8f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c @@ -2429,7 +2429,7 @@ static bool dcn20_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; dc->caps.max_downscale_ratio = 200; dc->caps.i2c_speed_in_khz = 100; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c index f7f75604ef33..3a5dc8ca1457 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c @@ -1408,7 +1408,7 @@ static bool dcn21_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; /* max pipe num for ASIC before check pipe fuses */ pool->base.pipe_count = pool->base.res_cap->num_timing_generator; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 90223b7c2fcd..8468c0fe3737 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -2297,7 +2297,7 @@ static bool dcn30_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c index d21b928055e5..0a110be2b9da 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c @@ -1428,7 +1428,7 @@ static bool dcn301_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index d24b9b81df77..0b2fc8464ef7 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -1218,7 +1218,7 @@ static bool dcn302_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->pipe_count = pool->res_cap->num_timing_generator; pool->mpcc_count = pool->res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index 0b44a33a0d32..a5000134cd97 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -1159,7 +1159,7 @@ static bool dcn303_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->pipe_count = pool->res_cap->num_timing_generator; pool->mpcc_count = pool->res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 428524f2ede6..55a11d61a2aa 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -1894,7 +1894,7 @@ static bool dcn31_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index 795de8f65117..b74a167ae5f7 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -1827,7 +1827,7 @@ static bool dcn314_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index 4db684fbc217..d69c18872b53 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -1866,7 +1866,7 @@ static bool dcn315_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; /* Enable 4to1MPC by default */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index db94141d113f..c20521d0dd1e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -1741,7 +1741,7 @@ static bool dcn316_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index 2b9d8d224572..3c0d046ab747 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -2191,7 +2191,7 @@ static bool dcn32_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.timing_generator_count = num_pipes; pool->base.pipe_count = num_pipes; pool->base.mpcc_count = num_pipes; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index e3dc4b1aacda..b8ae6e8397ef 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -1695,7 +1695,7 @@ static bool dcn321_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.timing_generator_count = num_pipes; pool->base.pipe_count = num_pipes; pool->base.mpcc_count = num_pipes; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 5118bec38d32..f4a751027065 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -1850,7 +1850,7 @@ static bool dcn35_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index b64ad2d9fc2f..bf8e83db9cc6 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -1823,7 +1823,7 @@ static bool dcn351_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index 1ad44fb64213..fec0911ce22c 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -1826,7 +1826,7 @@ static bool dcn36_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.pipe_count = pool->base.res_cap->num_timing_generator; pool->base.mpcc_count = pool->base.res_cap->num_timing_generator; dc->caps.max_downscale_ratio = 600; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c index 491860cc8378..dc0f0ab27ce0 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c @@ -1915,7 +1915,7 @@ static bool dcn401_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.timing_generator_count = num_pipes; pool->base.pipe_count = num_pipes; pool->base.mpcc_count = num_pipes; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 6328b3dc35f9..11b302c4d06f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -1864,7 +1864,7 @@ static bool dcn42_resource_construct( /************************************************* * Resource + asic cap harcoding * *************************************************/ - pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.underlay_pipe_index = (unsigned int)NO_UNDERLAY_PIPE; pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; pool->base.pipe_count = num_pipes; pool->base.mpcc_count = num_pipes; From 5efcf63aa63809b96b6d3230f1151b07e7f5120e Mon Sep 17 00:00:00 2001 From: Clay King Date: Mon, 16 Mar 2026 17:37:31 -0400 Subject: [PATCH 453/712] drm/amd/display: Fix silence signed/unsigned mismatch warnings in dml [Why & How] Fix signed/unsigned mismatch warnings by using the same signedness for a given value Reviewed-by: Austin Zheng Signed-off-by: Clay King Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h | 1 + .../drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c | 10 +++++----- .../amd/display/dc/dml2_0/dml2_translation_helper.c | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h index 42aea81fb61c..9568ca06f00f 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h @@ -74,6 +74,7 @@ unsigned int dcn42_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type bool dcn42_is_smu_present(struct clk_mgr *clk_mgr_base); bool dcn42_has_active_display(struct dc *dc, const struct dc_state *context); int dcn42_get_active_display_cnt_wa(struct dc *dc, struct dc_state *context, int *all_active_disps); +bool dcn42_has_active_display(struct dc *dc, const struct dc_state *context); void dcn42_update_clocks_update_dpp_dto(struct clk_mgr_internal *clk_mgr, struct dc_state *context, bool safe_to_lower); void dcn42_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, struct dc_state *context, int ref_dtbclk_khz); bool dcn42_is_spll_ssc_enabled(struct clk_mgr *clk_mgr_base); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c index fd3c61509f1b..40f2f1ebab3a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c @@ -35,7 +35,7 @@ #define MAX_MPCC_FACTOR 4 struct dc_plane_pipe_pool { - int pipes_assigned_to_plane[MAX_ODM_FACTOR][MAX_MPCC_FACTOR]; + unsigned int pipes_assigned_to_plane[MAX_ODM_FACTOR][MAX_MPCC_FACTOR]; bool pipe_used[MAX_ODM_FACTOR][MAX_MPCC_FACTOR]; int num_pipes_assigned_to_plane_for_mpcc_combine; int num_pipes_assigned_to_plane_for_odm_combine; @@ -340,8 +340,8 @@ static bool is_pipe_in_candidate_array(const unsigned int pipe_idx, static bool find_more_pipes_for_stream(struct dml2_context *ctx, struct dc_state *state, // The state we want to find a free mapping in unsigned int stream_id, // The stream we want this pipe to drive - int *assigned_pipes, - int *assigned_pipe_count, + unsigned int *assigned_pipes, + unsigned int *assigned_pipe_count, int pipes_needed, const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to { @@ -406,8 +406,8 @@ static bool find_more_pipes_for_stream(struct dml2_context *ctx, static bool find_more_free_pipes(struct dml2_context *ctx, struct dc_state *state, // The state we want to find a free mapping in unsigned int stream_id, // The stream we want this pipe to drive - int *assigned_pipes, - int *assigned_pipe_count, + unsigned int *assigned_pipes, + unsigned int *assigned_pipe_count, int pipes_needed, const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c index e25b88f2d6b9..57f45b27de1d 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c @@ -330,8 +330,8 @@ void dml2_init_soc_states(struct dml2_context *dml2, const struct dc *in_dc, { struct dml2_policy_build_synthetic_soc_states_scratch *s = &dml2->v20.scratch.create_scratch.build_synthetic_socbb_scratch; struct dml2_policy_build_synthetic_soc_states_params *p = &dml2->v20.scratch.build_synthetic_socbb_params; - unsigned int dcfclk_stas_mhz[NUM_DCFCLK_STAS] = {0}; - unsigned int dcfclk_stas_mhz_new[NUM_DCFCLK_STAS_NEW] = {0}; + int dcfclk_stas_mhz[NUM_DCFCLK_STAS] = {0}; + int dcfclk_stas_mhz_new[NUM_DCFCLK_STAS_NEW] = {0}; unsigned int dml_project = dml2->v20.dml_core_ctx.project; unsigned int i = 0; From 096bff6706eb105954bb45f8e8f3e37f05d10d0e Mon Sep 17 00:00:00 2001 From: Lincheng Ku Date: Wed, 18 Mar 2026 03:58:35 +0800 Subject: [PATCH 454/712] drm/amd/dc: Add link output control for DPIA [Why] To support specific sequencing requirements for DPIA link output [How] Implement the dpia_link_hwss structure and define the necessary control function pointers. The initialization order is aligned with the core link_hwss definition to ensure consistency Reviewed-by: Wenjing Liu Signed-off-by: Lincheng Ku Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../amd/display/dc/link/hwss/link_hwss_dpia.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c index 81bf3c5e1fdf..5d708039c7cf 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c @@ -145,13 +145,9 @@ static void disable_dpia_link_output(struct dc_link *link, } static const struct link_hwss dpia_link_hwss = { - .setup_stream_encoder = setup_dio_stream_encoder, - .reset_stream_encoder = reset_dio_stream_encoder, - .setup_stream_attribute = setup_dio_stream_attribute, - .disable_link_output = disable_dpia_link_output, - .setup_audio_output = setup_dio_audio_output, - .enable_audio_packet = enable_dio_audio_packet, - .disable_audio_packet = disable_dio_audio_packet, + /* Ensure initialization order matches the declaration in link_hwss.h + * for strict compiler compliance and consistency across HWSS implementations + */ .ext = { .set_throttled_vcp_size = set_dio_throttled_vcp_size, .enable_dp_link_output = enable_dpia_link_output, @@ -159,6 +155,14 @@ static const struct link_hwss dpia_link_hwss = { .set_dp_lane_settings = set_dio_dpia_lane_settings, .update_stream_allocation_table = update_dpia_stream_allocation_table, }, + + .setup_stream_encoder = setup_dio_stream_encoder, + .reset_stream_encoder = reset_dio_stream_encoder, + .setup_stream_attribute = setup_dio_stream_attribute, + .disable_link_output = disable_dpia_link_output, + .setup_audio_output = setup_dio_audio_output, + .enable_audio_packet = enable_dio_audio_packet, + .disable_audio_packet = disable_dio_audio_packet, }; bool can_use_dpia_link_hwss(const struct dc_link *link, From 724cf8ae6353c47ef762efd1342f0eb447e16e61 Mon Sep 17 00:00:00 2001 From: Clay King Date: Tue, 17 Mar 2026 15:17:48 -0400 Subject: [PATCH 455/712] drm/amd/display: Fixed silence signed/unsigned mismatch warnings Fix compiler warnings by consistently use the same signedness for a given value Reviewed-by: Joshua Aberback Signed-off-by: Clay King Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c | 4 ++-- drivers/gpu/drm/amd/display/dc/core/dc.c | 9 +++------ drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c | 4 ++-- drivers/gpu/drm/amd/display/dc/dc.h | 2 +- drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c | 3 +-- drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c | 2 +- .../gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 2 +- drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 2 +- drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 2 +- drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 2 +- drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c | 4 ++-- .../gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 2 +- drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 4 ++-- 13 files changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c index 5a5249f3ffbd..2efa962eecfe 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c @@ -97,7 +97,7 @@ void clk_mgr_exit_optimized_pwr_state(const struct dc *dc, struct clk_mgr *clk_m { struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link = NULL; - int edp_num; + unsigned int edp_num; unsigned int panel_inst; dc_get_edp_links(dc, edp_links, &edp_num); @@ -123,7 +123,7 @@ void clk_mgr_optimize_pwr_state(const struct dc *dc, struct clk_mgr *clk_mgr) { struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link = NULL; - int edp_num; + unsigned int edp_num; unsigned int panel_inst; dc_get_edp_links(dc, edp_links, &edp_num); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index b8d2cac39742..f1a336cff929 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1563,8 +1563,7 @@ static void detect_edp_presence(struct dc *dc) struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link = NULL; enum dc_connection_type type; - int i; - int edp_num; + unsigned int i, edp_num; dc_get_edp_links(dc, edp_links, &edp_num); if (!edp_num) @@ -6264,8 +6263,7 @@ void dc_disable_accelerated_mode(struct dc *dc) */ void dc_notify_vsync_int_state(struct dc *dc, struct dc_stream_state *stream, bool enable) { - int i; - int edp_num; + unsigned int i, edp_num; struct pipe_ctx *pipe = NULL; struct dc_link *link = stream->sink->link; struct dc_link *edp_links[MAX_NUM_EDP]; @@ -6319,8 +6317,7 @@ bool dc_abm_save_restore( struct dc_stream_state *stream, struct abm_save_restore *pData) { - int i; - int edp_num; + unsigned int i, edp_num; struct pipe_ctx *pipe = NULL; struct dc_link *link = stream->sink->link; struct dc_link *edp_links[MAX_NUM_EDP]; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c index 7bb4504889be..f4e99ca7918f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c @@ -46,7 +46,7 @@ struct dc_link *dc_get_link_at_index(struct dc *dc, uint32_t link_index) void dc_get_edp_links(const struct dc *dc, struct dc_link **edp_links, - int *edp_num) + unsigned int *edp_num) { int i; @@ -68,7 +68,7 @@ bool dc_get_edp_link_panel_inst(const struct dc *dc, unsigned int *inst_out) { struct dc_link *edp_links[MAX_NUM_EDP]; - int edp_num, i; + unsigned int edp_num, i; *inst_out = 0; if (link->connector_signal != SIGNAL_TYPE_EDP) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 41b5dedb6eff..48b523fabbbe 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -2062,7 +2062,7 @@ bool dc_get_edp_link_panel_inst(const struct dc *dc, /* Return an array of link pointers to edp links. */ void dc_get_edp_links(const struct dc *dc, struct dc_link **edp_links, - int *edp_num); + unsigned int *edp_num); void dc_set_edp_power(const struct dc *dc, struct dc_link *edp_link, bool powerOn); diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c index b686d89b79b2..967ffdfd6077 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c @@ -41,8 +41,7 @@ static unsigned int abm_feature_support(struct abm *abm, unsigned int panel_inst { struct dc_context *dc = abm->ctx; struct dc_link *edp_links[MAX_NUM_EDP]; - int i; - int edp_num; + unsigned int i, edp_num; unsigned int ret = ABM_FEATURE_NO_SUPPORT; dc_get_edp_links(dc->dc, edp_links, &edp_num); diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c index d0ffa99f1fe0..52673e2f504c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c @@ -82,7 +82,7 @@ bool dmub_hw_lock_mgr_does_link_require_lock(const struct dc *dc, const struct d if (link->psr_settings.psr_version == DC_PSR_VERSION_1) { struct dc_link *edp_links[MAX_NUM_EDP]; - int edp_num; + unsigned int edp_num; dc_get_edp_links(dc, edp_links, &edp_num); if (edp_num == 1) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index e4bd6089026b..8f56f164d567 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -1986,7 +1986,7 @@ void dce110_enable_accelerated_mode(struct dc *dc, struct dc_state *context) struct pipe_ctx *pipe_ctx = NULL; struct dce_hwseq *hws = dc->hwseq; int edp_with_sink_num; - int edp_num; + unsigned int edp_num; int edp_stream_num; int i; bool can_apply_edp_fast_boot = false; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 17ff66d9a617..996ec85f9727 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -1904,7 +1904,7 @@ void dcn10_power_down_on_boot(struct dc *dc) { struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link = NULL; - int edp_num; + unsigned int edp_num; int i = 0; dc_get_edp_links(dc, edp_links, &edp_num); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index d04cfd403b7e..34d4519b3a17 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -645,7 +645,7 @@ void dcn30_init_hw(struct dc *dc) struct dc_bios *dcb = dc->ctx->dc_bios; struct resource_pool *res_pool = dc->res_pool; int i; - int edp_num; + unsigned int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index e5d93dd348dd..ef08c98b11e9 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -793,7 +793,7 @@ void dcn32_init_hw(struct dc *dc) struct dc_bios *dcb = dc->ctx->dc_bios; struct resource_pool *res_pool = dc->res_pool; int i; - int edp_num; + unsigned int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index b5f60f59382e..7c25911089b8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -520,7 +520,7 @@ void dcn35_power_down_on_boot(struct dc *dc) { struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link = NULL; - int edp_num; + unsigned int edp_num; int i = 0; dc_get_edp_links(dc, edp_links, &edp_num); @@ -921,7 +921,7 @@ void dcn35_calc_blocks_to_gate(struct dc *dc, struct dc_state *context, bool hpo_frl_stream_enc_acquired = false; bool hpo_dp_stream_enc_acquired = false; int i = 0, j = 0; - int edp_num = 0; + unsigned int edp_num = 0; struct dc_link *edp_links[MAX_NUM_EDP] = { NULL }; memset(update_state, 0, sizeof(struct pg_block_update)); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index f53ce9d4d8c8..476dee9e722f 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -140,7 +140,7 @@ void dcn401_init_hw(struct dc *dc) struct dc_bios *dcb = dc->ctx->dc_bios; struct resource_pool *res_pool = dc->res_pool; int i; - int edp_num; + unsigned int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; bool dchub_ref_freq_changed; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index 7f9c121c00e6..ba1813eb3c6c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -66,7 +66,7 @@ void dcn42_init_hw(struct dc *dc) struct dc_bios *dcb = dc->ctx->dc_bios; struct resource_pool *res_pool = dc->res_pool; int i; - int edp_num; + unsigned int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; bool dchub_ref_freq_changed; @@ -1063,7 +1063,7 @@ void dcn42_power_down_on_boot(struct dc *dc) { struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link = NULL; - int edp_num; + unsigned int edp_num; int i = 0; dc_get_edp_links(dc, edp_links, &edp_num); From 4d55784b9153f9463fed0be011b61d3e4265a4e4 Mon Sep 17 00:00:00 2001 From: Ovidiu Bunea Date: Wed, 18 Mar 2026 09:23:43 -0400 Subject: [PATCH 456/712] drm/amd/dc: Disable PSR & Replay CRTC disable by default [why & how] Let IPS FSM handle OTG disable. Reviewed-by: Leo Chen Reviewed-by: Nicholas Kazlauskas Signed-off-by: Ovidiu Bunea Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 11b302c4d06f..f2a6e260f061 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -761,6 +761,8 @@ static const struct dc_debug_options debug_defaults_drv = { .ignore_pg = true, .disable_stutter_for_wm_program = true, .min_deep_sleep_dcfclk_khz = 8000, + .replay_skip_crtc_disabled = true, + .psr_skip_crtc_disable = true, }; static const struct dc_check_config config_defaults = { From 246808e7922056594311ad6ca11d31d2d8c27b01 Mon Sep 17 00:00:00 2001 From: Relja Vojvodic Date: Tue, 17 Mar 2026 16:47:07 -0400 Subject: [PATCH 457/712] Revert "drm/amd/display: Rework YCbCr422 DSC policy" Revert commit 19b79e4f2182 ("drm/amd/display: Rework YCbCr422 DSC policy") Reason for Revert: This commit is causing compliance failures Reviewed-by: Wenjing Liu Signed-off-by: Relja Vojvodic Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc.h | 2 +- drivers/gpu/drm/amd/display/dc/dc_dsc.h | 1 - drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 13 +++++++------ .../gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c | 2 +- .../gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c | 2 +- .../gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c | 2 +- .../gpu/drm/amd/display/dc/link/link_detection.c | 11 ++++++----- drivers/gpu/drm/amd/display/dc/link/link_dpms.c | 3 +-- .../amd/display/dc/resource/dcn31/dcn31_resource.c | 2 -- .../display/dc/resource/dcn315/dcn315_resource.c | 2 -- 10 files changed, 18 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 48b523fabbbe..ab7dea9d47f7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -563,7 +563,6 @@ struct dc_config { bool frame_update_cmd_version2; struct spl_sharpness_range dcn_sharpness_range; struct spl_sharpness_range dcn_override_sharpness_range; - bool no_native422_support; }; enum visual_confirm { @@ -988,6 +987,7 @@ struct link_service; * causing an issue or not. */ struct dc_debug_options { + bool native422_support; bool disable_dsc; enum visual_confirm visual_confirm; int visual_confirm_rect_height; diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h b/drivers/gpu/drm/amd/display/dc/dc_dsc.h index 101bce6b8de6..9d18f1c08079 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dsc.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dsc.h @@ -52,7 +52,6 @@ struct dc_dsc_policy { uint32_t max_target_bpp; uint32_t min_target_bpp; bool enable_dsc_when_not_needed; - bool ycbcr422_simple; }; struct dc_dsc_config_options { diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index 8dfb6dd14eb2..5b3584ad5b6b 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -680,6 +680,9 @@ static void get_dsc_enc_caps( } else { build_dsc_enc_caps(dsc, dsc_enc_caps); } + + if (dsc->ctx->dc->debug.native422_support) + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; } /* Returns 'false' if no intersection was found for at least one capability. @@ -1097,14 +1100,13 @@ static bool setup_dsc_config( branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_0_mps; break; case PIXEL_ENCODING_YCBCR422: - if (policy.ycbcr422_simple) { + is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_422; + sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps; + branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps; + if (!is_dsc_possible) { is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_SIMPLE_422; dsc_cfg->ycbcr422_simple = is_dsc_possible; sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps; - } else { - is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_422; - sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps; - branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps; } break; case PIXEL_ENCODING_YCBCR420: @@ -1404,7 +1406,6 @@ void dc_dsc_get_policy_for_timing(const struct dc_crtc_timing *timing, policy->min_target_bpp = 8; /* DP specs limits to 3 x bpc */ policy->max_target_bpp = 3 * bpc; - policy->ycbcr422_simple = true; break; case PIXEL_ENCODING_YCBCR420: /* DP specs limits to 6 */ diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c index 6e1e759462bf..242f1e6f0d8f 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c @@ -100,7 +100,7 @@ void dsc2_get_enc_caps(struct dsc_enc_caps *dsc_enc_caps, int pixel_clock_100Hz) dsc_enc_caps->color_formats.bits.RGB = 1; dsc_enc_caps->color_formats.bits.YCBCR_444 = 1; dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1; - dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 0; dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1; dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c index 17acb64a9d80..e712985f7abd 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c @@ -128,7 +128,7 @@ void dsc35_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsigned int m dsc_enc_caps->color_formats.bits.RGB = 1; dsc_enc_caps->color_formats.bits.YCBCR_444 = 1; dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1; - dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 0; dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1; dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c index bbb8b5b18a4e..c1bdbb38c690 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c @@ -78,7 +78,7 @@ static void dsc401_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsign dsc_enc_caps->color_formats.bits.RGB = 1; dsc_enc_caps->color_formats.bits.YCBCR_444 = 1; dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1; - dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 0; dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1; dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index 7f1761080aba..b761f330311f 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -781,8 +781,10 @@ static void restore_phy_clocks_for_destructive_link_verification(const struct dc } static void verify_link_capability_destructive(struct dc_link *link, + struct dc_sink *sink, enum dc_detect_reason reason) { + (void)sink; bool should_prepare_phy_clocks = should_prepare_phy_clocks_for_link_verification(link->dc, reason); @@ -854,11 +856,11 @@ static bool should_verify_link_capability_destructively(struct dc_link *link, return destrictive; } -static void verify_link_capability(struct dc_link *link, +static void verify_link_capability(struct dc_link *link, struct dc_sink *sink, enum dc_detect_reason reason) { if (should_verify_link_capability_destructively(link, reason)) - verify_link_capability_destructive(link, reason); + verify_link_capability_destructive(link, sink, reason); else verify_link_capability_non_destructive(link); } @@ -1452,9 +1454,8 @@ bool link_detect(struct dc_link *link, enum dc_detect_reason reason) is_local_sink_detect_success = detect_link_and_local_sink(link, reason); - if (is_local_sink_detect_success && link->local_sink) { - verify_link_capability(link, reason); - } + if (is_local_sink_detect_success && link->local_sink) + verify_link_capability(link, link->local_sink, reason); DC_LOG_DC("%s: link_index=%d is_local_sink_detect_success=%d pre_link_type=%d link_type=%d\n", __func__, link->link_index, is_local_sink_detect_success, pre_link_type, link->type); diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index e12c25896364..b4f46408a000 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -181,8 +181,7 @@ void link_set_all_streams_dpms_off_for_link(struct dc_link *link) /* link can be also enabled by vbios. In this case it is not recorded * in pipe_ctx. Disable link phy here to make sure it is completely off */ - if (dc_is_dp_signal(link->connector_signal)) - dp_disable_link_phy(link, &link_res, link->connector_signal); + dp_disable_link_phy(link, &link_res, link->connector_signal); } void link_resume(struct dc_link *link) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 55a11d61a2aa..4920bc136282 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -1963,8 +1963,6 @@ static bool dcn31_resource_construct( dc->config.use_pipe_ctx_sync_logic = true; dc->config.disable_hbr_audio_dp2 = true; - dc->config.no_native422_support = true; - /* read VBIOS LTTPR caps */ { if (ctx->dc_bios->funcs->get_lttpr_caps) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index d69c18872b53..d9818bc2dfdb 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -1926,8 +1926,6 @@ static bool dcn315_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; - dc->config.no_native422_support = true; - /* read VBIOS LTTPR caps */ { if (ctx->dc_bios->funcs->get_lttpr_caps) { From add9aee8d889a7ffbff9d5ddbf1f5472a7cbfb82 Mon Sep 17 00:00:00 2001 From: Mohit Bawa Date: Wed, 5 Nov 2025 13:58:35 -0500 Subject: [PATCH 458/712] drm/amd/display: enable eDP DSC seamless boot support [Why] VBIOS supports DSC for seamless boot on newer hardware. Reading hardware state allows proper DSC validation without breaking existing boot display. [What] Remove DSC block for boot timing validation and implement hardware state reading to populate DSC configuration from VBIOS-configured state. Enhance dsc_read_state function in DCN401 to read additional DSC parameters. Reviewed-by: Yihan Zhu Signed-off-by: Mohit Bawa Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 73 ++++++++++++++++++- .../amd/display/dc/dsc/dcn401/dcn401_dsc.c | 5 ++ drivers/gpu/drm/amd/display/dc/dsc/dsc.h | 5 ++ 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index f1a336cff929..a62b03c5fa0f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1922,10 +1922,77 @@ bool dc_validate_boot_timing(const struct dc *dc, return false; } - /* block DSC for now, as VBIOS does not currently support DSC timings */ if (crtc_timing->flags.DSC) { - DC_LOG_DEBUG("boot timing validation failed due to DSC\n"); - return false; + struct display_stream_compressor *dsc = NULL; + struct dcn_dsc_state dsc_state = {0}; + + /* Find DSC associated with this timing generator */ + if (tg_inst < dc->res_pool->res_cap->num_dsc) { + dsc = dc->res_pool->dscs[tg_inst]; + } + + if (!dsc || !dsc->funcs->dsc_read_state) { + DC_LOG_DEBUG("boot timing validation failed due to no DSC resource or read function\n"); + return false; + } + + /* Read current DSC hardware state */ + dsc->funcs->dsc_read_state(dsc, &dsc_state); + + /* Check if DSC is actually enabled in hardware */ + if (dsc_state.dsc_clock_en == 0) { + DC_LOG_DEBUG("boot timing validation failed due to DSC not enabled in hardware\n"); + return false; + } + + uint32_t num_slices_h = 0; + uint32_t num_slices_v = 0; + + if (dsc_state.dsc_slice_width > 0) { + num_slices_h = (crtc_timing->h_addressable + dsc_state.dsc_slice_width - 1) / dsc_state.dsc_slice_width; + } + + if (dsc_state.dsc_slice_height > 0) { + num_slices_v = (crtc_timing->v_addressable + dsc_state.dsc_slice_height - 1) / dsc_state.dsc_slice_height; + } + + if (crtc_timing->dsc_cfg.num_slices_h != num_slices_h) { + DC_LOG_DEBUG("boot timing validation failed due to num_slices_h mismatch\n"); + return false; + } + + if (crtc_timing->dsc_cfg.num_slices_v != num_slices_v) { + DC_LOG_DEBUG("boot timing validation failed due to num_slices_v mismatch\n"); + return false; + } + + if (crtc_timing->dsc_cfg.bits_per_pixel != dsc_state.dsc_bits_per_pixel) { + DC_LOG_DEBUG("boot timing validation failed due to bits_per_pixel mismatch\n"); + return false; + } + + if (crtc_timing->dsc_cfg.block_pred_enable != dsc_state.dsc_block_pred_enable) { + DC_LOG_DEBUG("boot timing validation failed due to block_pred_enable mismatch\n"); + return false; + } + + if (crtc_timing->dsc_cfg.linebuf_depth != dsc_state.dsc_line_buf_depth) { + DC_LOG_DEBUG("boot timing validation failed due to linebuf_depth mismatch\n"); + return false; + } + + if (crtc_timing->dsc_cfg.version_minor != dsc_state.dsc_version_minor) { + DC_LOG_DEBUG("boot timing validation failed due to version_minor mismatch\n"); + return false; + } + + if (crtc_timing->dsc_cfg.ycbcr422_simple != dsc_state.dsc_simple_422) { + DC_LOG_DEBUG("boot timing validation failed due to pixel encoding mismatch\n"); + return false; + } + + // Skip checks for is_frl, is_dp, and rc_buffer_size which are not programmed by vbios + // or not necessary for seamless boot validation. } if (dc_is_dp_signal(link->connector_signal)) { diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c index c1bdbb38c690..3bf737195bac 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c @@ -107,6 +107,11 @@ void dsc401_read_state(struct display_stream_compressor *dsc, struct dcn_dsc_sta REG_GET(DSCC_PPS_CONFIG7, SLICE_BPG_OFFSET, &s->dsc_slice_bpg_offset); REG_GET_2(DSCRM_DSC_FORWARD_CONFIG, DSCRM_DSC_FORWARD_EN, &s->dsc_fw_en, DSCRM_DSC_OPP_PIPE_SOURCE, &s->dsc_opp_source); + REG_GET(DSCC_PPS_CONFIG1, BLOCK_PRED_ENABLE, &s->dsc_block_pred_enable); + REG_GET(DSCC_PPS_CONFIG0, LINEBUF_DEPTH, &s->dsc_line_buf_depth); + REG_GET(DSCC_PPS_CONFIG0, DSC_VERSION_MINOR, &s->dsc_version_minor); + REG_GET(DSCC_CONFIG1, DSCC_RATE_CONTROL_BUFFER_MODEL_SIZE, &s->dsc_rc_buffer_size); + REG_GET(DSCC_PPS_CONFIG0, SIMPLE_422, &s->dsc_simple_422); } diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dsc.h b/drivers/gpu/drm/amd/display/dc/dsc/dsc.h index ad7ef83694ea..a16c60d8532f 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dsc.h +++ b/drivers/gpu/drm/amd/display/dc/dsc/dsc.h @@ -64,6 +64,11 @@ struct dcn_dsc_state { uint32_t dsc_chunk_size; uint32_t dsc_fw_en; uint32_t dsc_opp_source; + uint32_t dsc_block_pred_enable; + uint32_t dsc_line_buf_depth; + uint32_t dsc_version_minor; + uint32_t dsc_rc_buffer_size; + uint32_t dsc_simple_422; }; struct dcn_dsc_reg_state { From 0b577cd658510b1c2acbee6b089b2a94407cf3ff Mon Sep 17 00:00:00 2001 From: Dillon Varone Date: Tue, 24 Mar 2026 09:09:09 +0800 Subject: [PATCH 459/712] drm/amd/display: using cm structure for lut3d related info [Why] Using the alternative implementation via cm structure of config lut3d data Reviewed-by: Austin Zheng Signed-off-by: Dillon Varone Signed-off-by: ChuanYu Tseng Signed-off-by: Alex Deucher --- .../dml2_0/dml21/dml21_translation_helper.c | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c index 847fab508750..eadf84842ca0 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c @@ -601,27 +601,33 @@ static void populate_dml21_plane_config_from_plane_state(struct dml2_context *dm plane->composition.viewport.stationary = false; - if (plane_state->cm.flags.bits.lut3d_dma_enable) { + if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) { plane->tdlut.setup_for_tdlut = true; - switch (plane_state->cm.lut3d_dma.swizzle) { - case CM_LUT_3D_SWIZZLE_LINEAR_RGB: - case CM_LUT_3D_SWIZZLE_LINEAR_BGR: + switch (plane_state->mcm_luts.lut3d_data.gpu_mem_params.layout) { + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB: + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR: plane->tdlut.tdlut_addressing_mode = dml2_tdlut_sw_linear; break; - case CM_LUT_1D_PACKED_LINEAR: - default: + case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR: plane->tdlut.tdlut_addressing_mode = dml2_tdlut_simple_linear; break; } - switch (plane_state->cm.lut3d_dma.size) { - case CM_LUT_SIZE_333333: + switch (plane_state->mcm_luts.lut3d_data.gpu_mem_params.size) { + case DC_CM2_GPU_MEM_SIZE_171717: + plane->tdlut.tdlut_width_mode = dml2_tdlut_width_17_cube; + break; + case DC_CM2_GPU_MEM_SIZE_333333: plane->tdlut.tdlut_width_mode = dml2_tdlut_width_33_cube; break; - case CM_LUT_SIZE_171717: + // handling when use case and HW support available + case DC_CM2_GPU_MEM_SIZE_454545: + case DC_CM2_GPU_MEM_SIZE_656565: + break; + case DC_CM2_GPU_MEM_SIZE_TRANSFORMED: default: - plane->tdlut.tdlut_width_mode = dml2_tdlut_width_17_cube; + //plane->tdlut.tdlut_width_mode = dml2_tdlut_width_flatten; // dml2_tdlut_width_flatten undefined break; } } From 8de2559ec172b04301d6e53c4f30388e40fad08c Mon Sep 17 00:00:00 2001 From: Roman Li Date: Mon, 16 Mar 2026 16:45:47 -0400 Subject: [PATCH 460/712] drm/amd/display: Remove invalid DPSTREAMCLK mask usage [Why] The invalid register field access causes ASSERT(mask != 0) to fire in set_reg_field_values() during display enable. WARNING: at drivers/gpu/drm/amd/amdgpu/../display/dc/dc_helper.c:100 set_reg_field_values.isra.0+0xcf/0xf0 [amdgpu] Call Trace: generic_reg_update_ex+0x66/0x1d0 [amdgpu] dccg401_set_dpstreamclk+0xed/0x350 [amdgpu] dcn401_enable_stream+0x165/0x370 [amdgpu] link_set_dpms_on+0x6e9/0xe90 [amdgpu] dce110_apply_single_controller_ctx_to_hw+0x343/0x530 [amdgpu] dce110_apply_ctx_to_hw+0x1f6/0x2d0 [amdgpu] dc_commit_state_no_check+0x49a/0xe20 [amdgpu] dc_commit_streams+0x354/0x570 [amdgpu] amdgpu_dm_atomic_commit_tail+0x6f8/0x3fc0 [amdgpu] DCN4.x hardware does not have DPSTREAMCLK_GATE_DISABLE and DPSTREAMCLK_ROOT_GATE_DISABLE fields in DCCG_GATE_DISABLE_CNTL3. These global fields only exist in DCN3.1.x hardware. [How] Remove the call that tries to update non-existent fields in CNTL3. DCN4.x uses per-instance fields in CNTL5 instead, which are already correctly programmed in the switch cases above. Reviewed-by: Dillon Varone Signed-off-by: Roman Li Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c index 4b9a14c679d3..b6d2ead93345 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c @@ -526,10 +526,6 @@ static void dccg401_enable_dpstreamclk(struct dccg *dccg, int otg_inst, int dp_h BREAK_TO_DEBUGGER(); return; } - if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpstream) - REG_UPDATE_2(DCCG_GATE_DISABLE_CNTL3, - DPSTREAMCLK_GATE_DISABLE, 1, - DPSTREAMCLK_ROOT_GATE_DISABLE, 1); } void dccg401_disable_dpstreamclk(struct dccg *dccg, int dp_hpo_inst) From 60c741a13fd17ae2ec668442bd5a0cc9dd73e261 Mon Sep 17 00:00:00 2001 From: Dillon Varone Date: Thu, 19 Mar 2026 11:28:19 -0400 Subject: [PATCH 461/712] Revert "drm/amd/display: Add 3DLUT DMA broadcast support" Revert commit 7d59465de38e ("drm/amd/display: Add 3DLUT DMA broadcast support") [WHY&HOW] Dependencies of this change are still causing issues, so reverting until those can be fixed. Reviewed-by: Martin Leung Signed-off-by: Dillon Varone Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 2 +- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 107 +++++++----------- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.h | 3 +- .../drm/amd/display/dc/hwss/hw_sequencer.h | 2 +- 4 files changed, 48 insertions(+), 66 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index a62b03c5fa0f..0326bb64294d 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -4671,7 +4671,7 @@ static void commit_planes_for_stream(struct dc *dc, srf_updates[i].cm->flags.bits.lut3d_enable && srf_updates[i].cm->flags.bits.lut3d_dma_enable && dc->hwss.trigger_3dlut_dma_load) - dc->hwss.trigger_3dlut_dma_load(pipe_ctx); + dc->hwss.trigger_3dlut_dma_load(dc, pipe_ctx); /*program triple buffer after lock based on flip type*/ if (dc->hwss.program_triplebuffer != NULL && dc->debug.enable_tri_buf) { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 476dee9e722f..897f8af4b65a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -374,14 +374,13 @@ void dcn401_init_hw(struct dc *dc) } } -void dcn401_trigger_3dlut_dma_load(struct pipe_ctx *pipe_ctx) +void dcn401_trigger_3dlut_dma_load(struct dc *dc, struct pipe_ctx *pipe_ctx) { - const struct pipe_ctx *primary_dpp_pipe_ctx = resource_get_primary_dpp_pipe(pipe_ctx); - struct hubp *primary_hubp = primary_dpp_pipe_ctx ? - primary_dpp_pipe_ctx->plane_res.hubp : NULL; + (void)dc; + struct hubp *hubp = pipe_ctx->plane_res.hubp; - if (primary_hubp && primary_hubp->funcs->hubp_enable_3dlut_fl) { - primary_hubp->funcs->hubp_enable_3dlut_fl(primary_hubp, true); + if (hubp->funcs->hubp_enable_3dlut_fl) { + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); } } @@ -389,11 +388,8 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) { struct dc *dc = pipe_ctx->plane_res.hubp->ctx->dc; - const struct pipe_ctx *primary_dpp_pipe_ctx = resource_get_primary_dpp_pipe(pipe_ctx); struct dpp *dpp_base = pipe_ctx->plane_res.dpp; struct hubp *hubp = pipe_ctx->plane_res.hubp; - struct hubp *primary_hubp = primary_dpp_pipe_ctx ? - primary_dpp_pipe_ctx->plane_res.hubp : NULL; const struct dc_plane_cm *cm = &plane_state->cm; int mpcc_id = hubp->inst; struct mpc *mpc = dc->res_pool->mpc; @@ -491,41 +487,25 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, 12, mpcc_id); if (mpc->funcs->update_3dlut_fast_load_select) - mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, primary_hubp->inst); + mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, hubp->inst); /* HUBP */ - if (primary_hubp->inst == hubp->inst) { - /* only program if this is the primary dpp pipe for the given plane */ - if (hubp->funcs->hubp_program_3dlut_fl_config) - hubp->funcs->hubp_program_3dlut_fl_config(hubp, &cm->lut3d_dma); + if (hubp->funcs->hubp_program_3dlut_fl_config) + hubp->funcs->hubp_program_3dlut_fl_config(hubp, &cm->lut3d_dma); - if (hubp->funcs->hubp_program_3dlut_fl_crossbar) - hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, cm->lut3d_dma.format); + if (hubp->funcs->hubp_program_3dlut_fl_crossbar) + hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, cm->lut3d_dma.format); - if (hubp->funcs->hubp_program_3dlut_fl_addr) - hubp->funcs->hubp_program_3dlut_fl_addr(hubp, &cm->lut3d_dma.addr); + if (hubp->funcs->hubp_program_3dlut_fl_addr) + hubp->funcs->hubp_program_3dlut_fl_addr(hubp, &cm->lut3d_dma.addr); - if (hubp->funcs->hubp_enable_3dlut_fl) { - hubp->funcs->hubp_enable_3dlut_fl(hubp, true); - } else { - /* GPU memory only supports fast load path */ - BREAK_TO_DEBUGGER(); - lut_enable = false; - result = false; - } + if (hubp->funcs->hubp_enable_3dlut_fl) { + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); } else { - /* re-trigger priamry HUBP to load 3DLUT */ - if (primary_hubp->funcs->hubp_enable_3dlut_fl) { - primary_hubp->funcs->hubp_enable_3dlut_fl(primary_hubp, true); - } - - /* clear FL setup on this pipe's HUBP */ - memset(&lut3d_dma, 0, sizeof(lut3d_dma)); - if (hubp->funcs->hubp_program_3dlut_fl_config) - hubp->funcs->hubp_program_3dlut_fl_config(hubp, &lut3d_dma); - - if (hubp->funcs->hubp_enable_3dlut_fl) - hubp->funcs->hubp_enable_3dlut_fl(hubp, false); + /* GPU memory only supports fast load path */ + BREAK_TO_DEBUGGER(); + lut_enable = false; + result = false; } } else { /* Legacy (Host) Load Mode */ @@ -1835,41 +1815,42 @@ void dcn401_perform_3dlut_wa_unlock(struct pipe_ctx *pipe_ctx) * This is meant to work around a known HW issue where VREADY will cancel the pending 3DLUT_ENABLE signal regardless * of whether OTG lock is currently being held or not. */ - const struct pipe_ctx *otg_master_pipe_ctx = resource_get_otg_master(pipe_ctx); - struct timing_generator *tg = otg_master_pipe_ctx ? - otg_master_pipe_ctx->stream_res.tg : NULL; - const struct pipe_ctx *primary_dpp_pipe_ctx = resource_is_pipe_type(pipe_ctx, DPP_PIPE) ? - resource_get_primary_dpp_pipe(pipe_ctx) : pipe_ctx; - struct hubp *primary_hubp = primary_dpp_pipe_ctx ? - primary_dpp_pipe_ctx->plane_res.hubp : NULL; + struct pipe_ctx *wa_pipes[MAX_PIPES] = { NULL }; + struct pipe_ctx *odm_pipe, *mpc_pipe; + int i, wa_pipe_ct = 0; - if (!otg_master_pipe_ctx && !tg) { - return; + for (odm_pipe = pipe_ctx; odm_pipe != NULL; odm_pipe = odm_pipe->next_odm_pipe) { + for (mpc_pipe = odm_pipe; mpc_pipe != NULL; mpc_pipe = mpc_pipe->bottom_pipe) { + if (mpc_pipe->plane_state && + mpc_pipe->plane_state->cm.flags.bits.lut3d_enable && + mpc_pipe->plane_state->cm.flags.bits.lut3d_dma_enable) { + wa_pipes[wa_pipe_ct++] = mpc_pipe; + } + } } - if (primary_dpp_pipe_ctx && - primary_dpp_pipe_ctx->plane_state && - primary_dpp_pipe_ctx->plane_state->cm.flags.bits.lut3d_enable && - primary_dpp_pipe_ctx->plane_state->cm.flags.bits.lut3d_dma_enable) { - if (tg->funcs->set_vupdate_keepout) - tg->funcs->set_vupdate_keepout(tg, true); + if (wa_pipe_ct > 0) { + if (pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout) + pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout(pipe_ctx->stream_res.tg, true); - if (primary_hubp->funcs->hubp_enable_3dlut_fl) { - primary_hubp->funcs->hubp_enable_3dlut_fl(primary_hubp, true); + for (i = 0; i < wa_pipe_ct; ++i) { + if (wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl) + wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl(wa_pipes[i]->plane_res.hubp, true); } - tg->funcs->unlock(tg); - if (tg->funcs->wait_update_lock_status) - tg->funcs->wait_update_lock_status(tg, false); + pipe_ctx->stream_res.tg->funcs->unlock(pipe_ctx->stream_res.tg); + if (pipe_ctx->stream_res.tg->funcs->wait_update_lock_status) + pipe_ctx->stream_res.tg->funcs->wait_update_lock_status(pipe_ctx->stream_res.tg, false); - if (primary_hubp->funcs->hubp_enable_3dlut_fl) { - primary_hubp->funcs->hubp_enable_3dlut_fl(primary_hubp, true); + for (i = 0; i < wa_pipe_ct; ++i) { + if (wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl) + wa_pipes[i]->plane_res.hubp->funcs->hubp_enable_3dlut_fl(wa_pipes[i]->plane_res.hubp, true); } - if (tg->funcs->set_vupdate_keepout) - tg->funcs->set_vupdate_keepout(tg, false); + if (pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout) + pipe_ctx->stream_res.tg->funcs->set_vupdate_keepout(pipe_ctx->stream_res.tg, false); } else { - tg->funcs->unlock(tg); + pipe_ctx->stream_res.tg->funcs->unlock(pipe_ctx->stream_res.tg); } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h index b9a03ffa2717..f78162ab859b 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h @@ -41,7 +41,8 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, bool dcn401_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_stream_state *stream); -void dcn401_trigger_3dlut_dma_load(struct pipe_ctx *pipe_ctx); +void dcn401_trigger_3dlut_dma_load(struct dc *dc, + struct pipe_ctx *pipe_ctx); void dcn401_calculate_dccg_tmds_div_value(struct pipe_ctx *pipe_ctx, unsigned int *tmds_div); enum dc_status dcn401_enable_stream_timing( diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index 98abe0d2d30f..d1dba7ffcd9b 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -1120,7 +1120,7 @@ struct hw_sequencer_funcs { void (*program_output_csc)(struct dc *dc, struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace, uint16_t *matrix, int opp_id); - void (*trigger_3dlut_dma_load)(struct pipe_ctx *pipe_ctx); + void (*trigger_3dlut_dma_load)(struct dc *dc, struct pipe_ctx *pipe_ctx); /* VM Related */ int (*init_sys_ctx)(struct dce_hwseq *hws, From 4e91f4322ebefc93a1f23d101595a45530d5de1f Mon Sep 17 00:00:00 2001 From: Dillon Varone Date: Thu, 19 Mar 2026 11:31:17 -0400 Subject: [PATCH 462/712] Revert "drm/amd/display: Refactor DC update checks" Revert commit c24bb00cc6cf ("drm/amd/display: Refactor DC update checks") [WHY] Causing issues with PSR/Replay, reverting until those can be fixed. Reviewed-by: Martin Leung Signed-off-by: Dillon Varone Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +- .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 2 +- drivers/gpu/drm/amd/display/dc/core/dc.c | 482 +++++++++++------- drivers/gpu/drm/amd/display/dc/dc.h | 22 +- 4 files changed, 308 insertions(+), 204 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 3e87219e2aa4..8b161c9d649c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9890,7 +9890,7 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_crtc *acrtc_attach, } /* Decrement skip count when SR is enabled and we're doing fast updates. */ - if (acrtc_state->update_type <= UPDATE_TYPE_FAST && + if (acrtc_state->update_type == UPDATE_TYPE_FAST && (psr->psr_feature_enabled || pr->config.replay_supported)) { if (aconn->sr_skip_count > 0) aconn->sr_skip_count--; @@ -10100,7 +10100,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, * fast updates. */ if (crtc->state->async_flip && - (acrtc_state->update_type > UPDATE_TYPE_FAST || + (acrtc_state->update_type != UPDATE_TYPE_FAST || get_mem_type(old_plane_state->fb) != get_mem_type(fb))) drm_warn_once(state->dev, "[PLANE:%d:%s] async flip with non-fast update\n", @@ -10108,7 +10108,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, bundle->flip_addrs[planes_count].flip_immediate = crtc->state->async_flip && - acrtc_state->update_type <= UPDATE_TYPE_FAST && + acrtc_state->update_type == UPDATE_TYPE_FAST && get_mem_type(old_plane_state->fb) == get_mem_type(fb); timestamp_ns = ktime_get_ns(); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 82727f6ec469..304437c2284d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -685,7 +685,7 @@ static int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc, * pitch, the DCC state, rotation, etc. */ if (crtc_state->async_flip && - dm_crtc_state->update_type > UPDATE_TYPE_FAST) { + dm_crtc_state->update_type != UPDATE_TYPE_FAST) { drm_dbg_atomic(crtc->dev, "[CRTC:%d:%s] async flips are only supported for fast updates\n", crtc->base.id, crtc->name); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 0326bb64294d..79a4680ddb27 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -2761,7 +2761,7 @@ static bool is_surface_in_context( static struct surface_update_descriptor get_plane_info_update_type(const struct dc_surface_update *u) { union surface_update_flags *update_flags = &u->surface->update_flags; - struct surface_update_descriptor update_type = { UPDATE_TYPE_ADDR_ONLY, LOCK_DESCRIPTOR_NONE }; + struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; if (!u->plane_info) return update_type; @@ -2853,7 +2853,7 @@ static struct surface_update_descriptor get_scaling_info_update_type( const struct dc_surface_update *u) { union surface_update_flags *update_flags = &u->surface->update_flags; - struct surface_update_descriptor update_type = { UPDATE_TYPE_ADDR_ONLY, LOCK_DESCRIPTOR_NONE }; + struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; if (!u->scaling_info) return update_type; @@ -2904,11 +2904,11 @@ static struct surface_update_descriptor get_scaling_info_update_type( return update_type; } -static struct surface_update_descriptor check_update_surface( +static struct surface_update_descriptor det_surface_update( const struct dc_check_config *check_config, struct dc_surface_update *u) { - struct surface_update_descriptor overall_type = { UPDATE_TYPE_ADDR_ONLY, LOCK_DESCRIPTOR_NONE }; + struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; union surface_update_flags *update_flags = &u->surface->update_flags; if (u->surface->force_full_update) { @@ -2928,7 +2928,7 @@ static struct surface_update_descriptor check_update_surface( if (u->flip_addr) { update_flags->bits.addr_update = 1; - elevate_update_type(&overall_type, UPDATE_TYPE_ADDR_ONLY, LOCK_DESCRIPTOR_STREAM); + elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); if (u->flip_addr->address.tmz_surface != u->surface->address.tmz_surface) { update_flags->bits.tmz_changed = 1; @@ -2942,43 +2942,27 @@ static struct surface_update_descriptor check_update_surface( if (u->input_csc_color_matrix) { update_flags->bits.input_csc_change = 1; - elevate_update_type(&overall_type, - check_config->enable_legacy_fast_update ? UPDATE_TYPE_MED : UPDATE_TYPE_FAST, - LOCK_DESCRIPTOR_STREAM); - } - - if (u->cursor_csc_color_matrix) { - elevate_update_type(&overall_type, - check_config->enable_legacy_fast_update ? UPDATE_TYPE_MED : UPDATE_TYPE_FAST, - LOCK_DESCRIPTOR_STREAM); + elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->coeff_reduction_factor) { update_flags->bits.coeff_reduction_change = 1; - elevate_update_type(&overall_type, - check_config->enable_legacy_fast_update ? UPDATE_TYPE_MED : UPDATE_TYPE_FAST, - LOCK_DESCRIPTOR_STREAM); + elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->gamut_remap_matrix) { update_flags->bits.gamut_remap_change = 1; - elevate_update_type(&overall_type, - check_config->enable_legacy_fast_update ? UPDATE_TYPE_MED : UPDATE_TYPE_FAST, - LOCK_DESCRIPTOR_STREAM); + elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->cm || (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) { update_flags->bits.gamma_change = 1; - elevate_update_type(&overall_type, - check_config->enable_legacy_fast_update ? UPDATE_TYPE_MED : UPDATE_TYPE_FAST, - LOCK_DESCRIPTOR_STREAM); + elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->cm && (u->cm->flags.bits.lut3d_enable || u->surface->cm.flags.bits.lut3d_enable)) { update_flags->bits.lut_3d = 1; - elevate_update_type(&overall_type, - check_config->enable_legacy_fast_update ? UPDATE_TYPE_MED : UPDATE_TYPE_FAST, - LOCK_DESCRIPTOR_STREAM); + elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->cm && u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable && @@ -2994,10 +2978,9 @@ static struct surface_update_descriptor check_update_surface( if (u->hdr_mult.value) if (u->hdr_mult.value != u->surface->hdr_mult.value) { + // TODO: Should be fast? update_flags->bits.hdr_mult = 1; - elevate_update_type(&overall_type, - check_config->enable_legacy_fast_update ? UPDATE_TYPE_MED : UPDATE_TYPE_FAST, - LOCK_DESCRIPTOR_STREAM); + elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } if (u->sdr_white_level_nits) @@ -3051,7 +3034,7 @@ static struct surface_update_descriptor check_update_surfaces_for_stream( int surface_count, struct dc_stream_update *stream_update) { - struct surface_update_descriptor overall_type = { UPDATE_TYPE_ADDR_ONLY, LOCK_DESCRIPTOR_NONE }; + struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; /* When countdown finishes, promote this flip to full to trigger deferred final transition */ if (check_config->deferred_transition_state && !check_config->transition_countdown_to_steady_state) { @@ -3118,18 +3101,7 @@ static struct surface_update_descriptor check_update_surfaces_for_stream( if (su_flags->raw) elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); - /* Non-global cases */ - if (stream_update->hdr_static_metadata || - stream_update->vrr_infopacket || - stream_update->vsc_infopacket || - stream_update->vsp_infopacket || - stream_update->hfvsif_infopacket || - stream_update->adaptive_sync_infopacket || - stream_update->vtem_infopacket || - stream_update->avi_infopacket) { - elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); - } - + // Non-global cases if (stream_update->output_csc_transform) { su_flags->bits.out_csc = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); @@ -3139,32 +3111,11 @@ static struct surface_update_descriptor check_update_surfaces_for_stream( su_flags->bits.out_tf = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } - - if (stream_update->periodic_interrupt) { - elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); - } - - if (stream_update->dither_option) { - elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); - } - - if (stream_update->cursor_position || stream_update->cursor_attributes) { - elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); - } - - /* TODO - cleanup post blend CM */ - if (stream_update->func_shaper || stream_update->lut3d_func) { - elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); - } - - if (stream_update->pending_test_pattern) { - elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); - } } for (int i = 0 ; i < surface_count; i++) { struct surface_update_descriptor inner_type = - check_update_surface(check_config, &updates[i]); + det_surface_update(check_config, &updates[i]); elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor); } @@ -3191,84 +3142,6 @@ struct surface_update_descriptor dc_check_update_surfaces_for_stream( return check_update_surfaces_for_stream(check_config, updates, surface_count, stream_update); } -/* - * check_update_state_and_surfaces_for_stream() - Determine update type (fast, med, or full) - * - * This function performs checks on the DC global state, and is therefore not re-entrant. It - * should not be called from DM. - * - * See :c:type:`enum surface_update_type ` for explanation of update types - */ -static struct surface_update_descriptor check_update_state_and_surfaces_for_stream( - const struct dc *dc, - const struct dc_check_config *check_config, - const struct dc_stream_state *stream, - const struct dc_surface_update *updates, - const int surface_count, - const struct dc_stream_update *stream_update) -{ - (void)check_config; - (void)stream_update; - - const struct dc_state *context = dc->current_state; - - struct surface_update_descriptor overall_type = { UPDATE_TYPE_ADDR_ONLY, LOCK_DESCRIPTOR_NONE}; - - if (updates) - for (int i = 0; i < surface_count; i++) - if (!is_surface_in_context(context, updates[i].surface)) - elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); - - if (stream) { - const struct dc_stream_status *stream_status = dc_stream_get_status_const(stream); - if (stream_status == NULL || stream_status->plane_count != surface_count) - elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); - } - if (dc->idle_optimizations_allowed) - elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); - - if (dc_can_clear_cursor_limit(dc)) - elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); - - return overall_type; -} - -/* - * dc_check_update_state_and_surfaces_for_stream() - Determine update type (fast, med, or full) - * - * This function performs checks on the DC global state, stream and surface update, and is - * therefore not re-entrant. It should not be called from DM. - * - * See :c:type:`enum surface_update_type ` for explanation of update types - */ -static struct surface_update_descriptor dc_check_update_state_and_surfaces_for_stream( - const struct dc *dc, - const struct dc_check_config *check_config, - struct dc_stream_state *stream, - struct dc_surface_update *updates, - int surface_count, - struct dc_stream_update *stream_update) -{ - /* check updates against the entire DC state (global) first */ - struct surface_update_descriptor overall_update_type = check_update_state_and_surfaces_for_stream( - dc, - check_config, - stream, - updates, - surface_count, - stream_update); - - /* check updates for stream and plane */ - struct surface_update_descriptor stream_update_type = dc_check_update_surfaces_for_stream( - check_config, - updates, - surface_count, - stream_update); - elevate_update_type(&overall_update_type, stream_update_type.update_type, stream_update_type.lock_descriptor); - - return overall_update_type; -} - static struct dc_stream_status *stream_get_status( struct dc_state *ctx, struct dc_stream_state *stream) @@ -3625,6 +3498,13 @@ static void update_seamless_boot_flags(struct dc *dc, } } +static bool full_update_required_weak( + const struct dc *dc, + const struct dc_surface_update *srf_updates, + int surface_count, + const struct dc_stream_update *stream_update, + const struct dc_stream_state *stream); + struct pipe_split_policy_backup { bool dynamic_odm_policy; bool subvp_policy; @@ -3695,11 +3575,12 @@ static bool update_planes_and_stream_state(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - struct surface_update_descriptor *update_descriptor, + enum surface_update_type *new_update_type, struct dc_state **new_context) { struct dc_state *context; int i, j; + enum surface_update_type update_type; const struct dc_stream_status *stream_status; struct dc_context *dc_ctx = dc->ctx; @@ -3713,20 +3594,17 @@ static bool update_planes_and_stream_state(struct dc *dc, } context = dc->current_state; - *update_descriptor = dc_check_update_state_and_surfaces_for_stream( - dc, - &dc->check_config, - stream, - srf_updates, - surface_count, - stream_update); + update_type = dc_check_update_surfaces_for_stream( + &dc->check_config, srf_updates, surface_count, stream_update).update_type; + if (full_update_required_weak(dc, srf_updates, surface_count, stream_update, stream)) + update_type = UPDATE_TYPE_FULL; /* It is possible to receive a flip for one plane while there are multiple flip_immediate planes in the same stream. * E.g. Desktop and MPO plane are flip_immediate but only the MPO plane received a flip * Force the other flip_immediate planes to flip so GSL doesn't wait for a flip that won't come. */ force_immediate_gsl_plane_flip(dc, srf_updates, surface_count); - if (update_descriptor->update_type == UPDATE_TYPE_FULL) + if (update_type == UPDATE_TYPE_FULL) backup_planes_and_stream_state(&dc->scratch.current_state, stream); /* update current stream with the new updates */ @@ -3752,7 +3630,7 @@ static bool update_planes_and_stream_state(struct dc *dc, } } - if (update_descriptor->update_type == UPDATE_TYPE_FULL) { + if (update_type == UPDATE_TYPE_FULL) { if (stream_update) { uint32_t dsc_changed = stream_update->stream->update_flags.bits.dsc_changed; stream_update->stream->update_flags.raw = 0xFFFFFFFF; @@ -3762,13 +3640,13 @@ static bool update_planes_and_stream_state(struct dc *dc, srf_updates[i].surface->update_flags.raw = 0xFFFFFFFF; } - if (update_descriptor->update_type >= update_surface_trace_level) + if (update_type >= update_surface_trace_level) update_surface_trace(dc, srf_updates, surface_count); for (i = 0; i < surface_count; i++) copy_surface_update_to_plane(srf_updates[i].surface, &srf_updates[i]); - if (update_descriptor->update_type >= UPDATE_TYPE_FULL) { + if (update_type >= UPDATE_TYPE_FULL) { struct dc_plane_state *new_planes[MAX_SURFACES] = {0}; for (i = 0; i < surface_count; i++) @@ -3806,7 +3684,7 @@ static bool update_planes_and_stream_state(struct dc *dc, for (i = 0; i < surface_count; i++) { struct dc_plane_state *surface = srf_updates[i].surface; - if (update_descriptor->update_type != UPDATE_TYPE_MED) + if (update_type != UPDATE_TYPE_MED) continue; if (surface->update_flags.bits.position_change) { for (j = 0; j < dc->res_pool->pipe_count; j++) { @@ -3820,7 +3698,7 @@ static bool update_planes_and_stream_state(struct dc *dc, } } - if (update_descriptor->update_type == UPDATE_TYPE_FULL) { + if (update_type == UPDATE_TYPE_FULL) { struct pipe_split_policy_backup policy; bool minimize = false; @@ -3849,7 +3727,8 @@ static bool update_planes_and_stream_state(struct dc *dc, update_seamless_boot_flags(dc, context, surface_count, stream); *new_context = context; - if (update_descriptor->update_type == UPDATE_TYPE_FULL) + *new_update_type = update_type; + if (update_type == UPDATE_TYPE_FULL) backup_planes_and_stream_state(&dc->scratch.new_state, stream); return true; @@ -3929,7 +3808,7 @@ static void commit_planes_do_stream_update(struct dc *dc, program_cursor_position(dc, stream); /* Full fe update*/ - if (update_type <= UPDATE_TYPE_FAST) + if (update_type == UPDATE_TYPE_FAST) continue; if (stream_update->dsc_config) @@ -4238,7 +4117,7 @@ static void commit_planes_for_stream_fast(struct dc *dc, struct pipe_ctx *top_pipe_to_program = NULL; struct dc_stream_status *stream_status = NULL; bool should_offload_fams2_flip = false; - bool should_lock_all_pipes = (update_type > UPDATE_TYPE_FAST); + bool should_lock_all_pipes = (update_type != UPDATE_TYPE_FAST); if (should_lock_all_pipes) determine_pipe_unlock_order(dc, context); @@ -4298,7 +4177,7 @@ static void commit_planes_for_stream_fast(struct dc *dc, continue; pipe_ctx->plane_state->triplebuffer_flips = false; - if (update_type <= UPDATE_TYPE_FAST && + if (update_type == UPDATE_TYPE_FAST && dc->hwss.program_triplebuffer != NULL && !pipe_ctx->plane_state->flip_immediate && dc->debug.enable_tri_buf) { /*triple buffer for VUpdate only*/ @@ -4355,7 +4234,7 @@ static void commit_planes_for_stream(struct dc *dc, { int i, j; struct pipe_ctx *top_pipe_to_program = NULL; - bool should_lock_all_pipes = (update_type > UPDATE_TYPE_FAST); + bool should_lock_all_pipes = (update_type != UPDATE_TYPE_FAST); bool subvp_prev_use = false; bool subvp_curr_use = false; uint8_t current_stream_mask = 0; @@ -4372,7 +4251,7 @@ static void commit_planes_for_stream(struct dc *dc, if (update_type == UPDATE_TYPE_FULL && dc->optimized_required) hwss_process_outstanding_hw_updates(dc, dc->current_state); - if (update_type > UPDATE_TYPE_FAST && dc->res_pool->funcs->prepare_mcache_programming) + if (update_type != UPDATE_TYPE_FAST && dc->res_pool->funcs->prepare_mcache_programming) dc->res_pool->funcs->prepare_mcache_programming(dc, context); for (i = 0; i < dc->res_pool->pipe_count; i++) { @@ -4434,7 +4313,7 @@ static void commit_planes_for_stream(struct dc *dc, odm_pipe->ttu_regs.min_ttu_vblank = MAX_TTU; } - if ((update_type > UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed) + if ((update_type != UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed) if (top_pipe_to_program && top_pipe_to_program->stream_res.tg->funcs->lock_doublebuffer_enable) { if (should_use_dmub_inbox1_lock(dc, stream->link)) { @@ -4505,7 +4384,7 @@ static void commit_planes_for_stream(struct dc *dc, } dc->hwss.post_unlock_program_front_end(dc, context); - if (update_type > UPDATE_TYPE_FAST) + if (update_type != UPDATE_TYPE_FAST) if (dc->hwss.commit_subvp_config) dc->hwss.commit_subvp_config(dc, context); @@ -4521,7 +4400,7 @@ static void commit_planes_for_stream(struct dc *dc, return; } - if (update_type > UPDATE_TYPE_FAST) { + if (update_type != UPDATE_TYPE_FAST) { for (j = 0; j < dc->res_pool->pipe_count; j++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; @@ -4549,7 +4428,7 @@ static void commit_planes_for_stream(struct dc *dc, if (!should_update_pipe_for_plane(context, pipe_ctx, plane_state)) continue; pipe_ctx->plane_state->triplebuffer_flips = false; - if (update_type <= UPDATE_TYPE_FAST && + if (update_type == UPDATE_TYPE_FAST && dc->hwss.program_triplebuffer != NULL && !pipe_ctx->plane_state->flip_immediate && dc->debug.enable_tri_buf) { /*triple buffer for VUpdate only*/ @@ -4576,7 +4455,7 @@ static void commit_planes_for_stream(struct dc *dc, continue; /* Full fe update*/ - if (update_type <= UPDATE_TYPE_FAST) + if (update_type == UPDATE_TYPE_FAST) continue; stream_status = @@ -4595,7 +4474,7 @@ static void commit_planes_for_stream(struct dc *dc, continue; /* Full fe update*/ - if (update_type <= UPDATE_TYPE_FAST) + if (update_type == UPDATE_TYPE_FAST) continue; ASSERT(!pipe_ctx->plane_state->triplebuffer_flips); @@ -4606,7 +4485,7 @@ static void commit_planes_for_stream(struct dc *dc, } } - if (dc->hwss.program_front_end_for_ctx && update_type > UPDATE_TYPE_FAST) { + if (dc->hwss.program_front_end_for_ctx && update_type != UPDATE_TYPE_FAST) { dc->hwss.program_front_end_for_ctx(dc, context); //Pipe busy until some frame and line # @@ -4634,7 +4513,7 @@ static void commit_planes_for_stream(struct dc *dc, } // Update Type FAST, Surface updates - if (update_type <= UPDATE_TYPE_FAST) { + if (update_type == UPDATE_TYPE_FAST) { if (dc->hwss.set_flip_control_gsl) for (i = 0; i < surface_count; i++) { struct dc_plane_state *plane_state = srf_updates[i].surface; @@ -4691,7 +4570,7 @@ static void commit_planes_for_stream(struct dc *dc, dc->hwss.pipe_control_lock(dc, top_pipe_to_program, false); } - if ((update_type > UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed) + if ((update_type != UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed) if (top_pipe_to_program && top_pipe_to_program->stream_res.tg->funcs->lock_doublebuffer_enable) { top_pipe_to_program->stream_res.tg->funcs->wait_for_state( @@ -4724,13 +4603,13 @@ static void commit_planes_for_stream(struct dc *dc, /* If enabling subvp or transitioning from subvp->subvp, enable the * phantom streams before we program front end for the phantom pipes. */ - if (update_type > UPDATE_TYPE_FAST) { + if (update_type != UPDATE_TYPE_FAST) { if (dc->hwss.enable_phantom_streams) dc->hwss.enable_phantom_streams(dc, context); } } - if (update_type > UPDATE_TYPE_FAST) + if (update_type != UPDATE_TYPE_FAST) dc->hwss.post_unlock_program_front_end(dc, context); if (subvp_prev_use && !subvp_curr_use) { @@ -4743,7 +4622,7 @@ static void commit_planes_for_stream(struct dc *dc, dc->hwss.disable_phantom_streams(dc, context); } - if (update_type > UPDATE_TYPE_FAST) + if (update_type != UPDATE_TYPE_FAST) if (dc->hwss.commit_subvp_config) dc->hwss.commit_subvp_config(dc, context); /* Since phantom pipe programming is moved to post_unlock_program_front_end, @@ -5215,12 +5094,198 @@ static bool commit_minimal_transition_state(struct dc *dc, return true; } +void populate_fast_updates(struct dc_fast_update *fast_update, + struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_update *stream_update) +{ + int i = 0; + + if (stream_update) { + fast_update[0].out_transfer_func = stream_update->out_transfer_func; + fast_update[0].output_csc_transform = stream_update->output_csc_transform; + } else { + fast_update[0].out_transfer_func = NULL; + fast_update[0].output_csc_transform = NULL; + } + + for (i = 0; i < surface_count; i++) { + fast_update[i].flip_addr = srf_updates[i].flip_addr; + fast_update[i].gamma = srf_updates[i].gamma; + fast_update[i].gamut_remap_matrix = srf_updates[i].gamut_remap_matrix; + fast_update[i].input_csc_color_matrix = srf_updates[i].input_csc_color_matrix; + fast_update[i].coeff_reduction_factor = srf_updates[i].coeff_reduction_factor; + fast_update[i].cursor_csc_color_matrix = srf_updates[i].cursor_csc_color_matrix; +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + fast_update[i].cm_hist_control = srf_updates[i].cm_hist_control; +#endif + } +} + +static bool fast_updates_exist(const struct dc_fast_update *fast_update, int surface_count) +{ + int i; + + if (fast_update[0].out_transfer_func || + fast_update[0].output_csc_transform) + return true; + + for (i = 0; i < surface_count; i++) { + if (fast_update[i].flip_addr || + fast_update[i].gamma || + fast_update[i].gamut_remap_matrix || + fast_update[i].input_csc_color_matrix || + fast_update[i].cursor_csc_color_matrix || +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + fast_update[i].cm_hist_control || +#endif + fast_update[i].coeff_reduction_factor) + return true; + } + + return false; +} + +bool fast_nonaddr_updates_exist(struct dc_fast_update *fast_update, int surface_count) +{ + int i; + + if (fast_update[0].out_transfer_func || + fast_update[0].output_csc_transform) + return true; + + for (i = 0; i < surface_count; i++) { + if (fast_update[i].input_csc_color_matrix || + fast_update[i].gamma || + fast_update[i].gamut_remap_matrix || + fast_update[i].coeff_reduction_factor || +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + fast_update[i].cm_hist_control || +#endif + fast_update[i].cursor_csc_color_matrix) + return true; + } + + return false; +} + +static bool full_update_required_weak( + const struct dc *dc, + const struct dc_surface_update *srf_updates, + int surface_count, + const struct dc_stream_update *stream_update, + const struct dc_stream_state *stream) +{ + (void)stream_update; + const struct dc_state *context = dc->current_state; + if (srf_updates) + for (int i = 0; i < surface_count; i++) + if (!is_surface_in_context(context, srf_updates[i].surface)) + return true; + + if (stream) { + const struct dc_stream_status *stream_status = dc_stream_get_status_const(stream); + if (stream_status == NULL || stream_status->plane_count != surface_count) + return true; + } + if (dc->idle_optimizations_allowed) + return true; + + if (dc_can_clear_cursor_limit(dc)) + return true; + + return false; +} + +static bool full_update_required( + const struct dc *dc, + const struct dc_surface_update *srf_updates, + int surface_count, + const struct dc_stream_update *stream_update, + const struct dc_stream_state *stream) +{ + const union dc_plane_cm_flags blend_only_flags = { + .bits = { + .blend_enable = 1, + } + }; + + if (full_update_required_weak(dc, srf_updates, surface_count, stream_update, stream)) + return true; + + for (int i = 0; i < surface_count; i++) { + if (srf_updates && + (srf_updates[i].plane_info || + srf_updates[i].scaling_info || + (srf_updates[i].hdr_mult.value && + srf_updates[i].hdr_mult.value != srf_updates->surface->hdr_mult.value) || + (srf_updates[i].sdr_white_level_nits && + srf_updates[i].sdr_white_level_nits != srf_updates->surface->sdr_white_level_nits) || + srf_updates[i].in_transfer_func || + srf_updates[i].surface->force_full_update || + (srf_updates[i].flip_addr && + srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface) || + (srf_updates[i].cm && + ((srf_updates[i].cm->flags.all != blend_only_flags.all && srf_updates[i].cm->flags.all != 0) || + (srf_updates[i].surface->cm.flags.all != blend_only_flags.all && srf_updates[i].surface->cm.flags.all != 0))))) + return true; + } + + if (stream_update && + (((stream_update->src.height != 0 && stream_update->src.width != 0) || + (stream_update->dst.height != 0 && stream_update->dst.width != 0) || + stream_update->integer_scaling_update) || + stream_update->hdr_static_metadata || + stream_update->abm_level || + stream_update->periodic_interrupt || + stream_update->vrr_infopacket || + stream_update->vsc_infopacket || + stream_update->vsp_infopacket || + stream_update->hfvsif_infopacket || + stream_update->vtem_infopacket || + stream_update->adaptive_sync_infopacket || + stream_update->avi_infopacket || + stream_update->dpms_off || + stream_update->allow_freesync || + stream_update->vrr_active_variable || + stream_update->vrr_active_fixed || + stream_update->gamut_remap || + stream_update->output_color_space || + stream_update->dither_option || + stream_update->wb_update || + stream_update->dsc_config || + stream_update->mst_bw_update || + stream_update->func_shaper || + stream_update->lut3d_func || + stream_update->pending_test_pattern || + stream_update->crtc_timing_adjust || + stream_update->scaler_sharpener_update || + stream_update->hw_cursor_req)) + return true; + + return false; +} + +static bool fast_update_only( + const struct dc *dc, + const struct dc_fast_update *fast_update, + const struct dc_surface_update *srf_updates, + int surface_count, + const struct dc_stream_update *stream_update, + const struct dc_stream_state *stream) +{ + return fast_updates_exist(fast_update, surface_count) + && !full_update_required(dc, srf_updates, surface_count, stream_update, stream); +} + static bool update_planes_and_stream_v2(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update) { struct dc_state *context; + enum surface_update_type update_type; + struct dc_fast_update fast_update[MAX_SURFACES] = {0}; /* In cases where MPO and split or ODM are used transitions can * cause underflow. Apply stream configuration with minimal pipe @@ -5228,9 +5293,11 @@ static bool update_planes_and_stream_v2(struct dc *dc, */ bool force_minimal_pipe_splitting = 0; bool is_plane_addition = 0; + bool is_fast_update_only; - struct surface_update_descriptor update_descriptor = {0}; - + populate_fast_updates(fast_update, srf_updates, surface_count, stream_update); + is_fast_update_only = fast_update_only(dc, fast_update, srf_updates, + surface_count, stream_update, stream); force_minimal_pipe_splitting = could_mpcc_tree_change_for_active_pipes( dc, stream, @@ -5249,7 +5316,7 @@ static bool update_planes_and_stream_v2(struct dc *dc, surface_count, stream, stream_update, - &update_descriptor, + &update_type, &context)) return false; @@ -5259,7 +5326,7 @@ static bool update_planes_and_stream_v2(struct dc *dc, dc_state_release(context); return false; } - elevate_update_type(&update_descriptor, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); + update_type = UPDATE_TYPE_FULL; } if (dc->hwss.is_pipe_topology_transition_seamless && @@ -5268,13 +5335,13 @@ static bool update_planes_and_stream_v2(struct dc *dc, commit_minimal_transition_state_in_dc_update(dc, context, stream, srf_updates, surface_count); - if (update_descriptor.update_type <= UPDATE_TYPE_FAST) { + if (is_fast_update_only && !dc->check_config.enable_legacy_fast_update) { commit_planes_for_stream_fast(dc, srf_updates, surface_count, stream, stream_update, - update_descriptor.update_type, + update_type, context); } else { if (!stream_update && @@ -5290,7 +5357,7 @@ static bool update_planes_and_stream_v2(struct dc *dc, surface_count, stream, stream_update, - update_descriptor.update_type, + update_type, context); } if (dc->current_state != context) @@ -5304,8 +5371,14 @@ static void commit_planes_and_stream_update_on_current_context(struct dc *dc, struct dc_stream_update *stream_update, enum surface_update_type update_type) { + struct dc_fast_update fast_update[MAX_SURFACES] = {0}; + ASSERT(update_type < UPDATE_TYPE_FULL); - if (update_type <= UPDATE_TYPE_FAST) + populate_fast_updates(fast_update, srf_updates, surface_count, + stream_update); + if (fast_update_only(dc, fast_update, srf_updates, surface_count, + stream_update, stream) && + !dc->check_config.enable_legacy_fast_update) commit_planes_for_stream_fast(dc, srf_updates, surface_count, @@ -5396,7 +5469,7 @@ static bool update_planes_and_stream_v3(struct dc *dc, struct dc_stream_update *stream_update) { struct dc_state *new_context; - struct surface_update_descriptor update_descriptor = {0}; + enum surface_update_type update_type; /* * When this function returns true and new_context is not equal to @@ -5408,26 +5481,22 @@ static bool update_planes_and_stream_v3(struct dc *dc, * replaced by a newer context. Refer to the use of * swap_and_free_current_context below. */ - if (!update_planes_and_stream_state(dc, - srf_updates, - surface_count, - stream, - stream_update, - &update_descriptor, + if (!update_planes_and_stream_state(dc, srf_updates, surface_count, + stream, stream_update, &update_type, &new_context)) return false; if (new_context == dc->current_state) { commit_planes_and_stream_update_on_current_context(dc, srf_updates, surface_count, stream, - stream_update, update_descriptor.update_type); + stream_update, update_type); if (dc->check_config.transition_countdown_to_steady_state) dc->check_config.transition_countdown_to_steady_state--; } else { commit_planes_and_stream_update_with_new_context(dc, srf_updates, surface_count, stream, - stream_update, update_descriptor.update_type, new_context); + stream_update, update_type, new_context); } return true; @@ -7199,7 +7268,7 @@ struct dc_update_scratch_space { struct dc_stream_update *stream_update; bool update_v3; bool do_clear_update_flags; - struct surface_update_descriptor update_descriptor; + enum surface_update_type update_type; struct dc_state *new_context; enum update_v3_flow flow; struct dc_state *backup_context; @@ -7282,28 +7351,45 @@ static bool update_planes_and_stream_prepare_v3( ASSERT(scratch->flow == UPDATE_V3_FLOW_INVALID); dc_exit_ips_for_hw_access(scratch->dc); + /* HWSS path determination needs to be done prior to updating the surface and stream states. */ + struct dc_fast_update fast_update[MAX_SURFACES] = { 0 }; + + populate_fast_updates(fast_update, + scratch->surface_updates, + scratch->surface_count, + scratch->stream_update); + + const bool is_hwss_fast_path_only = + fast_update_only(scratch->dc, + fast_update, + scratch->surface_updates, + scratch->surface_count, + scratch->stream_update, + scratch->stream) && + !scratch->dc->check_config.enable_legacy_fast_update; + if (!update_planes_and_stream_state( scratch->dc, scratch->surface_updates, scratch->surface_count, scratch->stream, scratch->stream_update, - &scratch->update_descriptor, + &scratch->update_type, &scratch->new_context )) { return false; } if (scratch->new_context == scratch->dc->current_state) { - ASSERT(scratch->update_descriptor.update_type < UPDATE_TYPE_FULL); + ASSERT(scratch->update_type < UPDATE_TYPE_FULL); - scratch->flow = scratch->update_descriptor.update_type <= UPDATE_TYPE_FAST + scratch->flow = is_hwss_fast_path_only ? UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST : UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL; return true; } - ASSERT(scratch->update_descriptor.update_type >= UPDATE_TYPE_FULL); + ASSERT(scratch->update_type >= UPDATE_TYPE_FULL); const bool seamless = scratch->dc->hwss.is_pipe_topology_transition_seamless( scratch->dc, @@ -7376,7 +7462,7 @@ static void update_planes_and_stream_execute_v3_commit( intermediate_update ? scratch->intermediate_count : scratch->surface_count, scratch->stream, use_stream_update ? scratch->stream_update : NULL, - intermediate_context ? UPDATE_TYPE_FULL : scratch->update_descriptor.update_type, + intermediate_context ? UPDATE_TYPE_FULL : scratch->update_type, // `dc->current_state` only used in `NO_NEW_CONTEXT`, where it is equal to `new_context` intermediate_context ? scratch->intermediate_context : scratch->new_context ); @@ -7394,7 +7480,7 @@ static void update_planes_and_stream_execute_v3( scratch->surface_count, scratch->stream, scratch->stream_update, - scratch->update_descriptor.update_type, + scratch->update_type, scratch->new_context ); break; diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index ab7dea9d47f7..afc06dfc161f 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -467,7 +467,6 @@ struct dc_static_screen_params { */ enum surface_update_type { - UPDATE_TYPE_ADDR_ONLY, /* only surface address is being updated, no other programming needed */ UPDATE_TYPE_FAST, /* super fast, safe to execute in isr */ UPDATE_TYPE_MED, /* ISR safe, most of programming needed, no bw/clk change*/ UPDATE_TYPE_FULL, /* may need to shuffle resources */ @@ -1881,6 +1880,20 @@ struct dc_scaling_info { struct scaling_taps scaling_quality; }; +struct dc_fast_update { + const struct dc_flip_addrs *flip_addr; + const struct dc_gamma *gamma; + const struct colorspace_transform *gamut_remap_matrix; + const struct dc_csc_transform *input_csc_color_matrix; + const struct fixed31_32 *coeff_reduction_factor; + struct dc_transfer_func *out_transfer_func; + struct dc_csc_transform *output_csc_transform; + const struct dc_csc_transform *cursor_csc_color_matrix; +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + struct cm_hist_control *cm_hist_control; +#endif +}; + struct dc_surface_update { struct dc_plane_state *surface; @@ -2019,7 +2032,12 @@ bool dc_resource_is_dsc_encoding_supported(const struct dc *dc); void get_audio_check(struct audio_info *aud_modes, struct audio_check *aud_chk); - /* +bool fast_nonaddr_updates_exist(struct dc_fast_update *fast_update, int surface_count); +void populate_fast_updates(struct dc_fast_update *fast_update, + struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_update *stream_update); +/* * Set up streams and links associated to drive sinks * The streams parameter is an absolute set of all active streams. * From 7b82e92da0d4fd686901edd9f649c51fcbcb9894 Mon Sep 17 00:00:00 2001 From: Charlene Liu Date: Thu, 19 Mar 2026 18:23:43 -0400 Subject: [PATCH 463/712] drm/amd/display: correct unknown plane state patch [why] dcn42x is using same gfx as dcn35, i.e. not use gfx_address3. Reviewed-by: Ovidiu Bunea Signed-off-by: Charlene Liu Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index f2a6e260f061..9d6a989d6dd2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -1785,7 +1785,7 @@ static struct resource_funcs dcn42_res_pool_funcs = { .acquire_post_bldn_3dlut = dcn32_acquire_post_bldn_3dlut, .release_post_bldn_3dlut = dcn32_release_post_bldn_3dlut, .update_bw_bounding_box = dcn42_update_bw_bounding_box, - .patch_unknown_plane_state = dcn401_patch_unknown_plane_state, + .patch_unknown_plane_state = dcn35_patch_unknown_plane_state, .get_panel_config_defaults = dcn42_get_panel_config_defaults, .get_preferred_eng_id_dpia = dcn42_get_preferred_eng_id_dpia, .update_soc_for_wm_a = dcn30_update_soc_for_wm_a, From 02c3060ee303846cea79910738753735d39067d4 Mon Sep 17 00:00:00 2001 From: Gangliang Xie Date: Wed, 18 Mar 2026 16:09:39 +0800 Subject: [PATCH 464/712] drm/amdgpu: add support to query vram info from firmware add support to query vram info from firmware v2: change APU vram type, add multi-aid check v3: seperate vram info query function into 3 parts and call them in a helper func when requirements are met. v4: calculate vram_width for v9.x Signed-off-by: Gangliang Xie Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher --- .../gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c | 465 ++++++++++-------- .../gpu/drm/amd/amdgpu/amdgpu_atomfirmware.h | 4 + drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 29 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h | 2 + drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 52 +- 8 files changed, 315 insertions(+), 243 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c index 7f4751e5caaf..cd9aa5b45e94 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c @@ -373,17 +373,148 @@ int amdgpu_atomfirmware_get_uma_carveout_info(struct amdgpu_device *adev, return -ENODEV; } -int -amdgpu_atomfirmware_get_vram_info(struct amdgpu_device *adev, +int amdgpu_atomfirmware_get_integrated_system_info(struct amdgpu_device *adev, + int *vram_width, int *vram_type, + int *vram_vendor) +{ + struct amdgpu_mode_info *mode_info = &adev->mode_info; + int index; + u16 data_offset, size; + union igp_info *igp_info; + u8 frev, crev; + u8 mem_type; + u32 mem_channel_number; + u32 mem_channel_width; + + index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, + integratedsysteminfo); + if (amdgpu_atom_parse_data_header(mode_info->atom_context, + index, &size, + &frev, &crev, &data_offset)) { + igp_info = (union igp_info *) + (mode_info->atom_context->bios + data_offset); + switch (frev) { + case 1: + switch (crev) { + case 11: + case 12: + mem_channel_number = igp_info->v11.umachannelnumber; + if (!mem_channel_number) + mem_channel_number = 1; + mem_type = igp_info->v11.memorytype; + if (mem_type == LpDdr5MemType) + mem_channel_width = 32; + else + mem_channel_width = 64; + if (vram_width) + *vram_width = mem_channel_number * mem_channel_width; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + break; + default: + return -EINVAL; + } + break; + case 2: + switch (crev) { + case 1: + case 2: + mem_channel_number = igp_info->v21.umachannelnumber; + if (!mem_channel_number) + mem_channel_number = 1; + mem_type = igp_info->v21.memorytype; + if (mem_type == LpDdr5MemType) + mem_channel_width = 32; + else + mem_channel_width = 64; + if (vram_width) + *vram_width = mem_channel_number * mem_channel_width; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + break; + case 3: + mem_channel_number = igp_info->v23.umachannelnumber; + if (!mem_channel_number) + mem_channel_number = 1; + mem_type = igp_info->v23.memorytype; + if (mem_type == LpDdr5MemType) + mem_channel_width = 32; + else + mem_channel_width = 64; + if (vram_width) + *vram_width = mem_channel_number * mem_channel_width; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + return 0; +} + +int amdgpu_atomfirmware_get_umc_info(struct amdgpu_device *adev, + int *vram_width, int *vram_type, + int *vram_vendor) +{ + struct amdgpu_mode_info *mode_info = &adev->mode_info; + int index; + u16 data_offset, size; + union umc_info *umc_info; + u8 frev, crev; + u8 mem_type; + u8 mem_vendor; + u32 mem_channel_number; + u32 mem_channel_width; + + index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, umc_info); + + if (amdgpu_atom_parse_data_header(mode_info->atom_context, + index, &size, + &frev, &crev, &data_offset)) { + umc_info = (union umc_info *)(mode_info->atom_context->bios + data_offset); + + if (frev == 4) { + switch (crev) { + case 0: + mem_channel_number = le32_to_cpu(umc_info->v40.channel_num); + mem_type = le32_to_cpu(umc_info->v40.vram_type); + mem_channel_width = le32_to_cpu(umc_info->v40.channel_width); + mem_vendor = RREG32(adev->bios_scratch_reg_offset + 4) & 0xF; + if (vram_vendor) + *vram_vendor = mem_vendor; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + if (vram_width) + *vram_width = mem_channel_number * (1 << mem_channel_width); + break; + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +int amdgpu_atomfirmware_get_vram_info(struct amdgpu_device *adev, int *vram_width, int *vram_type, int *vram_vendor) { struct amdgpu_mode_info *mode_info = &adev->mode_info; int index, i = 0; u16 data_offset, size; - union igp_info *igp_info; union vram_info *vram_info; - union umc_info *umc_info; union vram_module *vram_module; u8 frev, crev; u8 mem_type; @@ -392,230 +523,130 @@ amdgpu_atomfirmware_get_vram_info(struct amdgpu_device *adev, u32 mem_channel_width; u32 module_id; - if (adev->flags & AMD_IS_APU) - index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, - integratedsysteminfo); - else { - switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { - case IP_VERSION(12, 0, 0): - case IP_VERSION(12, 0, 1): - index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, umc_info); - break; - default: - index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, vram_info); - } - } + index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, vram_info); + if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, &size, &frev, &crev, &data_offset)) { - if (adev->flags & AMD_IS_APU) { - igp_info = (union igp_info *) - (mode_info->atom_context->bios + data_offset); - switch (frev) { - case 1: - switch (crev) { - case 11: - case 12: - mem_channel_number = igp_info->v11.umachannelnumber; - if (!mem_channel_number) - mem_channel_number = 1; - mem_type = igp_info->v11.memorytype; - if (mem_type == LpDdr5MemType) - mem_channel_width = 32; - else - mem_channel_width = 64; - if (vram_width) - *vram_width = mem_channel_number * mem_channel_width; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - break; - default: - return -EINVAL; - } + vram_info = (union vram_info *) + (mode_info->atom_context->bios + data_offset); + + module_id = (RREG32(adev->bios_scratch_reg_offset + 4) & 0x00ff0000) >> 16; + if (frev == 3) { + switch (crev) { + /* v30 */ + case 0: + vram_module = (union vram_module *)vram_info->v30.vram_module; + mem_vendor = (vram_module->v30.dram_vendor_id) & 0xF; + if (vram_vendor) + *vram_vendor = mem_vendor; + mem_type = vram_info->v30.memory_type; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + mem_channel_number = vram_info->v30.channel_num; + mem_channel_width = vram_info->v30.channel_width; + if (vram_width) + *vram_width = mem_channel_number * 16; break; - case 2: - switch (crev) { - case 1: - case 2: - mem_channel_number = igp_info->v21.umachannelnumber; - if (!mem_channel_number) - mem_channel_number = 1; - mem_type = igp_info->v21.memorytype; - if (mem_type == LpDdr5MemType) - mem_channel_width = 32; - else - mem_channel_width = 64; - if (vram_width) - *vram_width = mem_channel_number * mem_channel_width; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - break; - case 3: - mem_channel_number = igp_info->v23.umachannelnumber; - if (!mem_channel_number) - mem_channel_number = 1; - mem_type = igp_info->v23.memorytype; - if (mem_type == LpDdr5MemType) - mem_channel_width = 32; - else - mem_channel_width = 64; - if (vram_width) - *vram_width = mem_channel_number * mem_channel_width; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - break; - default: - return -EINVAL; + default: + return -EINVAL; + } + } else if (frev == 2) { + switch (crev) { + /* v23 */ + case 3: + if (module_id > vram_info->v23.vram_module_num) + module_id = 0; + vram_module = (union vram_module *)vram_info->v23.vram_module; + while (i < module_id) { + vram_module = (union vram_module *) + ((u8 *)vram_module + vram_module->v9.vram_module_size); + i++; } + mem_type = vram_module->v9.memory_type; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + mem_channel_number = vram_module->v9.channel_num; + mem_channel_width = vram_module->v9.channel_width; + if (vram_width) + *vram_width = mem_channel_number * (1 << mem_channel_width); + mem_vendor = (vram_module->v9.vender_rev_id) & 0xF; + if (vram_vendor) + *vram_vendor = mem_vendor; + break; + /* v24 */ + case 4: + if (module_id > vram_info->v24.vram_module_num) + module_id = 0; + vram_module = (union vram_module *)vram_info->v24.vram_module; + while (i < module_id) { + vram_module = (union vram_module *) + ((u8 *)vram_module + vram_module->v10.vram_module_size); + i++; + } + mem_type = vram_module->v10.memory_type; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + mem_channel_number = vram_module->v10.channel_num; + mem_channel_width = vram_module->v10.channel_width; + if (vram_width) + *vram_width = mem_channel_number * (1 << mem_channel_width); + mem_vendor = (vram_module->v10.vender_rev_id) & 0xF; + if (vram_vendor) + *vram_vendor = mem_vendor; + break; + /* v25 */ + case 5: + if (module_id > vram_info->v25.vram_module_num) + module_id = 0; + vram_module = (union vram_module *)vram_info->v25.vram_module; + while (i < module_id) { + vram_module = (union vram_module *) + ((u8 *)vram_module + vram_module->v11.vram_module_size); + i++; + } + mem_type = vram_module->v11.memory_type; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + mem_channel_number = vram_module->v11.channel_num; + mem_channel_width = vram_module->v11.channel_width; + if (vram_width) + *vram_width = mem_channel_number * (1 << mem_channel_width); + mem_vendor = (vram_module->v11.vender_rev_id) & 0xF; + if (vram_vendor) + *vram_vendor = mem_vendor; + break; + /* v26 */ + case 6: + if (module_id > vram_info->v26.vram_module_num) + module_id = 0; + vram_module = (union vram_module *)vram_info->v26.vram_module; + while (i < module_id) { + vram_module = (union vram_module *) + ((u8 *)vram_module + vram_module->v9.vram_module_size); + i++; + } + mem_type = vram_module->v9.memory_type; + if (vram_type) + *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); + mem_channel_number = vram_module->v9.channel_num; + mem_channel_width = vram_module->v9.channel_width; + if (vram_width) + *vram_width = mem_channel_number * (1 << mem_channel_width); + mem_vendor = (vram_module->v9.vender_rev_id) & 0xF; + if (vram_vendor) + *vram_vendor = mem_vendor; break; default: return -EINVAL; } } else { - switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { - case IP_VERSION(12, 0, 0): - case IP_VERSION(12, 0, 1): - umc_info = (union umc_info *)(mode_info->atom_context->bios + data_offset); - - if (frev == 4) { - switch (crev) { - case 0: - mem_channel_number = le32_to_cpu(umc_info->v40.channel_num); - mem_type = le32_to_cpu(umc_info->v40.vram_type); - mem_channel_width = le32_to_cpu(umc_info->v40.channel_width); - mem_vendor = RREG32(adev->bios_scratch_reg_offset + 4) & 0xF; - if (vram_vendor) - *vram_vendor = mem_vendor; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - if (vram_width) - *vram_width = mem_channel_number * (1 << mem_channel_width); - break; - default: - return -EINVAL; - } - } else - return -EINVAL; - break; - default: - vram_info = (union vram_info *) - (mode_info->atom_context->bios + data_offset); - - module_id = (RREG32(adev->bios_scratch_reg_offset + 4) & 0x00ff0000) >> 16; - if (frev == 3) { - switch (crev) { - /* v30 */ - case 0: - vram_module = (union vram_module *)vram_info->v30.vram_module; - mem_vendor = (vram_module->v30.dram_vendor_id) & 0xF; - if (vram_vendor) - *vram_vendor = mem_vendor; - mem_type = vram_info->v30.memory_type; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - mem_channel_number = vram_info->v30.channel_num; - mem_channel_width = vram_info->v30.channel_width; - if (vram_width) - *vram_width = mem_channel_number * 16; - break; - default: - return -EINVAL; - } - } else if (frev == 2) { - switch (crev) { - /* v23 */ - case 3: - if (module_id > vram_info->v23.vram_module_num) - module_id = 0; - vram_module = (union vram_module *)vram_info->v23.vram_module; - while (i < module_id) { - vram_module = (union vram_module *) - ((u8 *)vram_module + vram_module->v9.vram_module_size); - i++; - } - mem_type = vram_module->v9.memory_type; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - mem_channel_number = vram_module->v9.channel_num; - mem_channel_width = vram_module->v9.channel_width; - if (vram_width) - *vram_width = mem_channel_number * (1 << mem_channel_width); - mem_vendor = (vram_module->v9.vender_rev_id) & 0xF; - if (vram_vendor) - *vram_vendor = mem_vendor; - break; - /* v24 */ - case 4: - if (module_id > vram_info->v24.vram_module_num) - module_id = 0; - vram_module = (union vram_module *)vram_info->v24.vram_module; - while (i < module_id) { - vram_module = (union vram_module *) - ((u8 *)vram_module + vram_module->v10.vram_module_size); - i++; - } - mem_type = vram_module->v10.memory_type; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - mem_channel_number = vram_module->v10.channel_num; - mem_channel_width = vram_module->v10.channel_width; - if (vram_width) - *vram_width = mem_channel_number * (1 << mem_channel_width); - mem_vendor = (vram_module->v10.vender_rev_id) & 0xF; - if (vram_vendor) - *vram_vendor = mem_vendor; - break; - /* v25 */ - case 5: - if (module_id > vram_info->v25.vram_module_num) - module_id = 0; - vram_module = (union vram_module *)vram_info->v25.vram_module; - while (i < module_id) { - vram_module = (union vram_module *) - ((u8 *)vram_module + vram_module->v11.vram_module_size); - i++; - } - mem_type = vram_module->v11.memory_type; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - mem_channel_number = vram_module->v11.channel_num; - mem_channel_width = vram_module->v11.channel_width; - if (vram_width) - *vram_width = mem_channel_number * (1 << mem_channel_width); - mem_vendor = (vram_module->v11.vender_rev_id) & 0xF; - if (vram_vendor) - *vram_vendor = mem_vendor; - break; - /* v26 */ - case 6: - if (module_id > vram_info->v26.vram_module_num) - module_id = 0; - vram_module = (union vram_module *)vram_info->v26.vram_module; - while (i < module_id) { - vram_module = (union vram_module *) - ((u8 *)vram_module + vram_module->v9.vram_module_size); - i++; - } - mem_type = vram_module->v9.memory_type; - if (vram_type) - *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); - mem_channel_number = vram_module->v9.channel_num; - mem_channel_width = vram_module->v9.channel_width; - if (vram_width) - *vram_width = mem_channel_number * (1 << mem_channel_width); - mem_vendor = (vram_module->v9.vender_rev_id) & 0xF; - if (vram_vendor) - *vram_vendor = mem_vendor; - break; - default: - return -EINVAL; - } - } else { - /* invalid frev */ - return -EINVAL; - } - } + /* invalid frev */ + return -EINVAL; } + + } else { + return -EINVAL; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.h index 67c8d105729b..0760e4510513 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.h @@ -30,6 +30,10 @@ uint32_t amdgpu_atomfirmware_query_firmware_capability(struct amdgpu_device *ade bool amdgpu_atomfirmware_gpu_virtualization_supported(struct amdgpu_device *adev); void amdgpu_atomfirmware_scratch_regs_init(struct amdgpu_device *adev); int amdgpu_atomfirmware_allocate_fb_scratch(struct amdgpu_device *adev); +int amdgpu_atomfirmware_get_integrated_system_info(struct amdgpu_device *adev, + int *vram_width, int *vram_type, int *vram_vendor); +int amdgpu_atomfirmware_get_umc_info(struct amdgpu_device *adev, + int *vram_width, int *vram_type, int *vram_vendor); int amdgpu_atomfirmware_get_vram_info(struct amdgpu_device *adev, int *vram_width, int *vram_type, int *vram_vendor); int amdgpu_atomfirmware_get_uma_carveout_info(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 860a4405f7dd..ec74f3971732 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -34,6 +34,7 @@ #include "amdgpu_ras.h" #include "amdgpu_reset.h" #include "amdgpu_xgmi.h" +#include "amdgpu_atomfirmware.h" #include #include @@ -1747,3 +1748,31 @@ int amdgpu_gmc_init_mem_ranges(struct amdgpu_device *adev) return 0; } + +int amdgpu_gmc_get_vram_info(struct amdgpu_device *adev, + int *vram_width, int *vram_type, int *vram_vendor) +{ + int ret = 0; + + if (adev->flags & AMD_IS_APU) + return amdgpu_atomfirmware_get_integrated_system_info(adev, + vram_width, vram_type, vram_vendor); + switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { + case IP_VERSION(12, 0, 0): + case IP_VERSION(12, 0, 1): + return amdgpu_atomfirmware_get_umc_info(adev, + vram_width, vram_type, vram_vendor); + case IP_VERSION(9, 5, 0): + case IP_VERSION(9, 4, 4): + case IP_VERSION(9, 4, 3): + ret = amdgpu_atomfirmware_get_umc_info(adev, + vram_width, vram_type, vram_vendor); + if (vram_width && !ret) + *vram_width *= hweight32(adev->aid_mask); + return ret; + default: + return amdgpu_atomfirmware_get_vram_info(adev, + vram_width, vram_type, vram_vendor); + } + return 0; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index b9fdc3276e81..32e73e8ba778 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -482,4 +482,6 @@ amdgpu_gmc_query_memory_partition(struct amdgpu_device *adev); int amdgpu_gmc_init_mem_ranges(struct amdgpu_device *adev); void amdgpu_gmc_init_sw_mem_ranges(struct amdgpu_device *adev, struct amdgpu_mem_partition_info *mem_ranges); +int amdgpu_gmc_get_vram_info(struct amdgpu_device *adev, + int *vram_width, int *vram_type, int *vram_vendor); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index 2568eeaae945..fd691b2a6e21 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -767,7 +767,7 @@ static int gmc_v10_0_sw_init(struct amdgpu_ip_block *ip_block) adev->gmc.vram_type = AMDGPU_VRAM_TYPE_GDDR6; adev->gmc.vram_width = 1 * 128; /* numchan * chansize */ } else { - r = amdgpu_atomfirmware_get_vram_info(adev, + r = amdgpu_gmc_get_vram_info(adev, &vram_width, &vram_type, &vram_vendor); adev->gmc.vram_width = vram_width; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 6349e239a367..e6db87b94eb1 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -751,7 +751,7 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) spin_lock_init(&adev->gmc.invalidate_lock); - r = amdgpu_atomfirmware_get_vram_info(adev, + r = amdgpu_gmc_get_vram_info(adev, &vram_width, &vram_type, &vram_vendor); adev->gmc.vram_width = vram_width; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index f1079bd8cf00..6e184ea069ef 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -825,7 +825,7 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 1, 0)) { gmc_v12_1_init_vram_info(adev); } else { - r = amdgpu_atomfirmware_get_vram_info(adev, + r = amdgpu_gmc_get_vram_info(adev, &vram_width, &vram_type, &vram_vendor); adev->gmc.vram_width = vram_width; adev->gmc.vram_type = vram_type; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 1ca0202cfdea..d865059e884a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1823,24 +1823,37 @@ static void gmc_v9_0_save_registers(struct amdgpu_device *adev) adev->gmc.sdpif_register = RREG32_SOC15(DCE, 0, mmDCHUBBUB_SDPIF_MMIO_CNTRL_0); } -static void gmc_v9_4_3_init_vram_info(struct amdgpu_device *adev) +static void gmc_v9_0_init_vram_info(struct amdgpu_device *adev) { static const u32 regBIF_BIOS_SCRATCH_4 = 0x50; + int dev_var = adev->pdev->device & 0xF; u32 vram_info; - adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM; - adev->gmc.vram_width = 128 * 64; + if (adev->gmc.is_app_apu) { + adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM; + adev->gmc.vram_width = 128 * 64; + } else if (adev->flags & AMD_IS_APU) { + adev->gmc.vram_type = AMDGPU_VRAM_TYPE_DDR4; + adev->gmc.vram_width = 64 * 64; + } else if (amdgpu_is_multi_aid(adev)) { + adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM; + adev->gmc.vram_width = 128 * 64; - if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0)) - adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM3E; + if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0)) + adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM3E; - if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4) && - adev->rev_id == 0x3) - adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM3E; + if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4) && + adev->rev_id == 0x3) + adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM3E; - if (!(adev->flags & AMD_IS_APU) && !amdgpu_sriov_vf(adev)) { - vram_info = RREG32(regBIF_BIOS_SCRATCH_4); - adev->gmc.vram_vendor = vram_info & 0xF; + if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) && + (dev_var == 0x5)) + adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM3E; + + if (!(adev->flags & AMD_IS_APU) && !amdgpu_sriov_vf(adev)) { + vram_info = RREG32(regBIF_BIOS_SCRATCH_4); + adev->gmc.vram_vendor = vram_info & 0xF; + } } } @@ -1856,19 +1869,11 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block) spin_lock_init(&adev->gmc.invalidate_lock); - if (amdgpu_is_multi_aid(adev)) { - gmc_v9_4_3_init_vram_info(adev); - } else if (!adev->bios) { - if (adev->flags & AMD_IS_APU) { - adev->gmc.vram_type = AMDGPU_VRAM_TYPE_DDR4; - adev->gmc.vram_width = 64 * 64; - } else { - adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM; - adev->gmc.vram_width = 128 * 64; - } + if (!adev->bios) { + gmc_v9_0_init_vram_info(adev); } else { - r = amdgpu_atomfirmware_get_vram_info(adev, - &vram_width, &vram_type, &vram_vendor); + r = amdgpu_gmc_get_vram_info(adev, + &vram_width, &vram_type, &vram_vendor); if (amdgpu_sriov_vf(adev)) /* For Vega10 SR-IOV, vram_width can't be read from ATOM as RAVEN, * and DF related registers is not readable, seems hardcord is the @@ -1896,6 +1901,7 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block) adev->gmc.vram_type = vram_type; adev->gmc.vram_vendor = vram_vendor; } + switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { case IP_VERSION(9, 1, 0): case IP_VERSION(9, 2, 2): From 3539437f354bd24c98928a80d4db3a23fa2a7b19 Mon Sep 17 00:00:00 2001 From: Rafal Ostrowski Date: Tue, 24 Feb 2026 15:36:09 +0100 Subject: [PATCH 465/712] drm/amd/display: Move FPU Guards From DML To DC - Part 1 [Why] FPU guards (DC_FP_START/DC_FP_END) are required to wrap around code that can manipulates floats. To do this properly, the FPU guards must be used in a file that is not compiled as a FPU unit. If the guards are used in a file that is a FPU unit, other sections in the file that aren't guarded may be end up being compiled to use FPU operations. [How] Added DC_FP_START and DC_FP_END to DC functions that call DML functions using FPU. Reviewed-by: Dillon Varone Signed-off-by: Rafal Ostrowski Signed-off-by: Alex Hung Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/dc_fpu.c | 25 ++++++- .../gpu/drm/amd/display/amdgpu_dm/dc_fpu.h | 17 ++++- .../display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c | 2 - .../display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c | 2 - drivers/gpu/drm/amd/display/dc/core/dc.c | 5 +- .../gpu/drm/amd/display/dc/core/dc_state.c | 75 ++++++++++++++----- .../gpu/drm/amd/display/dc/core/dc_stream.c | 13 +++- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 4 +- .../dc/resource/dcn35/dcn35_resource.c | 10 ++- .../dc/resource/dcn35/dcn35_resource.h | 1 + .../dc/resource/dcn351/dcn351_resource.c | 10 ++- .../dc/resource/dcn36/dcn36_resource.c | 4 +- .../dc/resource/dcn401/dcn401_resource.c | 32 +++++--- .../dc/resource/dcn42/dcn42_resource.c | 25 +++++-- 14 files changed, 171 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c index e46f8ce41d87..8ba9b4f56f87 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c @@ -53,11 +53,30 @@ inline void dc_assert_fp_enabled(void) { int depth; - depth = __this_cpu_read(fpu_recursion_depth); + depth = this_cpu_read(fpu_recursion_depth); ASSERT(depth >= 1); } +/** + * dc_assert_fp_enabled - Check if FPU protection is enabled + * + * This function tells if the code is already under FPU protection or not. A + * function that works as an API for a set of FPU operations can use this + * function for checking if the caller invoked it after DC_FP_START(). For + * example, take a look at dcn20_fpu.c file. + * + * Similar to dc_assert_fp_enabled, but does not assert, returns status instead. + */ +inline bool dc_is_fp_enabled(void) +{ + int depth; + + depth = this_cpu_read(fpu_recursion_depth); + + return (depth >= 1); +} + /** * dc_fpu_begin - Enables FPU protection * @function_name: A string containing the function name for debug purposes @@ -77,7 +96,7 @@ void dc_fpu_begin(const char *function_name, const int line) WARN_ON_ONCE(!in_task()); preempt_disable(); - depth = __this_cpu_inc_return(fpu_recursion_depth); + depth = this_cpu_inc_return(fpu_recursion_depth); if (depth == 1) { BUG_ON(!kernel_fpu_available()); kernel_fpu_begin(); @@ -100,7 +119,7 @@ void dc_fpu_end(const char *function_name, const int line) { int depth; - depth = __this_cpu_dec_return(fpu_recursion_depth); + depth = this_cpu_dec_return(fpu_recursion_depth); if (depth == 0) { kernel_fpu_end(); } else { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h index 4e921632bc4e..5e95419d3798 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h @@ -28,15 +28,30 @@ #define __DC_FPU_H__ void dc_assert_fp_enabled(void); +bool dc_is_fp_enabled(void); void dc_fpu_begin(const char *function_name, const int line); void dc_fpu_end(const char *function_name, const int line); #ifndef _LINUX_FPU_COMPILATION_UNIT #define DC_FP_START() dc_fpu_begin(__func__, __LINE__) #define DC_FP_END() dc_fpu_end(__func__, __LINE__) +#ifdef CONFIG_DRM_AMD_DC_FP +#define DC_RUN_WITH_PREEMPTION_ENABLED(code) \ + do { \ + bool dc_fp_enabled = dc_is_fp_enabled(); \ + if (dc_fp_enabled) \ + DC_FP_END(); \ + code; \ + if (dc_fp_enabled) \ + DC_FP_START(); \ + } while (0) +#else +#define DC_RUN_WITH_PREEMPTION_ENABLED(code) code +#endif // !CONFIG_DRM_AMD_DC_FP #else #define DC_FP_START() BUILD_BUG() #define DC_FP_END() BUILD_BUG() -#endif +#define DC_RUN_WITH_PREEMPTION_ENABLED(code) code +#endif // !_LINUX_FPU_COMPILATION_UNIT #endif /* __DC_FPU_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c index b48522480dfd..fe0bb383ddc1 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c @@ -421,10 +421,8 @@ static void dcn3_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) clk_mgr_base->bw_params->dc_mode_softmax_memclk = dcn30_smu_get_dc_mode_max_dpm_freq(clk_mgr, PPCLK_UCLK); /* Refresh bounding box */ - DC_FP_START(); clk_mgr_base->ctx->dc->res_pool->funcs->update_bw_bounding_box( clk_mgr->base.ctx->dc, clk_mgr_base->bw_params); - DC_FP_END(); } static bool dcn3_is_smu_present(struct clk_mgr *clk_mgr_base) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index 2856b0337e87..4007ab353ffd 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -1059,11 +1059,9 @@ static void dcn32_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) if (!clk_mgr->dpm_present) dcn32_patch_dpm_table(clk_mgr_base->bw_params); - DC_FP_START(); /* Refresh bounding box */ clk_mgr_base->ctx->dc->res_pool->funcs->update_bw_bounding_box( clk_mgr->base.ctx->dc, clk_mgr_base->bw_params); - DC_FP_END(); } static bool dcn32_are_clock_states_equal(struct dc_clocks *a, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 79a4680ddb27..0aec8d01c036 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1096,11 +1096,8 @@ static bool dc_construct(struct dc *dc, #ifdef CONFIG_DRM_AMD_DC_FP dc->clk_mgr->force_smu_not_present = init_params->force_smu_not_present; - if (dc->res_pool->funcs->update_bw_bounding_box) { - DC_FP_START(); + if (dc->res_pool->funcs->update_bw_bounding_box) dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params); - DC_FP_END(); - } dc->soc_and_ip_translator = dc_create_soc_and_ip_translator(dc_ctx->dce_version); if (!dc->soc_and_ip_translator) goto fail; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c b/drivers/gpu/drm/amd/display/dc/core/dc_state.c index a40e5c44143f..13d334c2cb6b 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c @@ -205,19 +205,33 @@ struct dc_state *dc_state_create(struct dc *dc, struct dc_state_create_params *p state->power_source = params ? params->power_source : DC_POWER_SOURCE_AC; #ifdef CONFIG_DRM_AMD_DC_FP + bool status; + if (dc->debug.using_dml2) { - if (!dml2_create(dc, &dc->dml2_options, &state->bw_ctx.dml2)) { + DC_FP_START(); + status = dml2_create(dc, &dc->dml2_options, &state->bw_ctx.dml2); + DC_FP_END(); + + if (!status) { dc_state_release(state); return NULL; } - if (dc->caps.dcmode_power_limits_present && !dml2_create(dc, &dc->dml2_dc_power_options, &state->bw_ctx.dml2_dc_power_source)) { - dc_state_release(state); - return NULL; + if (dc->caps.dcmode_power_limits_present) { + bool status; + + DC_FP_START(); + status = dml2_create(dc, &dc->dml2_dc_power_options, &state->bw_ctx.dml2_dc_power_source); + DC_FP_END(); + + if (!status) { + dc_state_release(state); + return NULL; + } } + } -#endif - +#endif // CONFIG_DRM_AMD_DC_FP kref_init(&state->refcount); return state; @@ -235,14 +249,20 @@ void dc_state_copy(struct dc_state *dst_state, struct dc_state *src_state) #ifdef CONFIG_DRM_AMD_DC_FP dst_state->bw_ctx.dml2 = dst_dml2; - if (src_state->bw_ctx.dml2) + if (src_state->bw_ctx.dml2) { + DC_FP_START(); dml2_copy(dst_state->bw_ctx.dml2, src_state->bw_ctx.dml2); + DC_FP_END(); + } dst_state->bw_ctx.dml2_dc_power_source = dst_dml2_dc_power_source; - if (src_state->bw_ctx.dml2_dc_power_source) - dml2_copy(dst_state->bw_ctx.dml2_dc_power_source, src_state->bw_ctx.dml2_dc_power_source); -#endif + if (src_state->bw_ctx.dml2_dc_power_source) { + DC_FP_START(); + dml2_copy(dst_state->bw_ctx.dml2_dc_power_source, src_state->bw_ctx.dml2_dc_power_source); + DC_FP_END(); + } +#endif // CONFIG_DRM_AMD_DC_FP /* context refcount should not be overridden */ dst_state->refcount = refcount; } @@ -258,22 +278,35 @@ struct dc_state *dc_state_create_copy(struct dc_state *src_state) dc_state_copy_internal(new_state, src_state); #ifdef CONFIG_DRM_AMD_DC_FP + bool status; + new_state->bw_ctx.dml2 = NULL; new_state->bw_ctx.dml2_dc_power_source = NULL; - if (src_state->bw_ctx.dml2 && - !dml2_create_copy(&new_state->bw_ctx.dml2, src_state->bw_ctx.dml2)) { - dc_state_release(new_state); - return NULL; + if (src_state->bw_ctx.dml2) { + DC_FP_START(); + status = dml2_create_copy(&new_state->bw_ctx.dml2, src_state->bw_ctx.dml2); + DC_FP_END(); + + if (!status) { + dc_state_release(new_state); + return NULL; + } } - if (src_state->bw_ctx.dml2_dc_power_source && - !dml2_create_copy(&new_state->bw_ctx.dml2_dc_power_source, src_state->bw_ctx.dml2_dc_power_source)) { - dc_state_release(new_state); - return NULL; - } -#endif + if (src_state->bw_ctx.dml2_dc_power_source) { + DC_FP_START(); + status = dml2_create_copy(&new_state->bw_ctx.dml2_dc_power_source, + src_state->bw_ctx.dml2_dc_power_source); + DC_FP_END(); + + if (!status) { + dc_state_release(new_state); + return NULL; + } + } +#endif // CONFIG_DRM_AMD_DC_FP kref_init(&new_state->refcount); return new_state; @@ -351,11 +384,13 @@ static void dc_state_free(struct kref *kref) dc_state_destruct(state); #ifdef CONFIG_DRM_AMD_DC_FP + DC_FP_START(); dml2_destroy(state->bw_ctx.dml2); state->bw_ctx.dml2 = 0; dml2_destroy(state->bw_ctx.dml2_dc_power_source); state->bw_ctx.dml2_dc_power_source = 0; + DC_FP_END(); #endif kvfree(state); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index daa7ab362239..e16de323f39c 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -42,6 +42,13 @@ #define MAX(x, y) ((x > y) ? x : y) #endif +#include "dc_fpu.h" + +#if !defined(DC_RUN_WITH_PREEMPTION_ENABLED) +#define DC_RUN_WITH_PREEMPTION_ENABLED(code) code +#endif // !DC_RUN_WITH_PREEMPTION_ENABLED + + /******************************************************************************* * Private functions ******************************************************************************/ @@ -170,12 +177,14 @@ struct dc_stream_state *dc_create_stream_for_sink( if (sink == NULL) goto fail; - stream = kzalloc_obj(struct dc_stream_state, GFP_ATOMIC); + DC_RUN_WITH_PREEMPTION_ENABLED(stream = kzalloc_obj(struct dc_stream_state, GFP_ATOMIC)); if (stream == NULL) goto fail; - stream->update_scratch = kzalloc((int32_t) dc_update_scratch_space_size(), GFP_ATOMIC); + DC_RUN_WITH_PREEMPTION_ENABLED(stream->update_scratch = + kzalloc((int32_t) dc_update_scratch_space_size(), + GFP_ATOMIC)); if (stream->update_scratch == NULL) goto fail; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 897f8af4b65a..60ad606c06ec 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -368,8 +368,8 @@ void dcn401_init_hw(struct dc *dc) dc->res_pool->funcs->update_bw_bounding_box && dc->clk_mgr && dc->clk_mgr->bw_params) { /* update bounding box if FAMS2 disabled, or if dchub clk has changed */ - dc->res_pool->funcs->update_bw_bounding_box(dc, - dc->clk_mgr->bw_params); + if (dc->clk_mgr) + dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params); } } } diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index f4a751027065..825ecaf9c580 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -1738,9 +1738,11 @@ static enum dc_status dcn35_validate_bandwidth(struct dc *dc, { bool out = false; + DC_FP_START(); out = dml2_validate(dc, context, context->power_source == DC_POWER_SOURCE_DC ? context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2, validate_mode); + DC_FP_END(); if (validate_mode != DC_VALIDATE_MODE_AND_PROGRAMMING) return out ? DC_OK : DC_FAIL_BANDWIDTH_VALIDATE; @@ -1774,6 +1776,12 @@ static int populate_dml_pipes_from_context_fpu(struct dc *dc, return ret; } +void dcn35_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn35_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} static struct resource_funcs dcn35_res_pool_funcs = { .destroy = dcn35_destroy_resource_pool, .link_enc_create = dcn35_link_encoder_create, @@ -1795,7 +1803,7 @@ static struct resource_funcs dcn35_res_pool_funcs = { .find_first_free_match_stream_enc_for_link = dcn10_find_first_free_match_stream_enc_for_link, .acquire_post_bldn_3dlut = dcn30_acquire_post_bldn_3dlut, .release_post_bldn_3dlut = dcn30_release_post_bldn_3dlut, - .update_bw_bounding_box = dcn35_update_bw_bounding_box_fpu, + .update_bw_bounding_box = dcn35_update_bw_bounding_box, .patch_unknown_plane_state = dcn35_patch_unknown_plane_state, .get_panel_config_defaults = dcn35_get_panel_config_defaults, .get_preferred_eng_id_dpia = dcn35_get_preferred_eng_id_dpia, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.h index 9c56ae76e0c7..6c2c61c711b9 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.h @@ -312,4 +312,5 @@ struct resource_pool *dcn35_create_resource_pool( #define DPP_REG_LIST_DCN35_RI(id)\ DPP_REG_LIST_DCN30_COMMON_RI(id) +void dcn35_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params); #endif /* _DCN35_RESOURCE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index bf8e83db9cc6..00286cba5742 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -1718,9 +1718,11 @@ static enum dc_status dcn351_validate_bandwidth(struct dc *dc, { bool out = false; + DC_FP_START(); out = dml2_validate(dc, context, context->power_source == DC_POWER_SOURCE_DC ? context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2, validate_mode); + DC_FP_END(); if (validate_mode != DC_VALIDATE_MODE_AND_PROGRAMMING) return out ? DC_OK : DC_FAIL_BANDWIDTH_VALIDATE; @@ -1747,6 +1749,12 @@ static int populate_dml_pipes_from_context_fpu(struct dc *dc, } +static void dcn351_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn351_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} static struct resource_funcs dcn351_res_pool_funcs = { .destroy = dcn351_destroy_resource_pool, .link_enc_create = dcn35_link_encoder_create, @@ -1768,7 +1776,7 @@ static struct resource_funcs dcn351_res_pool_funcs = { .find_first_free_match_stream_enc_for_link = dcn10_find_first_free_match_stream_enc_for_link, .acquire_post_bldn_3dlut = dcn30_acquire_post_bldn_3dlut, .release_post_bldn_3dlut = dcn30_release_post_bldn_3dlut, - .update_bw_bounding_box = dcn351_update_bw_bounding_box_fpu, + .update_bw_bounding_box = dcn351_update_bw_bounding_box, .patch_unknown_plane_state = dcn35_patch_unknown_plane_state, .get_panel_config_defaults = dcn35_get_panel_config_defaults, .get_preferred_eng_id_dpia = dcn351_get_preferred_eng_id_dpia, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index fec0911ce22c..7c4519d57e4d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -1725,9 +1725,11 @@ static enum dc_status dcn35_validate_bandwidth(struct dc *dc, { bool out = false; + DC_FP_START(); out = dml2_validate(dc, context, context->power_source == DC_POWER_SOURCE_DC ? context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2, validate_mode); + DC_FP_END(); if (validate_mode != DC_VALIDATE_MODE_AND_PROGRAMMING) return out ? DC_OK : DC_FAIL_BANDWIDTH_VALIDATE; @@ -1775,7 +1777,7 @@ static struct resource_funcs dcn36_res_pool_funcs = { .find_first_free_match_stream_enc_for_link = dcn10_find_first_free_match_stream_enc_for_link, .acquire_post_bldn_3dlut = dcn30_acquire_post_bldn_3dlut, .release_post_bldn_3dlut = dcn30_release_post_bldn_3dlut, - .update_bw_bounding_box = dcn35_update_bw_bounding_box_fpu, + .update_bw_bounding_box = dcn35_update_bw_bounding_box, .patch_unknown_plane_state = dcn20_patch_unknown_plane_state, .get_panel_config_defaults = dcn35_get_panel_config_defaults, .get_preferred_eng_id_dpia = dcn36_get_preferred_eng_id_dpia, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c index dc0f0ab27ce0..cb93bfbe9e9e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c @@ -1643,8 +1643,10 @@ static struct dc_cap_funcs cap_funcs = { .get_subvp_en = dcn32_subvp_in_use, }; -static void dcn401_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +static void dcn401_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) { + dc_assert_fp_enabled(); + /* re-calculate the available MALL size if required */ if (bw_params->num_channels > 0) { dc->caps.max_cab_allocation_bytes = dcn401_calc_num_avail_chans_for_mall( @@ -1653,17 +1655,19 @@ static void dcn401_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *b dc->caps.mall_size_total = dc->caps.max_cab_allocation_bytes; } - DC_FP_START(); - if (dc->debug.using_dml2 && dc->current_state && dc->current_state->bw_ctx.dml2) dml2_reinit(dc, &dc->dml2_options, &dc->current_state->bw_ctx.dml2); if (dc->debug.using_dml2 && dc->current_state && dc->current_state->bw_ctx.dml2_dc_power_source) dml2_reinit(dc, &dc->dml2_dc_power_options, &dc->current_state->bw_ctx.dml2_dc_power_source); - - DC_FP_END(); } +static void dcn401_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn401_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} enum dc_status dcn401_patch_unknown_plane_state(struct dc_plane_state *plane_state) { plane_state->tiling_info.gfxversion = DcGfxAddr3; @@ -1688,10 +1692,13 @@ enum dc_status dcn401_validate_bandwidth(struct dc *dc, } } - if (dc->debug.using_dml2) + if (dc->debug.using_dml2) { + DC_FP_START(); status = dml2_validate(dc, context, context->power_source == DC_POWER_SOURCE_DC ? context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2, validate_mode) ? DC_OK : DC_FAIL_BANDWIDTH_VALIDATE; + DC_FP_END(); + } if (validate_mode == DC_VALIDATE_MODE_AND_PROGRAMMING && status == DC_OK && dc_state_is_subvp_in_use(context)) { /* check new stream configuration still supports cursor if subvp used */ @@ -1710,10 +1717,13 @@ enum dc_status dcn401_validate_bandwidth(struct dc *dc, if (validate_mode == DC_VALIDATE_MODE_AND_PROGRAMMING && status == DC_FAIL_HW_CURSOR_SUPPORT) { /* attempt to validate again with subvp disabled due to cursor */ - if (dc->debug.using_dml2) + if (dc->debug.using_dml2) { + DC_FP_START(); status = dml2_validate(dc, context, context->power_source == DC_POWER_SOURCE_DC ? context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2, validate_mode) ? DC_OK : DC_FAIL_BANDWIDTH_VALIDATE; + DC_FP_END(); + } } return status; @@ -1722,9 +1732,13 @@ enum dc_status dcn401_validate_bandwidth(struct dc *dc, void dcn401_prepare_mcache_programming(struct dc *dc, struct dc_state *context) { - if (dc->debug.using_dml21) + if (dc->debug.using_dml21) { + DC_FP_START(); dml2_prepare_mcache_programming(dc, context, - context->power_source == DC_POWER_SOURCE_DC ? context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2); + context->power_source == DC_POWER_SOURCE_DC ? + context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2); + DC_FP_END(); + } } static void dcn401_build_pipe_pix_clk_params(struct pipe_ctx *pipe_ctx) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 9d6a989d6dd2..b9532ebcced4 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -1696,37 +1696,50 @@ static void dcn42_destroy_resource_pool(struct resource_pool **pool) static struct dc_cap_funcs cap_funcs = { .get_dcc_compression_cap = dcn20_get_dcc_compression_cap}; +static void dcn42_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) +{ + dc_assert_fp_enabled(); + + if (dc->current_state && dc->current_state->bw_ctx.dml2) + dml2_reinit(dc, &dc->dml2_options, &dc->current_state->bw_ctx.dml2); +} + static void dcn42_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) { DC_FP_START(); - if (dc->current_state && dc->current_state->bw_ctx.dml2) - dml2_reinit(dc, &dc->dml2_options, &dc->current_state->bw_ctx.dml2); + dcn42_update_bw_bounding_box_fpu(dc, bw_params); DC_FP_END(); } - enum dc_status dcn42_validate_bandwidth(struct dc *dc, struct dc_state *context, enum dc_validate_mode validate_mode) { bool out = false; + DC_FP_START(); + out = dml2_validate(dc, context, context->bw_ctx.dml2, validate_mode); - DC_FP_START(); + if (validate_mode == DC_VALIDATE_MODE_AND_PROGRAMMING) { /*not required for mode enumeration*/ dcn42_decide_zstate_support(dc, context); } + DC_FP_END(); + return out ? DC_OK : DC_FAIL_BANDWIDTH_VALIDATE; } void dcn42_prepare_mcache_programming(struct dc *dc, struct dc_state *context) { - if (dc->debug.using_dml21) + if (dc->debug.using_dml21) { + DC_FP_START(); dml2_prepare_mcache_programming(dc, context, context->power_source == DC_POWER_SOURCE_DC ? - context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2); + context->bw_ctx.dml2_dc_power_source : context->bw_ctx.dml2); + DC_FP_END(); + } } /* Create a minimal link encoder object not associated with a particular * physical connector. From 4bb2f0721ed8a2a70f864b9358bd6cd4d92199b3 Mon Sep 17 00:00:00 2001 From: Rafal Ostrowski Date: Wed, 18 Feb 2026 16:19:47 +0100 Subject: [PATCH 466/712] drm/amd/display: Move FPU Guards From DML To DC - Part 2 [Why] FPU guards (DC_FP_START/DC_FP_END) are required to wrap around code that can manipulates floats. To do this properly, the FPU guards must be used in a file that is not compiled as a FPU unit. If the guards are used in a file that is a FPU unit, other sections in the file that aren't guarded may be end up being compiled to use FPU operations. [How] Removed DC_FP_START and DC_FP_END. Reviewed-by: Dillon Varone Signed-off-by: Rafal Ostrowski Signed-off-by: Alex Hung Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/dml2_0/Makefile | 75 +--- .../display/dc/dml2_0/dml21/dml21_wrapper.c | 379 +---------------- .../display/dc/dml2_0/dml21/dml21_wrapper.h | 30 -- .../dc/dml2_0/dml21/dml21_wrapper_fpu.c | 381 ++++++++++++++++++ .../dc/dml2_0/dml21/dml21_wrapper_fpu.h | 60 +++ .../drm/amd/display/dc/dml2_0/dml2_wrapper.c | 21 +- .../amd/display/dc/dml2_0/dml2_wrapper_fpu.c | 9 +- 7 files changed, 484 insertions(+), 471 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile index 70d9f2cd0b60..5cb3035b814c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile @@ -53,25 +53,29 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/inc subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/inc subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/ -CFLAGS_$(AMDDALPATH)/dc/dml2_0/display_mode_core.o := $(dml2_ccflags) $(frame_warn_flag) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/display_mode_util.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_wrapper_fpu.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_utils.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_policy.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_translation_helper.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_mall_phantom.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml_display_rq_dlg_calc.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_dc_resource_mgmt.o := $(dml2_ccflags) +# Add FPU flags to all dml2 files by default, remove NO_FPU flags. +# FPU flags step 1: Find all .c files in dal/dc/dml2_0 and it's subfolders +DML2_ABS_PATH := $(FULL_AMD_DISPLAY_PATH)/dc/dml2_0 +DML2_C_FILES := $(shell find $(DML2_ABS_PATH) -name '*.c' -type f) +# FPU flags step 2: Convert to .o and make paths relative to $(AMDDALPATH)/dc/dml2_0/ +DML2_RELATIVE_O_FILES := $(patsubst $(DML2_ABS_PATH)/%,dc/dml2_0/%,$(patsubst %.c,%.o,$(DML2_C_FILES))) + +# FPU flags step 3: Apply FPU flags to all .o files from dal/dc/dml2_0 and it's subfolders +$(foreach obj,$(DML2_RELATIVE_O_FILES),$(eval CFLAGS_$(AMDDALPATH)/$(obj) := $(dml2_ccflags))) +$(foreach obj,$(DML2_RELATIVE_O_FILES),$(eval CFLAGS_REMOVE_$(AMDDALPATH)/$(obj) := $(dml2_rcflags))) + +# FPU flags step 4: Replace CFLAGS per file for files with additional flags beyond dml2_ccflags and dml2_rcflags +CFLAGS_$(AMDDALPATH)/dc/dml2_0/display_mode_core.o := $(dml2_ccflags) $(frame_warn_flag) +CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_ccflags) $(frame_warn_flag) +CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_ccflags) $(frame_warn_flag) +CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_wrapper.o := $(dml2_rcflags) +CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_wrapper.o := $(dml2_rcflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/display_mode_core.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/display_mode_util.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_wrapper_fpu.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_utils.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_policy.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_translation_helper.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_mall_phantom.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml_display_rq_dlg_calc.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_dc_resource_mgmt.o := $(dml2_rcflags) +CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_rcflags) +CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_rcflags) +CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_wrapper.o := $(dml2_ccflags) +CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_wrapper.o := $(dml2_ccflags) DML2 = display_mode_core.o display_mode_util.o dml2_wrapper_fpu.o dml2_wrapper.o \ dml2_utils.o dml2_policy.o dml2_translation_helper.o dml2_dc_resource_mgmt.o dml2_mall_phantom.o \ @@ -81,42 +85,6 @@ AMD_DAL_DML2 = $(addprefix $(AMDDALPATH)/dc/dml2_0/,$(DML2)) AMD_DISPLAY_FILES += $(AMD_DAL_DML2) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_ccflags) $(frame_warn_flag) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_ccflags) $(frame_warn_flag) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn42.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_translation_helper.o := $(dml2_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_utils.o := $(dml2_ccflags) - -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn42.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_translation_helper.o := $(dml2_rcflags) -CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_utils.o := $(dml2_rcflags) - DML21 := src/dml2_top/dml2_top_interfaces.o DML21 += src/dml2_top/dml2_top_soc15.o DML21 += src/dml2_core/dml2_core_dcn4.o @@ -134,6 +102,7 @@ DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o DML21 += src/dml2_standalone_libraries/lib_float_math.o DML21 += dml21_translation_helper.o DML21 += dml21_wrapper.o +DML21 += dml21_wrapper_fpu.o DML21 += dml21_utils.o AMD_DAL_DML21 = $(addprefix $(AMDDALPATH)/dc/dml2_0/dml21/,$(DML21)) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c index 2623e917ec28..1a98578f223c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c @@ -9,6 +9,10 @@ #include "dml21_utils.h" #include "dml21_translation_helper.h" #include "dml2_dc_resource_mgmt.h" +#include "dml2_wrapper.h" +#include "dml2_wrapper_fpu.h" +#include "dml21_wrapper.h" +#include "dml21_wrapper_fpu.h" #include "dc_fpu.h" #if !defined(DC_RUN_WITH_PREEMPTION_ENABLED) @@ -40,44 +44,11 @@ static bool dml21_allocate_memory(struct dml2_context **dml_ctx) return true; } -static void dml21_populate_configuration_options(const struct dc *in_dc, - struct dml2_context *dml_ctx, - const struct dml2_configuration_options *config) -{ - dml_ctx->config = *config; - - /* UCLK P-State options */ - if (in_dc->debug.dml21_force_pstate_method) { - dml_ctx->config.pmo.force_pstate_method_enable = true; - for (int i = 0; i < MAX_PIPES; i++) - dml_ctx->config.pmo.force_pstate_method_values[i] = in_dc->debug.dml21_force_pstate_method_values[i]; - } else { - dml_ctx->config.pmo.force_pstate_method_enable = false; - } -} - -static void dml21_init(const struct dc *in_dc, struct dml2_context *dml_ctx, const struct dml2_configuration_options *config) -{ - - dml_ctx->architecture = dml2_architecture_21; - - dml21_populate_configuration_options(in_dc, dml_ctx, config); - - DC_FP_START(); - - dml21_populate_dml_init_params(&dml_ctx->v21.dml_init, &dml_ctx->config, in_dc); - - dml2_initialize_instance(&dml_ctx->v21.dml_init); - - DC_FP_END(); -} - bool dml21_create(const struct dc *in_dc, struct dml2_context **dml_ctx, const struct dml2_configuration_options *config) { /* Allocate memory for initializing DML21 instance */ - if (!dml21_allocate_memory(dml_ctx)) { + if (!dml21_allocate_memory(dml_ctx)) return false; - } dml21_init(in_dc, *dml_ctx, config); @@ -90,337 +61,6 @@ void dml21_destroy(struct dml2_context *dml2) vfree(dml2->v21.mode_programming.programming); } -static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_state *context, struct resource_context *out_new_hw_state, - struct dml2_context *in_ctx, unsigned int pipe_cnt) -{ - unsigned int dml_prog_idx = 0, dc_pipe_index = 0, num_dpps_required = 0; - struct dml2_per_plane_programming *pln_prog = NULL; - struct dml2_per_stream_programming *stream_prog = NULL; - struct pipe_ctx *dc_main_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__]; - struct pipe_ctx *dc_phantom_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}; - int num_pipes; - unsigned int dml_phantom_prog_idx; - - context->bw_ctx.bw.dcn.clk.dppclk_khz = 0; - - /* copy global DCHUBBUB arbiter registers */ - memcpy(&context->bw_ctx.bw.dcn.arb_regs, &in_ctx->v21.mode_programming.programming->global_regs.arb_regs, sizeof(struct dml2_display_arb_regs)); - - /* legacy only */ - context->bw_ctx.bw.dcn.compbuf_size_kb = (int)in_ctx->v21.mode_programming.programming->global_regs.arb_regs.compbuf_size * 64; - - context->bw_ctx.bw.dcn.mall_ss_size_bytes = 0; - context->bw_ctx.bw.dcn.mall_ss_psr_active_size_bytes = 0; - context->bw_ctx.bw.dcn.mall_subvp_size_bytes = 0; - - /* phantom's start after main planes */ - dml_phantom_prog_idx = in_ctx->v21.mode_programming.programming->display_config.num_planes; - - for (dml_prog_idx = 0; dml_prog_idx < DML2_MAX_PLANES; dml_prog_idx++) { - pln_prog = &in_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; - - if (!pln_prog->plane_descriptor) - continue; - - stream_prog = &in_ctx->v21.mode_programming.programming->stream_programming[pln_prog->plane_descriptor->stream_index]; - num_dpps_required = pln_prog->num_dpps_required; - - if (num_dpps_required == 0) { - continue; - } - num_pipes = dml21_find_dc_pipes_for_plane(dc, context, in_ctx, dc_main_pipes, dc_phantom_pipes, dml_prog_idx); - - if (num_pipes <= 0) - continue; - - /* program each pipe */ - for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { - dml21_program_dc_pipe(in_ctx, context, dc_main_pipes[dc_pipe_index], pln_prog, stream_prog); - - if (pln_prog->phantom_plane.valid && dc_phantom_pipes[dc_pipe_index]) { - dml21_program_dc_pipe(in_ctx, context, dc_phantom_pipes[dc_pipe_index], pln_prog, stream_prog); - } - } - - /* copy per plane mcache allocation */ - memcpy(&context->bw_ctx.bw.dcn.mcache_allocations[dml_prog_idx], &pln_prog->mcache_allocation, sizeof(struct dml2_mcache_surface_allocation)); - if (pln_prog->phantom_plane.valid) { - memcpy(&context->bw_ctx.bw.dcn.mcache_allocations[dml_phantom_prog_idx], - &pln_prog->phantom_plane.mcache_allocation, - sizeof(struct dml2_mcache_surface_allocation)); - - dml_phantom_prog_idx++; - } - } - - /* assign global clocks */ - context->bw_ctx.bw.dcn.clk.bw_dppclk_khz = context->bw_ctx.bw.dcn.clk.dppclk_khz; - context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz; - if (in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values > 1) { - context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = - in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values] * 1000; - } else { - context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[0] * 1000; - } - - if (in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values > 1) { - context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = - in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values] * 1000; - } else { - context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[0] * 1000; - } - - /* get global mall allocation */ - if (dc->res_pool->funcs->calculate_mall_ways_from_bytes) { - context->bw_ctx.bw.dcn.clk.num_ways = dc->res_pool->funcs->calculate_mall_ways_from_bytes(dc, context->bw_ctx.bw.dcn.mall_subvp_size_bytes); - } else { - context->bw_ctx.bw.dcn.clk.num_ways = 0; - } -} - -static void dml21_prepare_mcache_params(struct dml2_context *dml_ctx, struct dc_state *context, struct dc_mcache_params *mcache_params) -{ - int dc_plane_idx = 0; - int dml_prog_idx, stream_idx, plane_idx; - struct dml2_per_plane_programming *pln_prog = NULL; - - for (stream_idx = 0; stream_idx < context->stream_count; stream_idx++) { - for (plane_idx = 0; plane_idx < context->stream_status[stream_idx].plane_count; plane_idx++) { - dml_prog_idx = map_plane_to_dml21_display_cfg(dml_ctx, context->streams[stream_idx]->stream_id, context->stream_status[stream_idx].plane_states[plane_idx], context); - if (dml_prog_idx == INVALID) { - continue; - } - pln_prog = &dml_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; - mcache_params[dc_plane_idx].valid = pln_prog->mcache_allocation.valid; - mcache_params[dc_plane_idx].num_mcaches_plane0 = pln_prog->mcache_allocation.num_mcaches_plane0; - mcache_params[dc_plane_idx].num_mcaches_plane1 = pln_prog->mcache_allocation.num_mcaches_plane1; - mcache_params[dc_plane_idx].requires_dedicated_mall_mcache = pln_prog->mcache_allocation.requires_dedicated_mall_mcache; - mcache_params[dc_plane_idx].last_slice_sharing.plane0_plane1 = pln_prog->mcache_allocation.last_slice_sharing.plane0_plane1; - memcpy(mcache_params[dc_plane_idx].mcache_x_offsets_plane0, - pln_prog->mcache_allocation.mcache_x_offsets_plane0, - sizeof(int) * (DML2_MAX_MCACHES + 1)); - memcpy(mcache_params[dc_plane_idx].mcache_x_offsets_plane1, - pln_prog->mcache_allocation.mcache_x_offsets_plane1, - sizeof(int) * (DML2_MAX_MCACHES + 1)); - dc_plane_idx++; - } - } -} - -static bool dml21_mode_check_and_programming(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx) -{ - bool result = false; - struct dml2_build_mode_programming_in_out *mode_programming = &dml_ctx->v21.mode_programming; - struct dc_mcache_params mcache_params[MAX_PLANES] = {0}; - - memset(&dml_ctx->v21.display_config, 0, sizeof(struct dml2_display_cfg)); - memset(&dml_ctx->v21.dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping)); - memset(&dml_ctx->v21.mode_programming.dml2_instance->scratch.build_mode_programming_locals.mode_programming_params, 0, sizeof(struct dml2_core_mode_programming_in_out)); - - if (!context) - return true; - - if (context->stream_count == 0) { - dml21_init_min_clocks_for_dc_state(dml_ctx, context); - dml21_build_fams2_programming(in_dc, context, dml_ctx); - return true; - } - - /* scrub phantom's from current dc_state */ - dml_ctx->config.svp_pstate.callbacks.remove_phantom_streams_and_planes(in_dc, context); - dml_ctx->config.svp_pstate.callbacks.release_phantom_streams_and_planes(in_dc, context); - - /* Populate stream, plane mappings and other fields in display config. */ - result = dml21_map_dc_state_into_dml_display_cfg(in_dc, context, dml_ctx); - if (!result) - return false; - - DC_FP_START(); - result = dml2_build_mode_programming(mode_programming); - DC_FP_END(); - if (!result) - return false; - - /* Check and map HW resources */ - if (result && !dml_ctx->config.skip_hw_state_mapping) { - dml21_map_hw_resources(dml_ctx); - dml2_map_dc_pipes(dml_ctx, context, NULL, &dml_ctx->v21.dml_to_dc_pipe_mapping, in_dc->current_state); - /* if subvp phantoms are present, expand them into dc context */ - dml21_handle_phantom_streams_planes(in_dc, context, dml_ctx); - - if (in_dc->res_pool->funcs->program_mcache_pipe_config) { - //Prepare mcache params for each plane based on mcache output from DML - dml21_prepare_mcache_params(dml_ctx, context, mcache_params); - - //populate mcache regs to each pipe - dml_ctx->config.callbacks.allocate_mcache(context, mcache_params); - } - } - - /* Copy DML CLK, WM and REG outputs to bandwidth context */ - if (result && !dml_ctx->config.skip_hw_state_mapping) { - dml21_calculate_rq_and_dlg_params(in_dc, context, &context->res_ctx, dml_ctx, in_dc->res_pool->pipe_count); - dml21_copy_clocks_to_dc_state(dml_ctx, context); - dml21_extract_watermark_sets(in_dc, &context->bw_ctx.bw.dcn.watermarks, dml_ctx); - dml21_build_fams2_programming(in_dc, context, dml_ctx); - } - - return true; -} - -static bool dml21_check_mode_support(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx) -{ - bool is_supported = false; - struct dml2_initialize_instance_in_out *dml_init = &dml_ctx->v21.dml_init; - struct dml2_check_mode_supported_in_out *mode_support = &dml_ctx->v21.mode_support; - - memset(&dml_ctx->v21.display_config, 0, sizeof(struct dml2_display_cfg)); - memset(&dml_ctx->v21.dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping)); - memset(&dml_ctx->v21.mode_programming.dml2_instance->scratch.check_mode_supported_locals.mode_support_params, 0, sizeof(struct dml2_core_mode_support_in_out)); - - if (!context || context->stream_count == 0) - return true; - - /* Scrub phantom's from current dc_state */ - dml_ctx->config.svp_pstate.callbacks.remove_phantom_streams_and_planes(in_dc, context); - dml_ctx->config.svp_pstate.callbacks.release_phantom_streams_and_planes(in_dc, context); - - mode_support->dml2_instance = dml_init->dml2_instance; - dml21_map_dc_state_into_dml_display_cfg(in_dc, context, dml_ctx); - dml_ctx->v21.mode_programming.dml2_instance->scratch.build_mode_programming_locals.mode_programming_params.programming = dml_ctx->v21.mode_programming.programming; - DC_FP_START(); - is_supported = dml2_check_mode_supported(mode_support); - DC_FP_END(); - if (!is_supported) - return false; - - return true; -} - -bool dml21_validate(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx, - enum dc_validate_mode validate_mode) -{ - bool out = false; - - /* Use dml21_check_mode_support for DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX path */ - if (validate_mode != DC_VALIDATE_MODE_AND_PROGRAMMING) - out = dml21_check_mode_support(in_dc, context, dml_ctx); - else - out = dml21_mode_check_and_programming(in_dc, context, dml_ctx); - - return out; -} - -void dml21_prepare_mcache_programming(struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx) -{ - unsigned int dml_prog_idx, dml_phantom_prog_idx, dc_pipe_index; - int num_pipes; - struct pipe_ctx *dc_main_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__]; - struct pipe_ctx *dc_phantom_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}; - - struct dml2_per_plane_programming *pln_prog = NULL; - struct dml2_plane_mcache_configuration_descriptor *mcache_config = NULL; - struct prepare_mcache_programming_locals *l = &dml_ctx->v21.scratch.prepare_mcache_locals; - - if (context->stream_count == 0) { - return; - } - - memset(&l->build_mcache_programming_params, 0, sizeof(struct dml2_build_mcache_programming_in_out)); - l->build_mcache_programming_params.dml2_instance = dml_ctx->v21.dml_init.dml2_instance; - - /* phantom's start after main planes */ - dml_phantom_prog_idx = dml_ctx->v21.mode_programming.programming->display_config.num_planes; - - /* Build mcache programming parameters per plane per pipe */ - for (dml_prog_idx = 0; dml_prog_idx < dml_ctx->v21.mode_programming.programming->display_config.num_planes; dml_prog_idx++) { - pln_prog = &dml_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; - - mcache_config = &l->build_mcache_programming_params.mcache_configurations[dml_prog_idx]; - memset(mcache_config, 0, sizeof(struct dml2_plane_mcache_configuration_descriptor)); - mcache_config->plane_descriptor = pln_prog->plane_descriptor; - mcache_config->mcache_allocation = &context->bw_ctx.bw.dcn.mcache_allocations[dml_prog_idx]; - mcache_config->num_pipes = pln_prog->num_dpps_required; - l->build_mcache_programming_params.num_configurations++; - - if (pln_prog->num_dpps_required == 0) { - continue; - } - - num_pipes = dml21_find_dc_pipes_for_plane(in_dc, context, dml_ctx, dc_main_pipes, dc_phantom_pipes, dml_prog_idx); - if (num_pipes <= 0 || dc_main_pipes[0]->stream == NULL || - dc_main_pipes[0]->plane_state == NULL) - continue; - - /* get config for each pipe */ - for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { - ASSERT(dc_main_pipes[dc_pipe_index]); - dml21_get_pipe_mcache_config(context, dc_main_pipes[dc_pipe_index], pln_prog, &mcache_config->pipe_configurations[dc_pipe_index]); - } - - /* get config for each phantom pipe */ - if (pln_prog->phantom_plane.valid && - dc_phantom_pipes[0] && - dc_main_pipes[0]->stream && - dc_phantom_pipes[0]->plane_state) { - mcache_config = &l->build_mcache_programming_params.mcache_configurations[dml_phantom_prog_idx]; - memset(mcache_config, 0, sizeof(struct dml2_plane_mcache_configuration_descriptor)); - mcache_config->plane_descriptor = pln_prog->plane_descriptor; - mcache_config->mcache_allocation = &context->bw_ctx.bw.dcn.mcache_allocations[dml_phantom_prog_idx]; - mcache_config->num_pipes = pln_prog->num_dpps_required; - l->build_mcache_programming_params.num_configurations++; - - for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { - ASSERT(dc_phantom_pipes[dc_pipe_index]); - dml21_get_pipe_mcache_config(context, dc_phantom_pipes[dc_pipe_index], pln_prog, &mcache_config->pipe_configurations[dc_pipe_index]); - } - - /* increment phantom index */ - dml_phantom_prog_idx++; - } - } - - /* Call to generate mcache programming per plane per pipe for the given display configuration */ - dml2_build_mcache_programming(&l->build_mcache_programming_params); - - /* get per plane per pipe mcache programming */ - for (dml_prog_idx = 0; dml_prog_idx < dml_ctx->v21.mode_programming.programming->display_config.num_planes; dml_prog_idx++) { - pln_prog = &dml_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; - - num_pipes = dml21_find_dc_pipes_for_plane(in_dc, context, dml_ctx, dc_main_pipes, dc_phantom_pipes, dml_prog_idx); - if (num_pipes <= 0 || dc_main_pipes[0]->stream == NULL || - dc_main_pipes[0]->plane_state == NULL) - continue; - - /* get config for each pipe */ - for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { - ASSERT(dc_main_pipes[dc_pipe_index]); - if (l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_prog_idx][dc_pipe_index]) { - memcpy(&dc_main_pipes[dc_pipe_index]->mcache_regs, - l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_prog_idx][dc_pipe_index], - sizeof(struct dml2_hubp_pipe_mcache_regs)); - } - } - - /* get config for each phantom pipe */ - if (pln_prog->phantom_plane.valid && - dc_phantom_pipes[0] && - dc_main_pipes[0]->stream && - dc_phantom_pipes[0]->plane_state) { - for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { - ASSERT(dc_phantom_pipes[dc_pipe_index]); - if (l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_phantom_prog_idx][dc_pipe_index]) { - memcpy(&dc_phantom_pipes[dc_pipe_index]->mcache_regs, - l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_phantom_prog_idx][dc_pipe_index], - sizeof(struct dml2_hubp_pipe_mcache_regs)); - } - } - /* increment phantom index */ - dml_phantom_prog_idx++; - } - } -} - void dml21_copy(struct dml2_context *dst_dml_ctx, struct dml2_context *src_dml_ctx) { @@ -446,12 +86,8 @@ void dml21_copy(struct dml2_context *dst_dml_ctx, dst_dml_ctx->v21.mode_programming.programming = dst_dml2_programming; - DC_FP_START(); - /* need to initialize copied instance for internal references to be correct */ dml2_initialize_instance(&dst_dml_ctx->v21.dml_init); - - DC_FP_END(); } bool dml21_create_copy(struct dml2_context **dst_dml_ctx, @@ -466,8 +102,3 @@ bool dml21_create_copy(struct dml2_context **dst_dml_ctx, return true; } -void dml21_reinit(const struct dc *in_dc, struct dml2_context *dml_ctx, const struct dml2_configuration_options *config) -{ - dml21_init(in_dc, dml_ctx, config); -} - diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h index b508bbcc0e16..c4813c51251b 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h @@ -34,36 +34,6 @@ void dml21_copy(struct dml2_context *dst_dml_ctx, struct dml2_context *src_dml_ctx); bool dml21_create_copy(struct dml2_context **dst_dml_ctx, struct dml2_context *src_dml_ctx); -void dml21_reinit(const struct dc *in_dc, struct dml2_context *dml_ctx, const struct dml2_configuration_options *config); - -/** - * dml21_validate - Determines if a display configuration is supported or not. - * @in_dc: dc. - * @context: dc_state to be validated. - * @dml_ctx: dml21 context. - * @validate_mode: DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX - * will not populate context.res_ctx. - * - * Based on fast_validate option internally would call: - * - * -dml21_mode_check_and_programming - for DC_VALIDATE_MODE_AND_PROGRAMMING option - * Calculates if dc_state can be supported on the input display - * configuration. If supported, generates the necessary HW - * programming for the new dc_state. - * - * -dml21_check_mode_support - for DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX option - * Calculates if dc_state can be supported for the input display - * config. - * - * Context: Two threads may not invoke this function concurrently unless they reference - * separate dc_states for validation. - * Return: True if mode is supported, false otherwise. - */ -bool dml21_validate(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx, - enum dc_validate_mode validate_mode); - -/* Prepare hubp mcache_regs for hubp mcache ID and split coordinate programming */ -void dml21_prepare_mcache_programming(struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx); /* Structure for inputting external SOCBB and DCNIP values for tool based debugging. */ struct socbb_ip_params_external { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c new file mode 100644 index 000000000000..d5885bbd14c4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#include "dml2_internal_types.h" +#include "dml_top.h" +#include "dml2_core_dcn4_calcs.h" +#include "dml2_internal_shared_types.h" +#include "dml21_utils.h" +#include "dml21_translation_helper.h" +#include "dml2_dc_resource_mgmt.h" +#include "dml2_wrapper.h" +#include "dml2_wrapper_fpu.h" +#include "dml21_wrapper.h" +#include "dml21_wrapper_fpu.h" + +#define INVALID -1 + +static void dml21_populate_configuration_options(const struct dc *in_dc, + struct dml2_context *dml_ctx, + const struct dml2_configuration_options *config) +{ + dml_ctx->config = *config; + + /* UCLK P-State options */ + if (in_dc->debug.dml21_force_pstate_method) { + dml_ctx->config.pmo.force_pstate_method_enable = true; + for (int i = 0; i < MAX_PIPES; i++) + dml_ctx->config.pmo.force_pstate_method_values[i] = in_dc->debug.dml21_force_pstate_method_values[i]; + } else { + dml_ctx->config.pmo.force_pstate_method_enable = false; + } +} + +void dml21_init(const struct dc *in_dc, struct dml2_context *dml_ctx, const struct dml2_configuration_options *config) +{ + dml_ctx->architecture = dml2_architecture_21; + + dml21_populate_configuration_options(in_dc, dml_ctx, config); + + dml21_populate_dml_init_params(&dml_ctx->v21.dml_init, &dml_ctx->config, in_dc); + + dml2_initialize_instance(&dml_ctx->v21.dml_init); +} + +void dml21_reinit(const struct dc *in_dc, struct dml2_context *dml_ctx, const struct dml2_configuration_options *config) +{ + dml21_init(in_dc, dml_ctx, config); +} + +static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_state *context, struct resource_context *out_new_hw_state, + struct dml2_context *in_ctx, unsigned int pipe_cnt) +{ + unsigned int dml_prog_idx = 0, dc_pipe_index = 0, num_dpps_required = 0; + struct dml2_per_plane_programming *pln_prog = NULL; + struct dml2_per_stream_programming *stream_prog = NULL; + struct pipe_ctx *dc_main_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__]; + struct pipe_ctx *dc_phantom_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}; + int num_pipes; + unsigned int dml_phantom_prog_idx; + + context->bw_ctx.bw.dcn.clk.dppclk_khz = 0; + + /* copy global DCHUBBUB arbiter registers */ + memcpy(&context->bw_ctx.bw.dcn.arb_regs, &in_ctx->v21.mode_programming.programming->global_regs.arb_regs, sizeof(struct dml2_display_arb_regs)); + + /* legacy only */ + context->bw_ctx.bw.dcn.compbuf_size_kb = (int)in_ctx->v21.mode_programming.programming->global_regs.arb_regs.compbuf_size * 64; + + context->bw_ctx.bw.dcn.mall_ss_size_bytes = 0; + context->bw_ctx.bw.dcn.mall_ss_psr_active_size_bytes = 0; + context->bw_ctx.bw.dcn.mall_subvp_size_bytes = 0; + + /* phantom's start after main planes */ + dml_phantom_prog_idx = in_ctx->v21.mode_programming.programming->display_config.num_planes; + + for (dml_prog_idx = 0; dml_prog_idx < DML2_MAX_PLANES; dml_prog_idx++) { + pln_prog = &in_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; + + if (!pln_prog->plane_descriptor) + continue; + + stream_prog = &in_ctx->v21.mode_programming.programming->stream_programming[pln_prog->plane_descriptor->stream_index]; + num_dpps_required = pln_prog->num_dpps_required; + + if (num_dpps_required == 0) { + continue; + } + num_pipes = dml21_find_dc_pipes_for_plane(dc, context, in_ctx, dc_main_pipes, dc_phantom_pipes, dml_prog_idx); + + if (num_pipes <= 0) + continue; + + /* program each pipe */ + for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { + dml21_program_dc_pipe(in_ctx, context, dc_main_pipes[dc_pipe_index], pln_prog, stream_prog); + + if (pln_prog->phantom_plane.valid && dc_phantom_pipes[dc_pipe_index]) { + dml21_program_dc_pipe(in_ctx, context, dc_phantom_pipes[dc_pipe_index], pln_prog, stream_prog); + } + } + + /* copy per plane mcache allocation */ + memcpy(&context->bw_ctx.bw.dcn.mcache_allocations[dml_prog_idx], &pln_prog->mcache_allocation, sizeof(struct dml2_mcache_surface_allocation)); + if (pln_prog->phantom_plane.valid) { + memcpy(&context->bw_ctx.bw.dcn.mcache_allocations[dml_phantom_prog_idx], + &pln_prog->phantom_plane.mcache_allocation, + sizeof(struct dml2_mcache_surface_allocation)); + + dml_phantom_prog_idx++; + } + } + + /* assign global clocks */ + context->bw_ctx.bw.dcn.clk.bw_dppclk_khz = context->bw_ctx.bw.dcn.clk.dppclk_khz; + context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz; + if (in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values > 1) { + context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = + in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values] * 1000; + } else { + context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[0] * 1000; + } + + if (in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values > 1) { + context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = + in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values] * 1000; + } else { + context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[0] * 1000; + } + + /* get global mall allocation */ + if (dc->res_pool->funcs->calculate_mall_ways_from_bytes) { + context->bw_ctx.bw.dcn.clk.num_ways = dc->res_pool->funcs->calculate_mall_ways_from_bytes(dc, context->bw_ctx.bw.dcn.mall_subvp_size_bytes); + } else { + context->bw_ctx.bw.dcn.clk.num_ways = 0; + } +} + +static void dml21_prepare_mcache_params(struct dml2_context *dml_ctx, struct dc_state *context, struct dc_mcache_params *mcache_params) +{ + int dc_plane_idx = 0; + int dml_prog_idx, stream_idx, plane_idx; + struct dml2_per_plane_programming *pln_prog = NULL; + + for (stream_idx = 0; stream_idx < context->stream_count; stream_idx++) { + for (plane_idx = 0; plane_idx < context->stream_status[stream_idx].plane_count; plane_idx++) { + dml_prog_idx = map_plane_to_dml21_display_cfg(dml_ctx, context->streams[stream_idx]->stream_id, context->stream_status[stream_idx].plane_states[plane_idx], context); + if (dml_prog_idx == INVALID) { + continue; + } + pln_prog = &dml_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; + mcache_params[dc_plane_idx].valid = pln_prog->mcache_allocation.valid; + mcache_params[dc_plane_idx].num_mcaches_plane0 = pln_prog->mcache_allocation.num_mcaches_plane0; + mcache_params[dc_plane_idx].num_mcaches_plane1 = pln_prog->mcache_allocation.num_mcaches_plane1; + mcache_params[dc_plane_idx].requires_dedicated_mall_mcache = pln_prog->mcache_allocation.requires_dedicated_mall_mcache; + mcache_params[dc_plane_idx].last_slice_sharing.plane0_plane1 = pln_prog->mcache_allocation.last_slice_sharing.plane0_plane1; + memcpy(mcache_params[dc_plane_idx].mcache_x_offsets_plane0, + pln_prog->mcache_allocation.mcache_x_offsets_plane0, + sizeof(int) * (DML2_MAX_MCACHES + 1)); + memcpy(mcache_params[dc_plane_idx].mcache_x_offsets_plane1, + pln_prog->mcache_allocation.mcache_x_offsets_plane1, + sizeof(int) * (DML2_MAX_MCACHES + 1)); + dc_plane_idx++; + } + } +} + +static bool dml21_check_mode_support(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx) +{ + bool is_supported = false; + struct dml2_initialize_instance_in_out *dml_init = &dml_ctx->v21.dml_init; + struct dml2_check_mode_supported_in_out *mode_support = &dml_ctx->v21.mode_support; + + memset(&dml_ctx->v21.display_config, 0, sizeof(struct dml2_display_cfg)); + memset(&dml_ctx->v21.dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping)); + memset(&dml_ctx->v21.mode_programming.dml2_instance->scratch.check_mode_supported_locals.mode_support_params, 0, sizeof(struct dml2_core_mode_support_in_out)); + + if (!context || context->stream_count == 0) + return true; + + /* Scrub phantom's from current dc_state */ + dml_ctx->config.svp_pstate.callbacks.remove_phantom_streams_and_planes(in_dc, context); + dml_ctx->config.svp_pstate.callbacks.release_phantom_streams_and_planes(in_dc, context); + + mode_support->dml2_instance = dml_init->dml2_instance; + dml21_map_dc_state_into_dml_display_cfg(in_dc, context, dml_ctx); + dml_ctx->v21.mode_programming.dml2_instance->scratch.build_mode_programming_locals.mode_programming_params.programming = dml_ctx->v21.mode_programming.programming; + + is_supported = dml2_check_mode_supported(mode_support); + + if (!is_supported) + return false; + + return true; +} + +static bool dml21_mode_check_and_programming(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx) +{ + bool result = false; + struct dml2_build_mode_programming_in_out *mode_programming = &dml_ctx->v21.mode_programming; + struct dc_mcache_params mcache_params[MAX_PLANES] = {0}; + + memset(&dml_ctx->v21.display_config, 0, sizeof(struct dml2_display_cfg)); + memset(&dml_ctx->v21.dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping)); + memset(&dml_ctx->v21.mode_programming.dml2_instance->scratch.build_mode_programming_locals.mode_programming_params, 0, sizeof(struct dml2_core_mode_programming_in_out)); + + if (!context) + return true; + + if (context->stream_count == 0) { + dml21_init_min_clocks_for_dc_state(dml_ctx, context); + dml21_build_fams2_programming(in_dc, context, dml_ctx); + return true; + } + + /* scrub phantom's from current dc_state */ + dml_ctx->config.svp_pstate.callbacks.remove_phantom_streams_and_planes(in_dc, context); + dml_ctx->config.svp_pstate.callbacks.release_phantom_streams_and_planes(in_dc, context); + + /* Populate stream, plane mappings and other fields in display config. */ + result = dml21_map_dc_state_into_dml_display_cfg(in_dc, context, dml_ctx); + if (!result) + return false; + + result = dml2_build_mode_programming(mode_programming); + + if (!result) + return false; + + /* Check and map HW resources */ + if (result && !dml_ctx->config.skip_hw_state_mapping) { + dml21_map_hw_resources(dml_ctx); + dml2_map_dc_pipes(dml_ctx, context, NULL, &dml_ctx->v21.dml_to_dc_pipe_mapping, in_dc->current_state); + /* if subvp phantoms are present, expand them into dc context */ + dml21_handle_phantom_streams_planes(in_dc, context, dml_ctx); + + if (in_dc->res_pool->funcs->program_mcache_pipe_config) { + //Prepare mcache params for each plane based on mcache output from DML + dml21_prepare_mcache_params(dml_ctx, context, mcache_params); + + //populate mcache regs to each pipe + dml_ctx->config.callbacks.allocate_mcache(context, mcache_params); + } + } + + /* Copy DML CLK, WM and REG outputs to bandwidth context */ + if (result && !dml_ctx->config.skip_hw_state_mapping) { + dml21_calculate_rq_and_dlg_params(in_dc, context, &context->res_ctx, dml_ctx, in_dc->res_pool->pipe_count); + dml21_copy_clocks_to_dc_state(dml_ctx, context); + dml21_extract_watermark_sets(in_dc, &context->bw_ctx.bw.dcn.watermarks, dml_ctx); + dml21_build_fams2_programming(in_dc, context, dml_ctx); + } + + return true; +} + +bool dml21_validate(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx, + enum dc_validate_mode validate_mode) +{ + bool out = false; + + /* Use dml21_check_mode_support for DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX path */ + if (validate_mode != DC_VALIDATE_MODE_AND_PROGRAMMING) + out = dml21_check_mode_support(in_dc, context, dml_ctx); + else + out = dml21_mode_check_and_programming(in_dc, context, dml_ctx); + + return out; +} + +void dml21_prepare_mcache_programming(struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx) +{ + unsigned int dml_prog_idx, dml_phantom_prog_idx, dc_pipe_index; + int num_pipes; + struct pipe_ctx *dc_main_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__]; + struct pipe_ctx *dc_phantom_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}; + + struct dml2_per_plane_programming *pln_prog = NULL; + struct dml2_plane_mcache_configuration_descriptor *mcache_config = NULL; + struct prepare_mcache_programming_locals *l = &dml_ctx->v21.scratch.prepare_mcache_locals; + + if (context->stream_count == 0) { + return; + } + + memset(&l->build_mcache_programming_params, 0, sizeof(struct dml2_build_mcache_programming_in_out)); + l->build_mcache_programming_params.dml2_instance = dml_ctx->v21.dml_init.dml2_instance; + + /* phantom's start after main planes */ + dml_phantom_prog_idx = dml_ctx->v21.mode_programming.programming->display_config.num_planes; + + /* Build mcache programming parameters per plane per pipe */ + for (dml_prog_idx = 0; dml_prog_idx < dml_ctx->v21.mode_programming.programming->display_config.num_planes; dml_prog_idx++) { + pln_prog = &dml_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; + + mcache_config = &l->build_mcache_programming_params.mcache_configurations[dml_prog_idx]; + memset(mcache_config, 0, sizeof(struct dml2_plane_mcache_configuration_descriptor)); + mcache_config->plane_descriptor = pln_prog->plane_descriptor; + mcache_config->mcache_allocation = &context->bw_ctx.bw.dcn.mcache_allocations[dml_prog_idx]; + mcache_config->num_pipes = pln_prog->num_dpps_required; + l->build_mcache_programming_params.num_configurations++; + + if (pln_prog->num_dpps_required == 0) { + continue; + } + + num_pipes = dml21_find_dc_pipes_for_plane(in_dc, context, dml_ctx, dc_main_pipes, dc_phantom_pipes, dml_prog_idx); + if (num_pipes <= 0 || dc_main_pipes[0]->stream == NULL || + dc_main_pipes[0]->plane_state == NULL) + continue; + + /* get config for each pipe */ + for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { + ASSERT(dc_main_pipes[dc_pipe_index]); + dml21_get_pipe_mcache_config(context, dc_main_pipes[dc_pipe_index], pln_prog, &mcache_config->pipe_configurations[dc_pipe_index]); + } + + /* get config for each phantom pipe */ + if (pln_prog->phantom_plane.valid && + dc_phantom_pipes[0] && + dc_main_pipes[0]->stream && + dc_phantom_pipes[0]->plane_state) { + mcache_config = &l->build_mcache_programming_params.mcache_configurations[dml_phantom_prog_idx]; + memset(mcache_config, 0, sizeof(struct dml2_plane_mcache_configuration_descriptor)); + mcache_config->plane_descriptor = pln_prog->plane_descriptor; + mcache_config->mcache_allocation = &context->bw_ctx.bw.dcn.mcache_allocations[dml_phantom_prog_idx]; + mcache_config->num_pipes = pln_prog->num_dpps_required; + l->build_mcache_programming_params.num_configurations++; + + for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { + ASSERT(dc_phantom_pipes[dc_pipe_index]); + dml21_get_pipe_mcache_config(context, dc_phantom_pipes[dc_pipe_index], pln_prog, &mcache_config->pipe_configurations[dc_pipe_index]); + } + + /* increment phantom index */ + dml_phantom_prog_idx++; + } + } + + /* Call to generate mcache programming per plane per pipe for the given display configuration */ + dml2_build_mcache_programming(&l->build_mcache_programming_params); + + /* get per plane per pipe mcache programming */ + for (dml_prog_idx = 0; dml_prog_idx < dml_ctx->v21.mode_programming.programming->display_config.num_planes; dml_prog_idx++) { + pln_prog = &dml_ctx->v21.mode_programming.programming->plane_programming[dml_prog_idx]; + + num_pipes = dml21_find_dc_pipes_for_plane(in_dc, context, dml_ctx, dc_main_pipes, dc_phantom_pipes, dml_prog_idx); + if (num_pipes <= 0 || dc_main_pipes[0]->stream == NULL || + dc_main_pipes[0]->plane_state == NULL) + continue; + + /* get config for each pipe */ + for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { + ASSERT(dc_main_pipes[dc_pipe_index]); + if (l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_prog_idx][dc_pipe_index]) { + memcpy(&dc_main_pipes[dc_pipe_index]->mcache_regs, + l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_prog_idx][dc_pipe_index], + sizeof(struct dml2_hubp_pipe_mcache_regs)); + } + } + + /* get config for each phantom pipe */ + if (pln_prog->phantom_plane.valid && + dc_phantom_pipes[0] && + dc_main_pipes[0]->stream && + dc_phantom_pipes[0]->plane_state) { + for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { + ASSERT(dc_phantom_pipes[dc_pipe_index]); + if (l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_phantom_prog_idx][dc_pipe_index]) { + memcpy(&dc_phantom_pipes[dc_pipe_index]->mcache_regs, + l->build_mcache_programming_params.per_plane_pipe_mcache_regs[dml_phantom_prog_idx][dc_pipe_index], + sizeof(struct dml2_hubp_pipe_mcache_regs)); + } + } + /* increment phantom index */ + dml_phantom_prog_idx++; + } + } +} + + diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h new file mode 100644 index 000000000000..2972c6eed21a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef _DML21_WRAPPER_FPU_H_ +#define _DML21_WRAPPER_FPU_H_ + +#include "os_types.h" +#include "dml_top_soc_parameter_types.h" +#include "dml_top_display_cfg_types.h" + +struct dc; +struct dc_state; +struct dml2_configuration_options; +struct dml2_context; +enum dc_validate_mode; + +/** + * dml21_init - Initialize DML21 context + * @in_dc: dc. + * @dml_ctx: DML21 context to initialize. + * @config: dml21 configuration options. + * + * Performs FPU-requiring initialization. Must be called with FPU protection. + */ +void dml21_init(const struct dc *in_dc, struct dml2_context *dml_ctx, const struct dml2_configuration_options *config); + +/** + * dml21_validate - Determines if a display configuration is supported or not. + * @in_dc: dc. + * @context: dc_state to be validated. + * @dml_ctx: dml21 context. + * @validate_mode: DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX + * will not populate context.res_ctx. + * + * Based on fast_validate option internally would call: + * + * -dml21_mode_check_and_programming - for DC_VALIDATE_MODE_AND_PROGRAMMING option + * Calculates if dc_state can be supported on the input display + * configuration. If supported, generates the necessary HW + * programming for the new dc_state. + * + * -dml21_check_mode_support - for DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX option + * Calculates if dc_state can be supported for the input display + * config. + * + * Context: Two threads may not invoke this function concurrently unless they reference + * separate dc_states for validation. + * Return: True if mode is supported, false otherwise. + */ + +void dml21_reinit(const struct dc *in_dc, struct dml2_context *dml_ctx, + const struct dml2_configuration_options *config); +bool dml21_validate(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx, + enum dc_validate_mode validate_mode); + +/* Prepare hubp mcache_regs for hubp mcache ID and split coordinate programming */ +void dml21_prepare_mcache_programming(struct dc *in_dc, struct dc_state *context, struct dml2_context *dml_ctx); + +#endif /* _DML21_WRAPPER_FPU_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c index 408559d6fb2d..f8250c80be02 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c @@ -6,7 +6,20 @@ */ #include "dml2_internal_types.h" +#include "dml2_wrapper.h" #include "dml2_wrapper_fpu.h" +#include "dml21_wrapper.h" +#include "dml21_wrapper_fpu.h" + +#include "dc_fpu.h" + +struct dml2_context *dml2_allocate_memory(void) +{ + struct dml2_context *dml2; + + DC_RUN_WITH_PREEMPTION_ENABLED(dml2 = vzalloc(sizeof(struct dml2_context))); + return dml2; +} bool dml2_validate(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml2, enum dc_validate_mode validate_mode) @@ -23,16 +36,12 @@ bool dml2_validate(const struct dc *in_dc, struct dc_state *context, struct dml2 return out; } - DC_FP_START(); - /* Use dml_validate_only for DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX path */ if (validate_mode != DC_VALIDATE_MODE_AND_PROGRAMMING) out = dml2_validate_only(context, validate_mode); else out = dml2_validate_and_build_resource(in_dc, context, validate_mode); - DC_FP_END(); - return out; } @@ -70,15 +79,11 @@ static void dml2_init(const struct dc *in_dc, const struct dml2_configuration_op break; } - DC_FP_START(); - initialize_dml2_ip_params(*dml2, in_dc, &(*dml2)->v20.dml_core_ctx.ip); initialize_dml2_soc_bbox(*dml2, in_dc, &(*dml2)->v20.dml_core_ctx.soc); initialize_dml2_soc_states(*dml2, in_dc, &(*dml2)->v20.dml_core_ctx.soc, &(*dml2)->v20.dml_core_ctx.states); - - DC_FP_END(); } bool dml2_create(const struct dc *in_dc, const struct dml2_configuration_options *config, struct dml2_context **dml2) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c index 203eef747262..66624cfc27b1 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c @@ -31,8 +31,10 @@ #include "dml2_translation_helper.h" #include "dml2_mall_phantom.h" #include "dml2_dc_resource_mgmt.h" -#include "dml21_wrapper.h" +#include "dml2_wrapper.h" #include "dml2_wrapper_fpu.h" +#include "dml21_wrapper.h" +#include "dml21_wrapper_fpu.h" void initialize_dml2_ip_params(struct dml2_context *dml2, const struct dc *in_dc, struct ip_params_st *out) { @@ -546,11 +548,6 @@ void dml2_apply_debug_options(const struct dc *dc, struct dml2_context *dml2) } } -inline struct dml2_context *dml2_allocate_memory(void) -{ - return (struct dml2_context *) vzalloc(sizeof(struct dml2_context)); -} - void dml2_destroy(struct dml2_context *dml2) { if (!dml2) From 32c1c35b6d8bd8b7ea9ab3d1454b56b605f17dd1 Mon Sep 17 00:00:00 2001 From: Rafal Ostrowski Date: Mon, 23 Feb 2026 06:13:32 +0100 Subject: [PATCH 467/712] drm/amd/display: Move FPU Guards From DML To DC - Part 3 [Why] FPU guards (DC_FP_START/DC_FP_END) are required to wrap around code that can manipulates floats. To do this properly, the FPU guards must be used in a file that is not compiled as a FPU unit. If the guards are used in a file that is a FPU unit, other sections in the file that aren't guarded may be end up being compiled to use FPU operations. [How] Added DC_FP_START and DC_FP_END to DC functions that call DML functions using FPU. Reviewed-by: Dillon Varone Signed-off-by: Rafal Ostrowski Signed-off-by: Alex Hung Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dml2_0/Makefile | 1 + drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c | 1 + .../gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c | 4 +--- .../gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h | 2 +- drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c | 6 +++++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile index 5cb3035b814c..2625943d7f7e 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile @@ -85,6 +85,7 @@ AMD_DAL_DML2 = $(addprefix $(AMDDALPATH)/dc/dml2_0/,$(DML2)) AMD_DISPLAY_FILES += $(AMD_DAL_DML2) + DML21 := src/dml2_top/dml2_top_interfaces.o DML21 += src/dml2_top/dml2_top_soc15.o DML21 += src/dml2_core/dml2_core_dcn4.o diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c index 1a98578f223c..7398f8b69adb 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c @@ -38,6 +38,7 @@ static bool dml21_allocate_memory(struct dml2_context **dml_ctx) (*dml_ctx)->v21.mode_programming.display_config = (*dml_ctx)->v21.mode_support.display_config; DC_RUN_WITH_PREEMPTION_ENABLED((*dml_ctx)->v21.mode_programming.programming = vzalloc(sizeof(struct dml2_display_cfg_programming))); + if (!((*dml_ctx)->v21.mode_programming.programming)) return false; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c index d5885bbd14c4..f3abfdbe6805 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright 2024 Advanced Micro Devices, Inc. +// Copyright 2026 Advanced Micro Devices, Inc. #include "dml2_internal_types.h" #include "dml_top.h" @@ -377,5 +377,3 @@ void dml21_prepare_mcache_programming(struct dc *in_dc, struct dc_state *context } } } - - diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h index 2972c6eed21a..e5d9a456645f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright 2024 Advanced Micro Devices, Inc. +// Copyright 2026 Advanced Micro Devices, Inc. #ifndef _DML21_WRAPPER_FPU_H_ #define _DML21_WRAPPER_FPU_H_ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c index f8250c80be02..93b7613fc4f2 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c @@ -13,6 +13,10 @@ #include "dc_fpu.h" +#if !defined(DC_RUN_WITH_PREEMPTION_ENABLED) +#define DC_RUN_WITH_PREEMPTION_ENABLED(code) code +#endif // !DC_RUN_WITH_PREEMPTION_ENABLED + struct dml2_context *dml2_allocate_memory(void) { struct dml2_context *dml2; @@ -20,7 +24,6 @@ struct dml2_context *dml2_allocate_memory(void) DC_RUN_WITH_PREEMPTION_ENABLED(dml2 = vzalloc(sizeof(struct dml2_context))); return dml2; } - bool dml2_validate(const struct dc *in_dc, struct dc_state *context, struct dml2_context *dml2, enum dc_validate_mode validate_mode) { @@ -84,6 +87,7 @@ static void dml2_init(const struct dc *in_dc, const struct dml2_configuration_op initialize_dml2_soc_bbox(*dml2, in_dc, &(*dml2)->v20.dml_core_ctx.soc); initialize_dml2_soc_states(*dml2, in_dc, &(*dml2)->v20.dml_core_ctx.soc, &(*dml2)->v20.dml_core_ctx.states); + } bool dml2_create(const struct dc *in_dc, const struct dml2_configuration_options *config, struct dml2_context **dml2) From f82480fafedf622541276d48a3b4fed20ce5d866 Mon Sep 17 00:00:00 2001 From: Gaghik Khachatrian Date: Mon, 9 Mar 2026 17:18:25 -0400 Subject: [PATCH 468/712] drm/amd/display: Fixed Silence complier warnings in dc [Why] Resolve compiler warnings by marking unused parameters explicitly. [How] In .c and .h function definitions, keep parameter names in signatures and add a line with `(void)param;` in function body Preserved function signatures and avoids breaking code paths that may reference the parameter under conditional compilation. Reviewed-by: Dillon Varone Reviewed-by: Austin Zheng Signed-off-by: Gaghik Khachatrian Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/basics/fixpt31_32.c | 1 + .../gpu/drm/amd/display/dc/basics/vector.c | 1 + .../gpu/drm/amd/display/dc/bios/bios_parser.c | 1 + .../drm/amd/display/dc/bios/bios_parser2.c | 9 ++ .../drm/amd/display/dc/bios/command_table2.c | 2 + .../bios/dce110/command_table_helper_dce110.c | 1 + .../dce112/command_table_helper2_dce112.c | 1 + .../bios/dce112/command_table_helper_dce112.c | 1 + .../gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c | 1 + .../display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c | 1 + .../display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c | 3 + .../dc/clk_mgr/dcn314/dcn314_clk_mgr.c | 3 + .../dc/clk_mgr/dcn315/dcn315_clk_mgr.c | 3 + .../dc/clk_mgr/dcn316/dcn316_clk_mgr.c | 3 + .../display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c | 2 + .../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 3 + .../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 5 + drivers/gpu/drm/amd/display/dc/core/dc.c | 12 ++ .../drm/amd/display/dc/core/dc_hw_sequencer.c | 1 + .../gpu/drm/amd/display/dc/core/dc_resource.c | 7 ++ .../gpu/drm/amd/display/dc/core/dc_state.c | 2 + .../gpu/drm/amd/display/dc/core/dc_surface.c | 1 + drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 1 + drivers/gpu/drm/amd/display/dc/dc_helper.c | 1 + .../amd/display/dc/dccg/dcn31/dcn31_dccg.c | 2 + .../amd/display/dc/dccg/dcn32/dcn32_dccg.c | 1 + .../amd/display/dc/dccg/dcn35/dcn35_dccg.c | 2 + .../amd/display/dc/dccg/dcn401/dcn401_dccg.c | 4 + .../amd/display/dc/dccg/dcn42/dcn42_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dce/dce_abm.c | 1 + .../gpu/drm/amd/display/dc/dce/dce_audio.c | 3 + .../drm/amd/display/dc/dce/dce_clock_source.c | 4 + drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c | 1 + drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c | 1 + .../gpu/drm/amd/display/dc/dce/dce_i2c_hw.c | 2 + .../gpu/drm/amd/display/dc/dce/dce_i2c_sw.c | 3 + drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c | 1 + .../drm/amd/display/dc/dce/dce_link_encoder.c | 1 + .../drm/amd/display/dc/dce/dce_mem_input.c | 4 + drivers/gpu/drm/amd/display/dc/dce/dce_opp.c | 1 + .../amd/display/dc/dce/dce_stream_encoder.c | 4 + .../drm/amd/display/dc/dce/dce_transform.c | 2 + drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c | 1 + .../gpu/drm/amd/display/dc/dce/dmub_abm_lcd.c | 1 + .../gpu/drm/amd/display/dc/dce/dmub_replay.c | 1 + .../display/dc/dce110/dce110_mem_input_v.c | 10 ++ .../amd/display/dc/dce110/dce110_opp_csc_v.c | 1 + .../display/dc/dce110/dce110_opp_regamma_v.c | 2 + .../dc/dce110/dce110_timing_generator.c | 9 ++ .../dc/dce110/dce110_timing_generator_v.c | 9 ++ .../display/dc/dce110/dce110_transform_v.c | 6 + .../amd/display/dc/dce112/dce112_compressor.c | 1 + .../dc/dce120/dce120_timing_generator.c | 7 ++ .../display/dc/dce80/dce80_timing_generator.c | 6 + .../gpu/drm/amd/display/dc/dcn10/dcn10_dwb.c | 2 + .../gpu/drm/amd/display/dc/dcn31/dcn31_apg.c | 1 + .../dc/dio/dcn10/dcn10_stream_encoder.c | 2 + .../dc/dio/dcn314/dcn314_dio_stream_encoder.c | 2 + .../dc/dio/dcn32/dcn32_dio_stream_encoder.c | 2 + .../dc/dio/dcn35/dcn35_dio_stream_encoder.c | 1 + .../dc/dio/dcn401/dcn401_dio_stream_encoder.c | 3 + .../dc/dio/virtual/virtual_link_encoder.c | 65 ++++++++-- .../dc/dio/virtual/virtual_stream_encoder.c | 111 +++++++++++++----- .../drm/amd/display/dc/dml/calcs/dcn_calcs.c | 1 + .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.c | 1 + .../dc/dml/dcn20/display_mode_vba_20.c | 4 + .../dc/dml/dcn20/display_mode_vba_20v2.c | 5 + .../dc/dml/dcn20/display_rq_dlg_calc_20.c | 5 + .../dc/dml/dcn20/display_rq_dlg_calc_20v2.c | 5 + .../dc/dml/dcn21/display_mode_vba_21.c | 19 +++ .../dc/dml/dcn21/display_rq_dlg_calc_21.c | 5 + .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.c | 2 + .../dc/dml/dcn30/display_mode_vba_30.c | 23 ++++ .../dc/dml/dcn30/display_rq_dlg_calc_30.c | 5 + .../dc/dml/dcn31/display_mode_vba_31.c | 24 ++++ .../dc/dml/dcn31/display_rq_dlg_calc_31.c | 8 ++ .../dc/dml/dcn314/display_mode_vba_314.c | 24 ++++ .../dc/dml/dcn314/display_rq_dlg_calc_314.c | 8 ++ .../drm/amd/display/dc/dml/dcn32/dcn32_fpu.c | 2 + .../dc/dml/dcn32/display_mode_vba_util_32.c | 23 ++++ .../drm/amd/display/dc/dml/dcn35/dcn35_fpu.c | 1 + .../drm/amd/display/dc/dml/display_mode_lib.c | 1 + .../display/dc/dml/display_rq_dlg_helpers.c | 14 +++ .../display/dc/dml/dml1_display_rq_dlg_calc.c | 3 + .../drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c | 1 + .../drm/amd/display/dc/dpp/dcn20/dcn20_dpp.c | 11 +- .../amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c | 1 + .../drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c | 1 + .../amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c | 1 + .../amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c | 5 + .../drm/amd/display/dc/dwb/dcn30/dcn30_dwb.c | 1 + .../dc/gpio/dcn42/hw_translate_dcn42.c | 1 + .../gpu/drm/amd/display/dc/gpio/hw_factory.c | 1 + drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c | 1 + .../drm/amd/display/dc/gpio/hw_translate.c | 1 + .../display/dc/hubbub/dcn31/dcn31_hubbub.c | 1 + .../display/dc/hubbub/dcn35/dcn35_hubbub.c | 1 + .../display/dc/hubbub/dcn401/dcn401_hubbub.c | 4 + .../display/dc/hubbub/dcn42/dcn42_hubbub.c | 2 + .../amd/display/dc/hubp/dcn10/dcn10_hubp.c | 2 + .../amd/display/dc/hubp/dcn20/dcn20_hubp.c | 2 + .../amd/display/dc/hubp/dcn30/dcn30_hubp.c | 2 + .../amd/display/dc/hubp/dcn35/dcn35_hubp.c | 1 + .../amd/display/dc/hubp/dcn401/dcn401_hubp.c | 3 + .../amd/display/dc/hubp/dcn42/dcn42_hubp.c | 1 + .../amd/display/dc/hwss/dce110/dce110_hwseq.c | 22 ++++ .../amd/display/dc/hwss/dce120/dce120_hwseq.c | 4 + .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 21 ++++ .../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 9 ++ .../amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 3 + .../amd/display/dc/hwss/dcn303/dcn303_hwseq.c | 11 ++ .../amd/display/dc/hwss/dcn31/dcn31_hwseq.c | 2 + .../amd/display/dc/hwss/dcn314/dcn314_hwseq.c | 1 + .../amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 2 + .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c | 2 + .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 6 + .../amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 2 + .../dc/irq/dce110/irq_service_dce110.c | 2 + .../display/dc/irq/dcn10/irq_service_dcn10.c | 3 + .../display/dc/irq/dcn20/irq_service_dcn20.c | 3 + .../display/dc/irq/dcn21/irq_service_dcn21.c | 3 + .../display/dc/irq/dcn30/irq_service_dcn30.c | 3 + .../dc/irq/dcn302/irq_service_dcn302.c | 3 + .../dc/irq/dcn303/irq_service_dcn303.c | 3 + .../display/dc/irq/dcn31/irq_service_dcn31.c | 3 + .../dc/irq/dcn314/irq_service_dcn314.c | 3 + .../dc/irq/dcn315/irq_service_dcn315.c | 3 + .../display/dc/irq/dcn32/irq_service_dcn32.c | 3 + .../display/dc/irq/dcn35/irq_service_dcn35.c | 3 + .../dc/irq/dcn351/irq_service_dcn351.c | 3 + .../display/dc/irq/dcn36/irq_service_dcn36.c | 3 + .../dc/irq/dcn401/irq_service_dcn401.c | 3 + .../display/dc/irq/dcn42/irq_service_dcn42.c | 3 + .../display/dc/link/accessories/link_dp_cts.c | 2 + .../amd/display/dc/link/hwss/link_hwss_dpia.c | 4 + .../display/dc/link/hwss/link_hwss_hpo_dp.c | 6 + .../link_hwss_hpo_fixed_vs_pe_retimer_dp.c | 1 + .../display/dc/link/hwss/link_hwss_virtual.c | 6 + .../drm/amd/display/dc/link/link_detection.c | 1 + .../drm/amd/display/dc/link/link_validation.c | 1 + .../dc/link/protocols/link_dp_training.c | 2 + .../protocols/link_dp_training_128b_132b.c | 1 + .../dc/link/protocols/link_dp_training_dpia.c | 6 + .../amd/display/dc/link/protocols/link_dpcd.c | 1 + .../drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c | 4 + .../drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c | 1 + .../drm/amd/display/dc/opp/dcn10/dcn10_opp.c | 1 + .../amd/display/dc/optc/dcn10/dcn10_optc.c | 4 + .../amd/display/dc/optc/dcn20/dcn20_optc.c | 2 + .../amd/display/dc/optc/dcn30/dcn30_optc.c | 1 + .../amd/display/dc/optc/dcn31/dcn31_optc.c | 1 + .../amd/display/dc/optc/dcn314/dcn314_optc.c | 1 + .../amd/display/dc/optc/dcn32/dcn32_optc.c | 1 + .../amd/display/dc/optc/dcn35/dcn35_optc.c | 1 + .../dc/resource/dce100/dce100_resource.c | 5 + .../dc/resource/dce110/dce110_resource.c | 6 + .../dc/resource/dce112/dce112_resource.c | 5 + .../dc/resource/dce120/dce120_resource.c | 1 + .../dc/resource/dce80/dce80_resource.c | 1 + .../dc/resource/dcn10/dcn10_resource.c | 3 + .../dc/resource/dcn20/dcn20_resource.c | 6 + .../dc/resource/dcn21/dcn21_resource.c | 1 + .../dc/resource/dcn30/dcn30_resource.c | 1 + .../dc/resource/dcn301/dcn301_resource.c | 1 + .../dc/resource/dcn302/dcn302_resource.c | 1 + .../dc/resource/dcn303/dcn303_resource.c | 1 + .../dc/resource/dcn31/dcn31_resource.c | 2 + .../dc/resource/dcn314/dcn314_resource.c | 1 + .../dc/resource/dcn315/dcn315_resource.c | 1 + .../dc/resource/dcn316/dcn316_resource.c | 1 + .../resource/dcn32/dcn32_resource_helpers.c | 1 + .../dc/resource/dcn42/dcn42_resource.c | 3 + .../dcn42/dcn42_soc_and_ip_translator.c | 1 + 173 files changed, 750 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c b/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c index 6073cadde76c..fa10f85df3db 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c +++ b/drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c @@ -503,6 +503,7 @@ struct fixed31_32 dc_fixpt_from_int_dy(unsigned int int_value, unsigned int integer_bits, unsigned int fractional_bits) { + (void)integer_bits; struct fixed31_32 fixpt_value = dc_fixpt_from_int(int_value); fixpt_value.value |= (long long)frac_value << (FIXED31_32_BITS_PER_FRACTIONAL_PART - fractional_bits); diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c index a8b750ff8573..e8736c134b8d 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/vector.c +++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c @@ -56,6 +56,7 @@ static bool dal_vector_presized_costruct(struct vector *vector, void *initial_value, uint32_t struct_size) { + (void)ctx; uint32_t i; vector->container = NULL; diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index 578ed0666438..dd362071a6c9 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -2696,6 +2696,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, struct slot_layout_info *slot_layout_info, unsigned int record_offset) { + (void)i; unsigned int j; struct bios_parser *bp; ATOM_BRACKET_LAYOUT_RECORD *record; diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index 94fddf22f5a9..a1c08e1cc411 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -205,6 +205,7 @@ static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, struct graphics_object_id object_id, uint32_t index, struct graphics_object_id *src_object_id) { + (void)index; struct bios_parser *bp = BP_FROM_DCB(dcb); unsigned int i; enum bp_result bp_result = BP_RESULT_BADINPUT; @@ -765,6 +766,7 @@ static enum bp_result bios_parser_get_device_tag( uint32_t device_tag_index, struct connector_device_tag_info *info) { + (void)device_tag_index; struct bios_parser *bp = BP_FROM_DCB(dcb); struct atom_display_object_path_v2 *object; @@ -809,6 +811,7 @@ static enum bp_result get_ss_info_v4_1( uint32_t index, struct spread_spectrum_info *ss_info) { + (void)index; enum bp_result result = BP_RESULT_OK; struct atom_display_controller_info_v4_1 *disp_cntl_tbl = NULL; struct atom_smu_info_v3_3 *smu_info = NULL; @@ -897,6 +900,7 @@ static enum bp_result get_ss_info_v4_2( uint32_t index, struct spread_spectrum_info *ss_info) { + (void)index; enum bp_result result = BP_RESULT_OK; struct atom_display_controller_info_v4_2 *disp_cntl_tbl = NULL; struct atom_smu_info_v3_1 *smu_info = NULL; @@ -977,6 +981,7 @@ static enum bp_result get_ss_info_v4_5( uint32_t index, struct spread_spectrum_info *ss_info) { + (void)index; enum bp_result result = BP_RESULT_OK; struct atom_display_controller_info_v4_5 *disp_cntl_tbl = NULL; @@ -1604,6 +1609,8 @@ static uint32_t bios_parser_get_ss_entry_number( struct dc_bios *dcb, enum as_signal_type signal) { + (void)dcb; + (void)signal; /* TODO: DAL2 atomfirmware implementation does not need this. * why DAL3 need this? */ @@ -3536,6 +3543,8 @@ static uint16_t bios_parser_pack_data_tables( struct dc_bios *dcb, void *dst) { + (void)dcb; + (void)dst; // TODO: There is data bytes alignment issue, disable it for now. return 0; } diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c index f2b1720a6a66..17ef515c6c69 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c @@ -783,6 +783,8 @@ static enum bp_result external_encoder_control_v3( struct bios_parser *bp, struct bp_external_encoder_control *cntl) { + (void)bp; + (void)cntl; /* TODO */ return BP_RESULT_OK; } diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c index 3099128223df..cec61c9d7263 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c +++ b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c @@ -94,6 +94,7 @@ static uint8_t hpd_sel_to_atom(enum hpd_source_id id) static uint8_t dig_encoder_sel_to_atom(enum engine_id id) { + (void)id; /* On any ASIC after DCE80, we manually program the DIG_FE * selection (see connect_dig_be_to_fe function of the link * encoder), so translation should always return 0 (no FE). diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c index 349f0e5d5856..478465fba224 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c @@ -93,6 +93,7 @@ static uint8_t hpd_sel_to_atom(enum hpd_source_id id) static uint8_t dig_encoder_sel_to_atom(enum engine_id id) { + (void)id; /* On any ASIC after DCE80, we manually program the DIG_FE * selection (see connect_dig_be_to_fe function of the link * encoder), so translation should always return 0 (no FE). diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c index 1a5fefcde8af..6b8a87f2c49e 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c @@ -91,6 +91,7 @@ static uint8_t hpd_sel_to_atom(enum hpd_source_id id) static uint8_t dig_encoder_sel_to_atom(enum engine_id id) { + (void)id; /* On any ASIC after DCE80, we manually program the DIG_FE * selection (see connect_dig_be_to_fe function of the link * encoder), so translation should always return 0 (no FE). diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c index 2efa962eecfe..880bce368238 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c @@ -78,6 +78,7 @@ int clk_mgr_helper_get_active_plane_cnt( struct dc *dc, struct dc_state *context) { + (void)dc; int i, total_plane_count; total_plane_count = 0; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c index fe0bb383ddc1..dcec9d0f8c34 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c @@ -521,6 +521,7 @@ void dcn3_clk_mgr_construct( struct pp_smu_funcs *pp_smu, struct dccg *dccg) { + (void)pp_smu; struct clk_state_registers_and_bypass s = { 0 }; clk_mgr->base.ctx = ctx; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c index 44bf48f96183..89fc482947ef 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c @@ -329,6 +329,9 @@ bool dcn31_are_clock_states_equal(struct dc_clocks *a, static void dcn31_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) { + (void)regs_and_bypass; + (void)clk_mgr_base; + (void)log_info; return; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c index c69ec7a0e0ae..b08a70a2f571 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c @@ -395,6 +395,9 @@ bool dcn314_are_clock_states_equal(struct dc_clocks *a, static void dcn314_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) { + (void)regs_and_bypass; + (void)clk_mgr_base; + (void)log_info; return; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c index 8d6949ad700d..3a651c1a866d 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c @@ -247,6 +247,9 @@ static void dcn315_update_clocks(struct clk_mgr *clk_mgr_base, static void dcn315_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) { + (void)regs_and_bypass; + (void)clk_mgr_base; + (void)log_info; return; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c index b858e21ca070..e9d492d8c8d4 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c @@ -255,6 +255,9 @@ static void dcn316_update_clocks(struct clk_mgr *clk_mgr_base, static void dcn316_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) { + (void)regs_and_bypass; + (void)clk_mgr_base; + (void)log_info; return; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index 4007ab353ffd..fda6cade30a8 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -872,6 +872,7 @@ static uint32_t dcn32_get_vco_frequency_from_reg(struct clk_mgr_internal *clk_mg static void dcn32_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) { + (void)log_info; struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); uint32_t dprefclk_did = 0; uint32_t dcfclk_did = 0; @@ -1145,6 +1146,7 @@ void dcn32_clk_mgr_construct( struct pp_smu_funcs *pp_smu, struct dccg *dccg) { + (void)pp_smu; struct clk_log_info log_info = {0}; clk_mgr->base.ctx = ctx; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c index 03464f21d119..82c1a55a2271 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c @@ -333,6 +333,7 @@ bool dcn401_is_dc_mode_present(struct clk_mgr *clk_mgr_base) static void dcn401_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) { + (void)log_info; struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); uint32_t dprefclk_did = 0; uint32_t dcfclk_did = 0; @@ -525,6 +526,7 @@ static void dcn401_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr struct dc_state *context, int ref_dtbclk_khz) { + (void)ref_dtbclk_khz; int i; struct dccg *dccg = clk_mgr->dccg; struct pipe_ctx *otg_master; @@ -614,6 +616,7 @@ static void dcn401_update_clocks_update_dentist( struct clk_mgr_internal *clk_mgr, struct dc_state *context) { + (void)context; uint32_t new_disp_divider = 0; uint32_t new_dispclk_wdivider = 0; uint32_t dentist_dispclk_wdivider_readback = 0; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c index 97f182bfc9ca..ec888aed207d 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c @@ -158,6 +158,9 @@ void dcn42_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, struct dc_state *context, int ref_dtbclk_khz) { + (void)clk_mgr; + (void)context; + (void)ref_dtbclk_khz; /* DCN42 does not implement set_dtbclk_dto function, so this is a no-op */ } @@ -835,6 +838,7 @@ void dcn42_set_low_power_state(struct clk_mgr *clk_mgr_base) void dcn42_exit_low_power_state(struct clk_mgr *clk_mgr_base) { + (void)clk_mgr_base; } @@ -937,6 +941,7 @@ unsigned int dcn42_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type int dcn42_get_dispclk_from_dentist(struct clk_mgr *clk_mgr_base) { + (void)clk_mgr_base; struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); uint32_t dispclk_wdivider; int disp_divider; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 0aec8d01c036..419f894c87b0 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1134,6 +1134,8 @@ static void disable_all_writeback_pipes_for_stream( struct dc_stream_state *stream, struct dc_state *context) { + (void)dc; + (void)context; int i; for (i = 0; i < stream->num_wb_info; i++) @@ -1145,6 +1147,8 @@ static void apply_ctx_interdependent_lock(struct dc *dc, struct dc_stream_state *stream, bool lock) { + (void)dc; + (void)context; int i; /* Checks if interdependent update function pointer is NULL or not, takes care of DCE110 case */ @@ -3007,6 +3011,7 @@ static struct surface_update_descriptor det_surface_update( */ static void force_immediate_gsl_plane_flip(struct dc *dc, struct dc_surface_update *updates, int surface_count) { + (void)dc; bool has_flip_immediate_plane = false; int i; @@ -3285,6 +3290,7 @@ static void copy_stream_update_to_stream(struct dc *dc, struct dc_stream_state *stream, struct dc_stream_update *update) { + (void)context; struct dc_context *dc_ctx = dc->ctx; if (update == NULL || stream == NULL) @@ -3889,6 +3895,7 @@ static void commit_planes_do_stream_update(struct dc *dc, static bool dc_dmub_should_send_dirty_rect_cmd(struct dc *dc, struct dc_stream_state *stream) { + (void)dc; if ((stream->link->psr_settings.psr_version == DC_PSR_VERSION_SU_1 || stream->link->psr_settings.psr_version == DC_PSR_VERSION_1) && stream->ctx->dce_version >= DCN_VERSION_3_1) @@ -4689,6 +4696,7 @@ static bool could_mpcc_tree_change_for_active_pipes(struct dc *dc, int surface_count, bool *is_plane_addition) { + (void)srf_updates; struct dc_stream_status *cur_stream_status = stream_get_status(dc->current_state, stream); bool force_minimal_pipe_splitting = false; @@ -5543,6 +5551,7 @@ void dc_commit_updates_for_stream(struct dc *dc, struct dc_stream_update *stream_update, struct dc_state *state) { + (void)state; bool ret = false; dc_exit_ips_for_hw_access(dc); @@ -5852,6 +5861,7 @@ void dc_lock_memory_clock_frequency(struct dc *dc) static void blank_and_force_memclk(struct dc *dc, bool apply, unsigned int memclk_mhz) { + (void)apply; struct dc_state *context = dc->current_state; struct hubp *hubp; struct pipe_ctx *pipe; @@ -6526,6 +6536,7 @@ void dc_query_current_properties(struct dc *dc, struct dc_current_properties *pr void dc_set_edp_power(const struct dc *dc, struct dc_link *edp_link, bool powerOn) { + (void)dc; if (edp_link->connector_signal != SIGNAL_TYPE_EDP) return; @@ -6652,6 +6663,7 @@ void dc_get_underflow_debug_data_for_otg(struct dc *dc, int primary_otg_inst, void dc_get_power_feature_status(struct dc *dc, int primary_otg_inst, struct power_features *out_data) { + (void)primary_otg_inst; out_data->uclk_p_state = dc->current_state->clk_mgr->clks.p_state_change_support; out_data->fams = dc->current_state->bw_ctx.bw.dcn.clk.fw_based_mclk_switching; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 5b3695e72e19..db86e346307c 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -249,6 +249,7 @@ void color_space_to_black_color( enum dc_color_space colorspace, struct tg_color *black_color) { + (void)dc; switch (colorspace) { case COLOR_SPACE_YCBCR601: case COLOR_SPACE_YCBCR709: diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index e95d5b269738..66597a1f5b78 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -1748,6 +1748,7 @@ enum dc_status resource_build_scaling_params_for_context( const struct dc *dc, struct dc_state *context) { + (void)dc; int i; for (i = 0; i < MAX_PIPES; i++) { @@ -1825,6 +1826,7 @@ int resource_find_free_pipe_used_as_sec_opp_head_by_cur_otg_master( struct resource_context *new_res_ctx, const struct pipe_ctx *cur_otg_master) { + (void)cur_res_ctx; const struct pipe_ctx *cur_sec_opp_head = cur_otg_master->next_odm_pipe; struct pipe_ctx *new_pipe; int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; @@ -1846,6 +1848,7 @@ int resource_find_free_pipe_used_in_cur_mpc_blending_tree( struct resource_context *new_res_ctx, const struct pipe_ctx *cur_opp_head) { + (void)cur_res_ctx; const struct pipe_ctx *cur_sec_dpp = cur_opp_head->bottom_pipe; struct pipe_ctx *new_pipe; int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; @@ -2941,6 +2944,7 @@ enum dc_status resource_add_otg_master_for_stream_output(struct dc_state *new_ct const struct resource_pool *pool, struct dc_stream_state *stream) { + (void)pool; struct dc *dc = stream->ctx->dc; return dc->res_pool->funcs->add_stream_to_ctx(dc, new_ctx, stream); @@ -3023,6 +3027,7 @@ static bool add_plane_to_opp_head_pipes(struct pipe_ctx *otg_master_pipe, struct dc_plane_state *plane_state, struct dc_state *context) { + (void)context; struct pipe_ctx *opp_head_pipe = otg_master_pipe; while (opp_head_pipe) { @@ -3615,6 +3620,7 @@ static struct hpo_dp_stream_encoder *find_first_free_match_hpo_dp_stream_enc_for const struct resource_pool *pool, struct dc_stream_state *stream) { + (void)stream; int i; for (i = 0; i < pool->hpo_dp_stream_enc_count; i++) { @@ -3634,6 +3640,7 @@ static struct audio *find_first_free_audio( enum engine_id id, enum dce_version dc_version) { + (void)dc_version; int i, available_audio_count; if (id == ENGINE_ID_UNKNOWN) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c b/drivers/gpu/drm/amd/display/dc/core/dc_state.c index 13d334c2cb6b..40f7aa732258 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c @@ -409,6 +409,7 @@ enum dc_status dc_state_add_stream( struct dc_state *state, struct dc_stream_state *stream) { + (void)dc; enum dc_status res; DC_LOGGER_INIT(dc->ctx->logger); @@ -784,6 +785,7 @@ struct dc_plane_state *dc_state_create_phantom_plane(const struct dc *dc, struct dc_state *state, struct dc_plane_state *main_plane) { + (void)main_plane; struct dc_plane_state *phantom_plane = dc_create_plane_state(dc); DC_LOGGER_INIT(dc->ctx->logger); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c index 5f12dcca7f71..a59b176d8e55 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c @@ -57,6 +57,7 @@ void dc_plane_construct(struct dc_context *ctx, struct dc_plane_state *plane_sta void dc_plane_destruct(struct dc_plane_state *plane_state) { + (void)plane_state; // no more pointers to free within dc_plane_state } diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index 4256ba3e5719..79c8b4cab053 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -1085,6 +1085,7 @@ static void dc_build_cursor_attribute_update_payload1( struct dmub_cursor_attributes_cfg *pl_A, const uint8_t p_idx, const struct hubp *hubp, const struct dpp *dpp) { + (void)p_idx; /* Hubp */ pl_A->aHubp.SURFACE_ADDR_HIGH = hubp->att.SURFACE_ADDR_HIGH; pl_A->aHubp.SURFACE_ADDR = hubp->att.SURFACE_ADDR; diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c index 04b8b798dfff..77299767096f 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c @@ -108,6 +108,7 @@ static void set_reg_field_values(struct dc_reg_value_masks *field_value_mask, uint8_t shift1, uint32_t mask1, uint32_t field_value1, va_list ap) { + (void)addr; uint32_t shift, mask, field_value; int i = 1; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c index 7f58acfe1177..a72bf413fad6 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c @@ -165,6 +165,7 @@ void dccg31_set_dpstreamclk( int otg_inst, int dp_hpo_inst) { + (void)dp_hpo_inst; if (src == REFCLK) dccg31_disable_dpstreamclk(dccg, otg_inst); else @@ -644,6 +645,7 @@ void dccg31_get_dccg_ref_freq(struct dccg *dccg, unsigned int xtalin_freq_inKhz, unsigned int *dccg_ref_freq_inKhz) { + (void)dccg; /* * Assume refclk is sourced from xtalin * expect 24MHz diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c index e817cd7c2b6a..18b9c5ceed43 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c @@ -265,6 +265,7 @@ static void dccg32_get_dccg_ref_freq(struct dccg *dccg, unsigned int xtalin_freq_inKhz, unsigned int *dccg_ref_freq_inKhz) { + (void)dccg; /* * Assume refclk is sourced from xtalin * expect 100MHz diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c index 0b7908fbb115..efac64165ccd 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c @@ -558,6 +558,7 @@ static void dccg35_set_symclk32_se_src_new( static int dccg35_is_symclk32_se_src_functional_le_new(struct dccg *dccg, int symclk_32_se_inst, int symclk_32_le_inst) { + (void)symclk_32_se_inst; uint32_t en; uint32_t src_sel; @@ -2373,6 +2374,7 @@ static void dccg35_disable_symclk_se_cb( uint32_t stream_enc_inst, uint32_t link_enc_inst) { + (void)link_enc_inst; dccg35_disable_symclk_fe_new(dccg, stream_enc_inst); /* DMU PHY sequence switches SYMCLK_BE (link_enc_inst) to ref clock once PHY is turned off */ diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c index b6d2ead93345..97605a416031 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c @@ -161,6 +161,7 @@ void dccg401_set_pixel_rate_div( enum pixel_rate_div tmds_div, enum pixel_rate_div unused) { + (void)unused; struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); uint32_t cur_tmds_div = PIXEL_RATE_DIV_NA; uint32_t dp_dto_int; @@ -353,6 +354,7 @@ void dccg401_get_dccg_ref_freq(struct dccg *dccg, unsigned int xtalin_freq_inKhz, unsigned int *dccg_ref_freq_inKhz) { + (void)dccg; /* * Assume refclk is sourced from xtalin * expect 100MHz @@ -721,6 +723,7 @@ void dccg401_init(struct dccg *dccg) void dccg401_set_dto_dscclk(struct dccg *dccg, uint32_t inst, uint32_t num_slices_h) { + (void)num_slices_h; struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); switch (inst) { @@ -838,6 +841,7 @@ void dccg401_enable_symclk_se(struct dccg *dccg, uint32_t stream_enc_inst, uint3 void dccg401_disable_symclk_se(struct dccg *dccg, uint32_t stream_enc_inst, uint32_t link_enc_inst) { + (void)link_enc_inst; struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); switch (stream_enc_inst) { diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c index 19dfc3fe5c3a..b813310763e5 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c @@ -186,6 +186,7 @@ void dccg42_set_pixel_rate_div( enum pixel_rate_div tmds_div, enum pixel_rate_div unused) { + (void)unused; struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); uint32_t cur_tmds_div = PIXEL_RATE_DIV_NA; uint32_t dp_dto_int; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c index 41169b42534c..469b4b8f88a3 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c @@ -57,6 +57,7 @@ static bool dce_abm_set_pipe(struct abm *abm, uint32_t controller_id, uint32_t panel_inst) { + (void)panel_inst; struct dce_abm *abm_dce = TO_DCE_ABM(abm); uint32_t rampingBoundary = 0xFFFF; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c index 0807d20985c7..77df61bfaf27 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c @@ -350,6 +350,7 @@ static uint32_t calculate_required_audio_bw_in_symbols( uint32_t av_stream_map_lane_count, uint32_t audio_sdp_overhead) { + (void)channel_count; /* DP spec recommends between 1.05 to 1.1 safety margin to prevent sample under-run */ struct fixed31_32 audio_sdp_margin = dc_fixpt_from_fraction(110, 100); struct fixed31_32 horizontal_line_freq_khz = dc_fixpt_from_fraction( @@ -1027,6 +1028,7 @@ static void get_azalia_clock_info_hdmi( uint32_t actual_pixel_clock_100Hz, struct azalia_clock_info *azalia_clock_info) { + (void)crtc_pixel_clock_100hz; /* audio_dto_phase= 24 * 10,000; * 24MHz in [100Hz] units */ azalia_clock_info->audio_dto_phase = @@ -1043,6 +1045,7 @@ static void get_azalia_clock_info_dp( const struct audio_pll_info *pll_info, struct azalia_clock_info *azalia_clock_info) { + (void)requested_pixel_clock_100Hz; /* Reported dpDtoSourceClockInkhz value for * DCE8 already adjusted for SS, do not need any * adjustment here anymore diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 0791b9144b00..34e54fdb9d13 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -539,6 +539,7 @@ static void dce112_get_pix_clk_dividers_helper ( struct pll_settings *pll_settings, struct pixel_clk_params *pix_clk_params) { + (void)clk_src; uint32_t actual_pixel_clock_100hz; actual_pixel_clock_100hz = pix_clk_params->requested_pix_clk_100hz; @@ -847,6 +848,7 @@ static bool dce110_program_pix_clk( enum dp_link_encoding encoding, struct pll_settings *pll_settings) { + (void)encoding; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); struct bp_pixel_clock_parameters bp_pc_params = {0}; @@ -921,6 +923,7 @@ static bool dce112_program_pix_clk( enum dp_link_encoding encoding, struct pll_settings *pll_settings) { + (void)encoding; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); struct bp_pixel_clock_parameters bp_pc_params = {0}; @@ -1070,6 +1073,7 @@ static bool dcn401_program_pix_clk( enum dp_link_encoding encoding, struct pll_settings *pll_settings) { + (void)encoding; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; const struct pixel_rate_range_table_entry *e = diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c index e871b72e43ef..25ebd8a52ae4 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c @@ -71,6 +71,7 @@ static const uint32_t abm_gain_stepsize = 0x0060; static bool dce_dmcu_init(struct dmcu *dmcu) { + (void)dmcu; // Do nothing return true; } diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c index f5cd2392fc5f..f5261e8d7678 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c @@ -31,6 +31,7 @@ bool dce_i2c_oem_device_present( size_t slave_address ) { + (void)pool; struct dc *dc = ddc->ctx->dc; struct dc_bios *dcb = dc->ctx->dc_bios; struct graphics_object_id id = {0}; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c index 365dd2e37aea..fe239a96121e 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c @@ -69,6 +69,7 @@ static enum i2c_channel_operation_result get_channel_status( struct dce_i2c_hw *dce_i2c_hw, uint8_t *returned_bytes) { + (void)returned_bytes; uint32_t i2c_sw_status = 0; uint32_t value = REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); @@ -631,6 +632,7 @@ bool dce_i2c_submit_command_hw( struct i2c_command *cmd, struct dce_i2c_hw *dce_i2c_hw) { + (void)ddc; uint8_t index_of_payload = 0; bool result; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c index 2d73b94c515c..52e05b9185f1 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c @@ -67,6 +67,7 @@ static void release_engine_dce_sw( struct resource_pool *pool, struct dce_i2c_sw *dce_i2c_sw) { + (void)pool; dal_ddc_close(dce_i2c_sw->ddc); dce_i2c_sw->ddc = NULL; } @@ -76,6 +77,7 @@ static bool wait_for_scl_high_sw( struct ddc *ddc, uint16_t clock_delay_div_4) { + (void)ctx; uint32_t scl_retry = 0; uint32_t scl_retry_max = I2C_SW_TIMEOUT_DELAY / clock_delay_div_4; @@ -469,6 +471,7 @@ bool dce_i2c_submit_command_sw( struct i2c_command *cmd, struct dce_i2c_sw *dce_i2c_sw) { + (void)ddc; uint8_t index_of_payload = 0; bool result; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c index 34bff9aef66c..ee55ec21d270 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c @@ -43,6 +43,7 @@ static void dce_ipp_cursor_set_position( const struct dc_cursor_position *position, const struct dc_cursor_mi_param *param) { + (void)param; struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); /* lock cursor registers */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c index a368802ba51d..5f40ae9e3120 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c @@ -847,6 +847,7 @@ bool dce110_link_encoder_validate_dp_output( const struct dce110_link_encoder *enc110, const struct dc_crtc_timing *crtc_timing) { + (void)enc110; if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) return false; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c index 1c2009e38aa1..168c2d0a5eaa 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c @@ -317,6 +317,7 @@ static void dce_mi_program_display_marks( struct dce_watermarks urgent, uint32_t total_dest_line_time_ns) { + (void)stutter_enter; struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; @@ -370,6 +371,7 @@ static void dce112_mi_program_display_marks(struct mem_input *mi, struct dce_watermarks urgent, uint32_t total_dest_line_time_ns) { + (void)stutter_entry; struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; @@ -656,6 +658,8 @@ static void dce_mi_program_surface_config( struct dc_plane_dcc_param *dcc, bool horizontal_mirror) { + (void)dcc; + (void)horizontal_mirror; struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); REG_UPDATE(GRPH_ENABLE, GRPH_ENABLE, 1); diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c index f342da5a5e50..61d478cfca6d 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c @@ -600,6 +600,7 @@ void dce110_opp_set_dyn_expansion( enum dc_color_depth color_dpth, enum signal_type signal) { + (void)color_sp; struct dce110_opp *opp110 = TO_DCE110_OPP(opp); REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c index 87c19f17c799..ed407e779c12 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c @@ -271,6 +271,8 @@ static void dce110_stream_encoder_dp_set_stream_attribute( bool use_vsc_sdp_for_colorimetry, uint32_t enable_sdp_splitting) { + (void)use_vsc_sdp_for_colorimetry; + (void)enable_sdp_splitting; uint32_t h_active_start; uint32_t v_active_start; uint32_t misc0 = 0; @@ -901,6 +903,7 @@ static void dce110_stream_encoder_dp_blank( struct dc_link *link, struct stream_encoder *enc) { + (void)link; struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); uint32_t reg1 = 0; uint32_t max_retries = DP_BLANK_MAX_RETRY * 10; @@ -951,6 +954,7 @@ static void dce110_stream_encoder_dp_unblank( struct stream_encoder *enc, const struct encoder_unblank_param *param) { + (void)link; struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); if (param->link_settings.link_rate != LINK_RATE_UNKNOWN) { diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index 1ab5ae9b5ea5..c1448ae47366 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -282,6 +282,7 @@ static void calculate_inits( const struct scaler_data *data, struct scl_ratios_inits *inits) { + (void)xfm_dce; struct fixed31_32 h_init; struct fixed31_32 v_init; @@ -1240,6 +1241,7 @@ static void program_color_matrix( const struct out_csc_color_matrix *tbl_entry, enum grph_color_adjust_option options) { + (void)options; { REG_SET_2(OUTPUT_CSC_C11_C12, 0, OUTPUT_CSC_C11, tbl_entry->regval[0], diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c index 967ffdfd6077..93550c5e4d02 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c @@ -173,6 +173,7 @@ static bool dmub_abm_set_backlight_level_pwm_ex(struct abm *abm, unsigned int controller_id, unsigned int panel_inst) { + (void)controller_id; bool ret = false; unsigned int feature_support; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm_lcd.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm_lcd.c index a641ae04450c..806b5709c9e7 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm_lcd.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm_lcd.c @@ -188,6 +188,7 @@ void dmub_abm_init_config(struct abm *abm, bool dmub_abm_set_pause(struct abm *abm, bool pause, unsigned int panel_inst, unsigned int stream_inst) { + (void)stream_inst; union dmub_rb_cmd cmd; struct dc_context *dc = abm->ctx; uint8_t panel_mask = 0x01 << panel_inst; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c index 28a218149b8b..0af1b8e0a49e 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c @@ -216,6 +216,7 @@ static void dmub_replay_set_coasting_vtotal(struct dmub_replay *dmub, uint8_t panel_inst, uint16_t frame_skip_number) { + (void)panel_inst; union dmub_rb_cmd cmd; struct dc_context *dc = dmub->ctx; struct dmub_rb_cmd_replay_set_coasting_vtotal *pCmd = NULL; diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c index 2c43c2422638..b265a72eeb70 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c @@ -40,6 +40,7 @@ static void set_flip_control( struct dce_mem_input *mem_input110, bool immediate) { + (void)immediate; uint32_t value = 0; value = dm_read_reg( @@ -165,6 +166,7 @@ static void program_tiling( const struct dc_tiling_info *info, const enum surface_pixel_format pixel_format) { + (void)pixel_format; uint32_t value = 0; set_reg_field_value(value, info->gfx8.num_banks, @@ -642,6 +644,8 @@ static void dce_mem_input_v_program_surface_config( struct dc_plane_dcc_param *dcc, bool horizotal_mirror) { + (void)dcc; + (void)horizotal_mirror; struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); enable(mem_input110); @@ -927,6 +931,7 @@ static void dce_mem_input_v_program_display_marks( struct dce_watermarks urgent, uint32_t total_dest_line_time_ns) { + (void)stutter_enter; program_urgency_watermark_l( mem_input->ctx, urgent, @@ -970,6 +975,9 @@ static void dce110_allocate_mem_input_v( uint32_t pix_clk_khz,/* for current stream */ uint32_t total_stream_num) { + (void)h_total; + (void)v_total; + (void)total_stream_num; uint32_t addr; uint32_t value; uint32_t pix_dur; @@ -1009,6 +1017,8 @@ static void dce110_free_mem_input_v( struct mem_input *mi, uint32_t total_stream_num) { + (void)mi; + (void)total_stream_num; } static const struct mem_input_funcs dce110_mem_input_v_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c index e096d2b95ef9..cf63fac82832 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -110,6 +110,7 @@ static void program_color_matrix_v( const struct out_csc_color_matrix *tbl_entry, enum grph_color_adjust_option options) { + (void)options; struct dc_context *ctx = xfm_dce->base.ctx; uint32_t cntl_value = dm_read_reg(ctx, mmCOL_MAN_OUTPUT_CSC_CONTROL); bool use_set_a = (get_reg_field_value(cntl_value, diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c index 9b65b77e8823..a4e76db46c9c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c @@ -551,5 +551,7 @@ void dce110_opp_set_regamma_mode_v( struct transform *xfm, enum opp_regamma mode) { + (void)xfm; + (void)mode; // TODO: need to implement the function } diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c index 61b0807693fb..b015b27cd1c6 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c @@ -66,6 +66,7 @@ static void dce110_timing_generator_apply_front_porch_workaround( struct timing_generator *tg, struct dc_crtc_timing *timing) { + (void)tg; if (timing->flags.INTERLACE == 1) { if (timing->v_front_porch < 2) timing->v_front_porch = 2; @@ -1115,6 +1116,7 @@ bool dce110_timing_generator_validate_timing( const struct dc_crtc_timing *timing, enum signal_type signal) { + (void)signal; uint32_t h_blank; uint32_t h_back_porch, hsync_offset, h_sync_start; @@ -1490,6 +1492,7 @@ void dce110_timing_generator_enable_reset_trigger( struct timing_generator *tg, int source_tg_inst) { + (void)source_tg_inst; uint32_t value; uint32_t rising_edge = 0; uint32_t falling_edge = 0; @@ -1959,6 +1962,12 @@ void dce110_tg_program_timing(struct timing_generator *tg, const enum signal_type signal, bool use_vbios) { + (void)vready_offset; + (void)vstartup_start; + (void)vupdate_offset; + (void)vupdate_width; + (void)pstate_keepout; + (void)signal; if (use_vbios) dce110_timing_generator_program_timing_generator(tg, timing); else diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c index 9837dec837ff..ba22c93acd81 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c @@ -442,6 +442,12 @@ static void dce110_timing_generator_v_program_timing(struct timing_generator *tg const enum signal_type signal, bool use_vbios) { + (void)vready_offset; + (void)vstartup_start; + (void)vupdate_offset; + (void)vupdate_width; + (void)pstate_keepout; + (void)signal; if (use_vbios) dce110_timing_generator_program_timing_generator(tg, timing); else @@ -621,6 +627,7 @@ static void dce110_timing_generator_v_setup_global_swap_lock( struct timing_generator *tg, const struct dcp_gsl_params *gsl_params) { + (void)gsl_params; DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); return; } @@ -629,6 +636,7 @@ static void dce110_timing_generator_v_enable_reset_trigger( struct timing_generator *tg, int source_tg_inst) { + (void)source_tg_inst; DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); return; } @@ -650,6 +658,7 @@ static void dce110_timing_generator_v_tear_down_global_swap_lock( static void dce110_timing_generator_v_disable_vga( struct timing_generator *tg) { + (void)tg; return; } diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c index 28d3b2663cd3..6be18665b1f7 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c @@ -371,6 +371,9 @@ static void calculate_inits( struct rect *luma_viewport, struct rect *chroma_viewport) { + (void)xfm_dce; + (void)luma_viewport; + (void)chroma_viewport; inits->h_int_scale_ratio_luma = dc_fixpt_u2d19(data->ratios.horz) << 5; inits->v_int_scale_ratio_luma = @@ -619,6 +622,8 @@ static void dce110_xfmv_set_gamut_remap( struct transform *xfm, const struct xfm_grph_csc_adjustment *adjust) { + (void)xfm; + (void)adjust; /* DO NOTHING*/ } @@ -627,6 +632,7 @@ static void dce110_xfmv_set_pixel_storage_depth( enum lb_pixel_depth depth, const struct bit_depth_reduction_params *bit_depth_params) { + (void)bit_depth_params; struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); int pixel_depth = 0; int expan_mode = 0; diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c index 187f45a7f5e1..fe97d3946cab 100644 --- a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c @@ -284,6 +284,7 @@ static uint32_t align_to_chunks_number_per_line( struct dce112_compressor *cp110, uint32_t pixels) { + (void)cp110; return 256 * ((pixels + 255) / 256); } diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c index 31c4f44ceaac..70410ef0c291 100644 --- a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c @@ -304,6 +304,7 @@ static void dce120_timing_generator_enable_reset_trigger( struct timing_generator *tg, int source) { + (void)source; enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); uint32_t rising_edge = 0; @@ -701,6 +702,12 @@ static void dce120_tg_program_timing(struct timing_generator *tg, const enum signal_type signal, bool use_vbios) { + (void)vready_offset; + (void)vstartup_start; + (void)vupdate_offset; + (void)vupdate_width; + (void)pstate_keepout; + (void)signal; if (use_vbios) dce110_timing_generator_program_timing_generator(tg, timing); else diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c index 88e7a1fc9a30..53c03364f5d4 100644 --- a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c @@ -115,6 +115,12 @@ static void dce80_timing_generator_program_timing(struct timing_generator *tg, const enum signal_type signal, bool use_vbios) { + (void)vready_offset; + (void)vstartup_start; + (void)vupdate_offset; + (void)vupdate_width; + (void)pstate_keepout; + (void)signal; if (!use_vbios) program_pix_dur(tg, timing->pix_clk_100hz); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dwb.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dwb.c index 365a3215f6d5..e9efbb49586e 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dwb.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dwb.c @@ -44,6 +44,7 @@ static bool dwb1_get_caps(struct dwbc *dwbc, struct dwb_caps *caps) { + (void)dwbc; if (caps) { caps->adapter_id = 0; /* we only support 1 adapter currently */ caps->hw_version = DCN_VERSION_1_0; @@ -63,6 +64,7 @@ static bool dwb1_get_caps(struct dwbc *dwbc, struct dwb_caps *caps) static bool dwb1_enable(struct dwbc *dwbc, struct dc_dwb_params *params) { + (void)params; struct dcn10_dwbc *dwbc10 = TO_DCN10_DWBC(dwbc); /* disable first. */ diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_apg.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_apg.c index 05aac3e444b4..4c7e4fe3c680 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_apg.c +++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_apg.c @@ -77,6 +77,7 @@ static void apg31_se_audio_setup( unsigned int az_inst, struct audio_info *audio_info) { + (void)az_inst; struct dcn31_apg *apg31 = DCN31_APG_FROM_APG(apg); ASSERT(audio_info); diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_stream_encoder.c index d928b4dcf6b8..d913f065ecca 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_stream_encoder.c @@ -249,6 +249,7 @@ void enc1_stream_encoder_dp_set_stream_attribute( bool use_vsc_sdp_for_colorimetry, uint32_t enable_sdp_splitting) { + (void)enable_sdp_splitting; uint32_t h_active_start; uint32_t v_active_start; uint32_t misc0 = 0; @@ -783,6 +784,7 @@ void enc1_stream_encoder_send_immediate_sdp_message( const uint8_t *custom_sdp_message, unsigned int sdp_message_size) { + (void)sdp_message_size; struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); uint32_t value = 0; diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn314/dcn314_dio_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn314/dcn314_dio_stream_encoder.c index 3e85e9c3d2cb..d1fd5462dca5 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn314/dcn314_dio_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn314/dcn314_dio_stream_encoder.c @@ -394,6 +394,8 @@ void enc314_dp_set_dsc_config(struct stream_encoder *enc, uint32_t dsc_bytes_per_pixel, uint32_t dsc_slice_width) { + (void)dsc_bytes_per_pixel; + (void)dsc_slice_width; struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); REG_UPDATE(DP_DSC_CNTL, DP_DSC_MODE, dsc_mode == OPTC_DSC_DISABLED ? 0 : 1); diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_stream_encoder.c index 3523d1cdc1a3..edafa3808455 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_stream_encoder.c @@ -356,6 +356,8 @@ static void enc32_dp_set_dsc_config(struct stream_encoder *enc, uint32_t dsc_bytes_per_pixel, uint32_t dsc_slice_width) { + (void)dsc_bytes_per_pixel; + (void)dsc_slice_width; struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); REG_UPDATE(DP_DSC_CNTL, DP_DSC_MODE, dsc_mode == OPTC_DSC_DISABLED ? 0 : 1); diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c index fd5d1dbf9dc6..de24dcd27e6c 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c @@ -369,6 +369,7 @@ static void enc35_stream_encoder_map_to_link( uint32_t stream_enc_inst, uint32_t link_enc_inst) { + (void)stream_enc_inst; struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); ASSERT(stream_enc_inst < 5 && link_enc_inst < 5); diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_stream_encoder.c index 99aab70ef3e1..2d33ed0c062d 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_stream_encoder.c @@ -57,6 +57,8 @@ static void enc401_dp_set_odm_combine( struct stream_encoder *enc, bool odm_combine) { + (void)enc; + (void)odm_combine; } /* setup stream encoder in dvi mode */ @@ -710,6 +712,7 @@ void enc401_stream_encoder_map_to_link( uint32_t stream_enc_inst, uint32_t link_enc_inst) { + (void)stream_enc_inst; struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc); REG_UPDATE(STREAM_MAPPER_CONTROL, diff --git a/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_link_encoder.c index 2655bc194a35..5b2bba0eff0e 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_link_encoder.c @@ -30,52 +30,92 @@ static bool virtual_link_encoder_validate_output_with_stream( struct link_encoder *enc, - const struct dc_stream_state *stream) { return true; } + const struct dc_stream_state *stream) { + (void)enc; + (void)stream; + return true; + } -static void virtual_link_encoder_hw_init(struct link_encoder *enc) {} +static void virtual_link_encoder_hw_init(struct link_encoder *enc) +{ + (void)enc; +} static void virtual_link_encoder_setup( - struct link_encoder *enc, - enum signal_type signal) {} + struct link_encoder *enc, enum signal_type signal) { + (void)enc; + (void)signal; + } static void virtual_link_encoder_enable_tmds_output( struct link_encoder *enc, enum clock_source_id clock_source, enum dc_color_depth color_depth, enum signal_type signal, - uint32_t pixel_clock) {} + uint32_t pixel_clock) { + (void)enc; + (void)clock_source; + (void)color_depth; + (void)signal; + (void)pixel_clock; + } static void virtual_link_encoder_enable_dp_output( struct link_encoder *enc, const struct dc_link_settings *link_settings, - enum clock_source_id clock_source) {} + enum clock_source_id clock_source) { + (void)enc; + (void)link_settings; + (void)clock_source; + } static void virtual_link_encoder_enable_dp_mst_output( struct link_encoder *enc, const struct dc_link_settings *link_settings, - enum clock_source_id clock_source) {} + enum clock_source_id clock_source) { + (void)enc; + (void)link_settings; + (void)clock_source; + } static void virtual_link_encoder_disable_output( struct link_encoder *link_enc, - enum signal_type signal) {} + enum signal_type signal) { + (void)link_enc; + (void)signal; + } static void virtual_link_encoder_dp_set_lane_settings( struct link_encoder *enc, const struct dc_link_settings *link_settings, - const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) {} + const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) { + (void)enc; + (void)link_settings; + (void)lane_settings; + } static void virtual_link_encoder_dp_set_phy_pattern( struct link_encoder *enc, - const struct encoder_set_dp_phy_pattern_param *param) {} + const struct encoder_set_dp_phy_pattern_param *param) { + (void)enc; + (void)param; + } static void virtual_link_encoder_update_mst_stream_allocation_table( struct link_encoder *enc, - const struct link_mst_stream_allocation_table *table) {} + const struct link_mst_stream_allocation_table *table) { + (void)enc; + (void)table; + } static void virtual_link_encoder_connect_dig_be_to_fe( struct link_encoder *enc, enum engine_id engine, - bool connect) {} + bool connect) { + (void)enc; + (void)engine; + (void)connect; + } static void virtual_link_encoder_destroy(struct link_encoder **enc) { @@ -86,6 +126,7 @@ static void virtual_link_encoder_destroy(struct link_encoder **enc) static void virtual_link_encoder_get_max_link_cap(struct link_encoder *enc, struct dc_link_settings *link_settings) { + (void)enc; /* Set Default link settings */ struct dc_link_settings max_link_cap = {LANE_COUNT_FOUR, LINK_RATE_HIGH, LINK_SPREAD_05_DOWNSPREAD_30KHZ, false, 0}; diff --git a/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_stream_encoder.c index a9c8857476ac..27448f2b2467 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/virtual/virtual_stream_encoder.c @@ -31,80 +31,127 @@ static void virtual_stream_encoder_dp_set_stream_attribute( struct dc_crtc_timing *crtc_timing, enum dc_color_space output_color_space, bool use_vsc_sdp_for_colorimetry, - uint32_t enable_sdp_splitting) {} + uint32_t enable_sdp_splitting) { + (void)enc; + (void)crtc_timing; + (void)output_color_space; + (void)use_vsc_sdp_for_colorimetry; + (void)enable_sdp_splitting; + } static void virtual_stream_encoder_hdmi_set_stream_attribute( struct stream_encoder *enc, struct dc_crtc_timing *crtc_timing, int actual_pix_clk_khz, - bool enable_audio) {} + bool enable_audio) { + (void)enc; + (void)crtc_timing; + (void)actual_pix_clk_khz; + (void)enable_audio; + } static void virtual_stream_encoder_dvi_set_stream_attribute( struct stream_encoder *enc, struct dc_crtc_timing *crtc_timing, - bool is_dual_link) {} + bool is_dual_link) { + (void)enc; + (void)crtc_timing; + (void)is_dual_link; + } static void virtual_stream_encoder_set_throttled_vcp_size( struct stream_encoder *enc, - struct fixed31_32 avg_time_slots_per_mtp) -{} + struct fixed31_32 avg_time_slots_per_mtp) { + (void)enc; + (void)avg_time_slots_per_mtp; + } static void virtual_stream_encoder_update_hdmi_info_packets( struct stream_encoder *enc, - const struct encoder_info_frame *info_frame) {} + const struct encoder_info_frame *info_frame) { + (void)enc; + (void)info_frame; + } static void virtual_stream_encoder_stop_hdmi_info_packets( - struct stream_encoder *enc) {} + struct stream_encoder *enc) { + (void)enc; + } static void virtual_stream_encoder_set_avmute( - struct stream_encoder *enc, - bool enable) {} + struct stream_encoder *enc, bool enable) { + (void)enc; + (void)enable; + } static void virtual_stream_encoder_update_dp_info_packets( struct stream_encoder *enc, - const struct encoder_info_frame *info_frame) {} + const struct encoder_info_frame *info_frame) { + (void)enc; + (void)info_frame; + } static void virtual_stream_encoder_stop_dp_info_packets( - struct stream_encoder *enc) {} + struct stream_encoder *enc) { + (void)enc; + } static void virtual_stream_encoder_dp_blank( struct dc_link *link, - struct stream_encoder *enc) {} + struct stream_encoder *enc) { + (void)link; + (void)enc; + } static void virtual_stream_encoder_dp_unblank( struct dc_link *link, struct stream_encoder *enc, - const struct encoder_unblank_param *param) {} + const struct encoder_unblank_param *param) { + (void)enc; + (void)link; + (void)param; + } static void virtual_audio_mute_control( - struct stream_encoder *enc, - bool mute) {} + struct stream_encoder *enc, bool mute) { + (void)enc; + (void)mute; + } static void virtual_stream_encoder_reset_hdmi_stream_attribute( - struct stream_encoder *enc) -{} + struct stream_encoder *enc) +{ + (void)enc; +} static void virtual_enc_dp_set_odm_combine( - struct stream_encoder *enc, - bool odm_combine) -{} + struct stream_encoder *enc, bool odm_combine) { + (void)enc; + (void)odm_combine; + } static void virtual_dig_connect_to_otg( - struct stream_encoder *enc, - int tg_inst) -{} + struct stream_encoder *enc, int tg_inst) { + (void)enc; + (void)tg_inst; + } static void virtual_setup_stereo_sync( - struct stream_encoder *enc, - int tg_inst, - bool enable) -{} + struct stream_encoder *enc, + int tg_inst, bool enable) { + (void)enc; + (void)tg_inst; + (void)enable; + } static void virtual_stream_encoder_set_dsc_pps_info_packet( - struct stream_encoder *enc, - bool enable, - uint8_t *dsc_packed_pps, - bool immediate_update) -{} + struct stream_encoder *enc, bool enable, uint8_t *dsc_packed_pps, + bool immediate_update) +{ + (void)enc; + (void)enable; + (void)dsc_packed_pps; + (void)immediate_update; +} static const struct stream_encoder_funcs virtual_str_enc_funcs = { .dp_set_odm_combine = diff --git a/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c index 74962791302f..61553e24d53e 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c @@ -525,6 +525,7 @@ static void split_stream_across_pipes( struct pipe_ctx *primary_pipe, struct pipe_ctx *secondary_pipe) { + (void)res_ctx; int pipe_idx = secondary_pipe->pipe_idx; if (!primary_pipe->plane_state) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c index ae34982b1b1c..887744d56d6a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c @@ -1316,6 +1316,7 @@ int dcn20_populate_dml_pipes_from_context(struct dc *dc, display_e2e_pipe_params_st *pipes, enum dc_validate_mode validate_mode) { + (void)validate_mode; int pipe_cnt, i; bool synchronized_vblank = true; struct resource_context *res_ctx = &context->res_ctx; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c index 0c8c4a080c50..f5f636afe33c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c @@ -501,6 +501,8 @@ static bool CalculatePrefetchSchedule( double *VUpdateWidthPix, double *VReadyOffsetPix) { + (void)mode_lib; + bool MyError = false; unsigned int DPPCycles, DISPCLKCycles; double DSTTotalPixelsAfterScaler, TotalRepeaterDelayTime; @@ -878,6 +880,7 @@ static unsigned int CalculateVMAndRowBytes( unsigned int *dpte_row_height, unsigned int *meta_row_height) { + (void)ViewportWidth; unsigned int MetaRequestHeight; unsigned int MetaRequestWidth; unsigned int MetaSurfWidth; @@ -2953,6 +2956,7 @@ static double CalculateRemoteSurfaceFlipDelay( double *TInitXFill, double *TslvChk) { + (void)mode_lib; double TSlvSetup, AvgfillRate, result; *SrcActiveDrainRate = VRatio * SwathWidth * Bpp / LineTime; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20v2.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20v2.c index c935903b68e1..95b0a3501880 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20v2.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20v2.c @@ -495,6 +495,7 @@ static bool CalculateDelayAfterScaler( double *DSTYAfterScaler ) { + (void)ReturnBW; unsigned int DPPCycles, DISPCLKCycles; double DataFabricLineDeliveryTimeLuma; double DataFabricLineDeliveryTimeChroma; @@ -592,6 +593,8 @@ static bool CalculatePrefetchSchedule( double *VUpdateWidthPix, double *VReadyOffsetPix) { + (void)mode_lib; + bool MyError = false; double TotalRepeaterDelayTime; double Tdm, LineTime, Tsetup; @@ -938,6 +941,7 @@ static unsigned int CalculateVMAndRowBytes( unsigned int *dpte_row_height, unsigned int *meta_row_height) { + (void)ViewportWidth; unsigned int MetaRequestHeight; unsigned int MetaRequestWidth; unsigned int MetaSurfWidth; @@ -3026,6 +3030,7 @@ static double CalculateRemoteSurfaceFlipDelay( double *TInitXFill, double *TslvChk) { + (void)mode_lib; double TSlvSetup, AvgfillRate, result; *SrcActiveDrainRate = VRatio * SwathWidth * Bpp / LineTime; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c index 9c58ff1069d6..591d9618bdc0 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c @@ -126,6 +126,7 @@ static double get_refcyc_per_delivery(struct display_mode_lib *mode_lib, unsigned int delivery_width, unsigned int req_per_swath_ub) { + (void)mode_lib; double refcyc_per_delivery = 0.0; if (vratio <= 1.0) { @@ -1538,6 +1539,9 @@ void dml20_rq_dlg_get_dlg_reg(struct display_mode_lib *mode_lib, const bool ignore_viewport_pos, const bool immediate_flip_support) { + (void)vm_en; + (void)ignore_viewport_pos; + (void)immediate_flip_support; display_rq_params_st rq_param = {0}; display_dlg_sys_params_st dlg_sys_param = {0}; @@ -1588,6 +1592,7 @@ static void calculate_ttu_cursor(struct display_mode_lib *mode_lib, unsigned int cur_width, enum cursor_bpp cur_bpp) { + (void)mode_lib; unsigned int cur_src_width = cur_width; unsigned int cur_req_size = 0; unsigned int cur_req_width = 0; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c index 570e6e39eb45..d4dddc9d535a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c @@ -126,6 +126,7 @@ static double get_refcyc_per_delivery(struct display_mode_lib *mode_lib, unsigned int delivery_width, unsigned int req_per_swath_ub) { + (void)mode_lib; double refcyc_per_delivery = 0.0; if (vratio <= 1.0) { @@ -1539,6 +1540,9 @@ void dml20v2_rq_dlg_get_dlg_reg(struct display_mode_lib *mode_lib, const bool ignore_viewport_pos, const bool immediate_flip_support) { + (void)vm_en; + (void)ignore_viewport_pos; + (void)immediate_flip_support; display_rq_params_st rq_param = {0}; display_dlg_sys_params_st dlg_sys_param = {0}; @@ -1589,6 +1593,7 @@ static void calculate_ttu_cursor(struct display_mode_lib *mode_lib, unsigned int cur_width, enum cursor_bpp cur_bpp) { + (void)mode_lib; unsigned int cur_src_width = cur_width; unsigned int cur_req_size = 0; unsigned int cur_req_width = 0; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c index 48905ca39b70..11570a0c9427 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c @@ -695,6 +695,9 @@ static bool CalculatePrefetchSchedule( double *VUpdateWidthPix, double *VReadyOffsetPix) { + (void)mode_lib; + (void)XFCEnabled; + bool MyError = false; unsigned int DPPCycles, DISPCLKCycles; double DSTTotalPixelsAfterScaler, TotalRepeaterDelayTime; @@ -1290,6 +1293,8 @@ static unsigned int CalculateVMAndRowBytes( unsigned int *DPDE0BytesFrame, unsigned int *MetaPTEBytesFrame) { + (void)SourcePixelFormat; + (void)ViewportWidth; unsigned int MPDEBytesFrame; unsigned int DCCMetaSurfaceBytes; unsigned int MacroTileSizeBytes; @@ -3040,6 +3045,7 @@ static double CalculateRemoteSurfaceFlipDelay( double *TInitXFill, double *TslvChk) { + (void)mode_lib; double TSlvSetup, AvgfillRate, result; *SrcActiveDrainRate = VRatio * SwathWidth * Bpp / LineTime; @@ -3187,6 +3193,7 @@ static void CalculateFlipSchedule( double *final_flip_bw, bool *ImmediateFlipSupportedForPipe) { + (void)mode_lib; double min_row_time = 0.0; unsigned int HostVMDynamicLevels; double TimeForFetchingMetaPTEImmediateFlip; @@ -5294,6 +5301,15 @@ static void CalculateWatermarksAndDRAMSpeedChangeSupport( double *StutterEnterPlusExitWatermark, double *MinActiveDRAMClockChangeLatencySupported) { + (void)DPPCLK; + (void)SwathWidthSingleDPPY; + (void)DCFCLK; + (void)UrgentOutOfOrderReturn; + (void)ReturnBW; + (void)GPUVMEnable; + (void)dpte_group_bytes; + (void)MetaChunkSize; + double EffectiveLBLatencyHidingY; double EffectiveLBLatencyHidingC; double DPPOutputBufferLinesY; @@ -5885,6 +5901,9 @@ static void CalculateMetaAndPTETimes( double TimePerVMRequestVBlank[], double TimePerVMRequestFlip[]) { + (void)VRatioPrefetchY; + (void)VRatioPrefetchC; + unsigned int meta_chunk_width; unsigned int min_meta_chunk_width; unsigned int meta_chunk_per_row_int; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c index f549da082c01..8a611b3bec33 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c @@ -102,6 +102,7 @@ static double get_refcyc_per_delivery( unsigned int delivery_width, unsigned int req_per_swath_ub) { + (void)mode_lib; double refcyc_per_delivery = 0.0; if (vratio <= 1.0) { @@ -1647,6 +1648,9 @@ void dml21_rq_dlg_get_dlg_reg( const bool ignore_viewport_pos, const bool immediate_flip_support) { + (void)vm_en; + (void)ignore_viewport_pos; + (void)immediate_flip_support; display_rq_params_st rq_param = {0}; display_dlg_sys_params_st dlg_sys_param = {0}; @@ -1702,6 +1706,7 @@ static void calculate_ttu_cursor( unsigned int cur_width, enum cursor_bpp cur_bpp) { + (void)mode_lib; unsigned int cur_src_width = cur_width; unsigned int cur_req_size = 0; unsigned int cur_req_width = 0; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c index e5f5c0663750..0cdd60869ce1 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c @@ -571,6 +571,7 @@ void dcn30_fpu_update_bw_bounding_box(struct dc *dc, unsigned int *dcfclk_mhz, unsigned int *dram_speed_mts) { + (void)bw_params; unsigned int i; dc_assert_fp_enabled(); @@ -720,6 +721,7 @@ void dcn3_fpu_build_wm_range_table(struct clk_mgr *base) void patch_dcn30_soc_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_st *dcn3_0_ip) { + (void)dcn3_0_ip; dc_assert_fp_enabled(); if (dc->ctx->dc_bios->funcs->get_soc_bb_info) { diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c index 1df3412be346..634982173190 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c @@ -778,6 +778,8 @@ static bool CalculatePrefetchSchedule( double *RequiredPrefetchPixDataBWChroma, bool *NotEnoughTimeForDynamicMetadata) { + (void)SwathWidthY; + (void)SwathWidthC; struct vba_vars_st *v = &mode_lib->vba; double DPPCLKDelaySubtotalPlusCNVCFormater = v->DPPCLKDelaySubtotal + v->DPPCLKDelayCNVCFormater; bool MyError = false; @@ -1233,6 +1235,10 @@ static void CalculateDCCConfiguration( unsigned int *IndependentBlockLuma, unsigned int *IndependentBlockChroma) { + (void)SurfaceWidthChroma; + (void)SurfaceHeightChroma; + (void)BytePerPixelDETY; + (void)BytePerPixelDETC; int yuv420 = 0; int horz_div_l = 0; int horz_div_c = 0; @@ -1595,6 +1601,7 @@ static unsigned int CalculateVMAndRowBytes( unsigned int *DPDE0BytesFrame, unsigned int *MetaPTEBytesFrame) { + (void)SourcePixelFormat; unsigned int MPDEBytesFrame = 0; unsigned int DCCMetaSurfaceBytes = 0; unsigned int MacroTileSizeBytes = 0; @@ -3068,6 +3075,8 @@ double dml30_CalculateWriteBackDISPCLK( unsigned int HTotal, unsigned int WritebackLineBufferSize) { + (void)WritebackPixelFormat; + (void)WritebackVRatio; double DISPCLK_H = 0, DISPCLK_V = 0, DISPCLK_HB = 0; DISPCLK_H = PixelClock * dml_ceil(WritebackHTaps / 8.0, 1) / WritebackHRatio; @@ -3086,6 +3095,8 @@ static double CalculateWriteBackDelay( long WritebackSourceHeight, unsigned int HTotal) { + (void)WritebackPixelFormat; + (void)WritebackHRatio; double CalculateWriteBackDelay = 0; double Line_length = 0; double Output_lines_last_notclamped = 0; @@ -3199,6 +3210,8 @@ static void CalculateFlipSchedule( double *final_flip_bw, bool *ImmediateFlipSupportedForPipe) { + (void)mode_lib; + (void)HostVMMinPageSize; double min_row_time = 0.0; unsigned int HostVMDynamicLevelsTrips = 0; double TimeForFetchingMetaPTEImmediateFlip = 0; @@ -4968,6 +4981,10 @@ static void CalculateWatermarksAndDRAMSpeedChangeSupport( double BytePerPixelDETC[], enum clock_change_support *DRAMClockChangeSupport) { + (void)DCFCLK; + (void)ReturnBW; + (void)DPPCLK; + (void)DETBufferSizeC; struct vba_vars_st *v = &mode_lib->vba; double EffectiveLBLatencyHidingY = 0; double EffectiveLBLatencyHidingC = 0; @@ -5212,6 +5229,8 @@ static void CalculateUrgentBurstFactor( double *UrgentBurstFactorChroma, bool *NotEnoughUrgentLatencyHiding) { + (void)DETBufferSizeInKByte; + (void)VRatioC; double LinesInDETLuma = 0; double LinesInDETChroma = 0; unsigned int LinesInCursorBuffer = 0; @@ -5575,6 +5594,8 @@ static void CalculateVMGroupAndRequestTimes( double TimePerVMRequestVBlank[], double TimePerVMRequestFlip[]) { + (void)dpte_row_width_luma_ub; + (void)dpte_row_width_chroma_ub; int num_group_per_lower_vm_stage = 0; int num_req_per_lower_vm_stage = 0; unsigned int k; @@ -5857,6 +5878,7 @@ static void CalculateSwathAndDETConfiguration( bool ViewportSizeSupportPerPlane[], bool *ViewportSizeSupport) { + (void)HRatioChroma; int MaximumSwathHeightY[DC__NUM_DPP__MAX] = { 0 }; int MaximumSwathHeightC[DC__NUM_DPP__MAX] = { 0 }; int MinimumSwathHeightY = 0; @@ -6039,6 +6061,7 @@ static void CalculateSwathWidth( unsigned int swath_width_luma_ub[], unsigned int swath_width_chroma_ub[]) { + (void)BytePerPixY; unsigned int k, j; long surface_width_ub_l; long surface_height_ub_l; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c index 4fb37df54d59..472ac5ee165f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c @@ -50,6 +50,7 @@ static double get_refcyc_per_delivery(struct display_mode_lib *mode_lib, unsigned int delivery_width, unsigned int req_per_swath_ub) { + (void)mode_lib; double refcyc_per_delivery = 0.0; if (vratio <= 1.0) { @@ -804,6 +805,7 @@ static void calculate_ttu_cursor(struct display_mode_lib *mode_lib, unsigned int cur_width, enum cursor_bpp cur_bpp) { + (void)mode_lib; unsigned int cur_src_width = cur_width; unsigned int cur_req_size = 0; unsigned int cur_req_width = 0; @@ -896,6 +898,9 @@ static void dml_rq_dlg_get_dlg_params(struct display_mode_lib *mode_lib, const bool ignore_viewport_pos, const bool immediate_flip_support) { + (void)vm_en; + (void)ignore_viewport_pos; + (void)immediate_flip_support; const display_pipe_source_params_st *src = &e2e_pipe_param[pipe_idx].pipe.src; const display_pipe_dest_params_st *dst = &e2e_pipe_param[pipe_idx].pipe.dest; const display_output_params_st *dout = &e2e_pipe_param[pipe_idx].dout; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c index ed59c77bc6f6..9833467722b9 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c @@ -873,6 +873,11 @@ static bool CalculatePrefetchSchedule( double *VUpdateWidthPix, double *VReadyOffsetPix) { + (void)mode_lib; + (void)HostVMMinPageSize; + (void)SwathWidthY; + (void)SwathWidthC; + bool MyError = false; unsigned int DPPCycles, DISPCLKCycles; double DSTTotalPixelsAfterScaler; @@ -1491,6 +1496,10 @@ static void CalculateDCCConfiguration( unsigned int *IndependentBlockLuma, unsigned int *IndependentBlockChroma) { + (void)SurfaceWidthChroma; + (void)SurfaceHeightChroma; + (void)BytePerPixelDETY; + (void)BytePerPixelDETC; int yuv420; int horz_div_l; int horz_div_c; @@ -1823,6 +1832,7 @@ static unsigned int CalculateVMAndRowBytes( int *DPDE0BytesFrame, int *MetaPTEBytesFrame) { + (void)SourcePixelFormat; struct vba_vars_st *v = &mode_lib->vba; unsigned int MPDEBytesFrame; unsigned int DCCMetaSurfaceBytes; @@ -3365,6 +3375,8 @@ double dml31_CalculateWriteBackDISPCLK( unsigned int HTotal, unsigned int WritebackLineBufferSize) { + (void)WritebackPixelFormat; + (void)WritebackVRatio; double DISPCLK_H, DISPCLK_V, DISPCLK_HB; DISPCLK_H = PixelClock * dml_ceil(WritebackHTaps / 8.0, 1) / WritebackHRatio; @@ -3383,6 +3395,8 @@ static double CalculateWriteBackDelay( int WritebackSourceHeight, unsigned int HTotal) { + (void)WritebackPixelFormat; + (void)WritebackHRatio; double CalculateWriteBackDelay; double Line_length; double Output_lines_last_notclamped; @@ -5566,6 +5580,9 @@ static void CalculateWatermarksAndDRAMSpeedChangeSupport( double *Z8StutterExitWatermark, double *Z8StutterEnterPlusExitWatermark) { + (void)DCFCLK; + (void)ReturnBW; + (void)DETBufferSizeC; struct vba_vars_st *v = &mode_lib->vba; double EffectiveLBLatencyHidingY; double EffectiveLBLatencyHidingC; @@ -5831,6 +5848,7 @@ static void CalculateUrgentBurstFactor( double *UrgentBurstFactorChroma, bool *NotEnoughUrgentLatencyHiding) { + (void)VRatioC; double LinesInDETLuma; double LinesInDETChroma; unsigned int LinesInCursorBuffer; @@ -6213,6 +6231,8 @@ static void CalculateVMGroupAndRequestTimes( double TimePerVMRequestVBlank[], double TimePerVMRequestFlip[]) { + (void)dpte_row_width_luma_ub; + (void)dpte_row_width_chroma_ub; int num_group_per_lower_vm_stage; int num_req_per_lower_vm_stage; int k; @@ -6350,6 +6370,8 @@ static void CalculateStutterEfficiency( int *Z8NumberOfStutterBurstsPerFrame, double *StutterPeriod) { + (void)ConfigReturnBufferSizeInKByte; + struct vba_vars_st *v = &mode_lib->vba; double DETBufferingTimeY; @@ -6649,6 +6671,7 @@ static void CalculateSwathAndDETConfiguration( bool ViewportSizeSupportPerPlane[], bool *ViewportSizeSupport) { + (void)HRatioChroma; int MaximumSwathHeightY[DC__NUM_DPP__MAX]; int MaximumSwathHeightC[DC__NUM_DPP__MAX]; int MinimumSwathHeightY; @@ -6823,6 +6846,7 @@ static void CalculateSwathWidth( int swath_width_luma_ub[], int swath_width_chroma_ub[]) { + (void)BytePerPixY; enum odm_combine_mode MainPlaneODMCombine; int j, k; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c index bfeb01477f0c..dfa1bc31eb0a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c @@ -51,6 +51,7 @@ static double get_refcyc_per_delivery( unsigned int delivery_width, unsigned int req_per_swath_ub) { + (void)mode_lib; double refcyc_per_delivery = 0.0; if (vratio <= 1.0) { @@ -785,6 +786,7 @@ static void calculate_ttu_cursor( unsigned int cur_width, enum cursor_bpp cur_bpp) { + (void)mode_lib; unsigned int cur_src_width = cur_width; unsigned int cur_req_size = 0; unsigned int cur_req_width = 0; @@ -859,6 +861,12 @@ static void dml_rq_dlg_get_dlg_params( const bool ignore_viewport_pos, const bool immediate_flip_support) { + (void)cstate_en; + (void)pstate_en; + (void)vm_en; + (void)ignore_viewport_pos; + (void)immediate_flip_support; + (void)dlg_sys_param; const display_pipe_source_params_st *src = &e2e_pipe_param[pipe_idx].pipe.src; const display_pipe_dest_params_st *dst = &e2e_pipe_param[pipe_idx].pipe.dest; const display_clocks_and_cfg_st *clks = &e2e_pipe_param[pipe_idx].clks_cfg; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c index 9f3938a50240..033fde774337 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c @@ -891,6 +891,11 @@ static bool CalculatePrefetchSchedule( double *VUpdateWidthPix, double *VReadyOffsetPix) { + (void)mode_lib; + (void)HostVMMinPageSize; + (void)SwathWidthY; + (void)SwathWidthC; + bool MyError = false; unsigned int DPPCycles, DISPCLKCycles; double DSTTotalPixelsAfterScaler; @@ -1508,6 +1513,10 @@ static void CalculateDCCConfiguration( unsigned int *IndependentBlockLuma, unsigned int *IndependentBlockChroma) { + (void)SurfaceWidthChroma; + (void)SurfaceHeightChroma; + (void)BytePerPixelDETY; + (void)BytePerPixelDETC; int yuv420; int horz_div_l; int horz_div_c; @@ -1840,6 +1849,7 @@ static unsigned int CalculateVMAndRowBytes( int *DPDE0BytesFrame, int *MetaPTEBytesFrame) { + (void)SourcePixelFormat; struct vba_vars_st *v = &mode_lib->vba; unsigned int MPDEBytesFrame; unsigned int DCCMetaSurfaceBytes; @@ -3471,6 +3481,8 @@ double dml314_CalculateWriteBackDISPCLK( unsigned int HTotal, unsigned int WritebackLineBufferSize) { + (void)WritebackPixelFormat; + (void)WritebackVRatio; double DISPCLK_H, DISPCLK_V, DISPCLK_HB; DISPCLK_H = PixelClock * dml_ceil(WritebackHTaps / 8.0, 1) / WritebackHRatio; @@ -3489,6 +3501,8 @@ static double CalculateWriteBackDelay( int WritebackSourceHeight, unsigned int HTotal) { + (void)WritebackPixelFormat; + (void)WritebackHRatio; double CalculateWriteBackDelay; double Line_length; double Output_lines_last_notclamped; @@ -5660,6 +5674,9 @@ static void CalculateWatermarksAndDRAMSpeedChangeSupport( double *Z8StutterExitWatermark, double *Z8StutterEnterPlusExitWatermark) { + (void)DCFCLK; + (void)ReturnBW; + (void)DETBufferSizeC; struct vba_vars_st *v = &mode_lib->vba; double EffectiveLBLatencyHidingY; double EffectiveLBLatencyHidingC; @@ -5925,6 +5942,7 @@ static void CalculateUrgentBurstFactor( double *UrgentBurstFactorChroma, bool *NotEnoughUrgentLatencyHiding) { + (void)VRatioC; double LinesInDETLuma; double LinesInDETChroma; unsigned int LinesInCursorBuffer; @@ -6308,6 +6326,8 @@ static void CalculateVMGroupAndRequestTimes( double TimePerVMRequestVBlank[], double TimePerVMRequestFlip[]) { + (void)dpte_row_width_luma_ub; + (void)dpte_row_width_chroma_ub; int num_group_per_lower_vm_stage; int num_req_per_lower_vm_stage; int k; @@ -6445,6 +6465,8 @@ static void CalculateStutterEfficiency( int *Z8NumberOfStutterBurstsPerFrame, double *StutterPeriod) { + (void)ConfigReturnBufferSizeInKByte; + struct vba_vars_st *v = &mode_lib->vba; double DETBufferingTimeY; @@ -6743,6 +6765,7 @@ static void CalculateSwathAndDETConfiguration( bool ViewportSizeSupportPerPlane[], bool *ViewportSizeSupport) { + (void)HRatioChroma; int MaximumSwathHeightY[DC__NUM_DPP__MAX]; int MaximumSwathHeightC[DC__NUM_DPP__MAX]; int MinimumSwathHeightY; @@ -6914,6 +6937,7 @@ static void CalculateSwathWidth( int swath_width_luma_ub[], int swath_width_chroma_ub[]) { + (void)BytePerPixY; enum odm_combine_mode MainPlaneODMCombine; int j, k; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c index 04df263ff65e..40a916c2a9c6 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c @@ -139,6 +139,7 @@ static double get_refcyc_per_delivery( unsigned int delivery_width, unsigned int req_per_swath_ub) { + (void)mode_lib; double refcyc_per_delivery = 0.0; if (vratio <= 1.0) { @@ -872,6 +873,7 @@ static void calculate_ttu_cursor( unsigned int cur_width, enum cursor_bpp cur_bpp) { + (void)mode_lib; unsigned int cur_src_width = cur_width; unsigned int cur_req_size = 0; unsigned int cur_req_width = 0; @@ -944,6 +946,12 @@ static void dml_rq_dlg_get_dlg_params( const bool ignore_viewport_pos, const bool immediate_flip_support) { + (void)cstate_en; + (void)pstate_en; + (void)vm_en; + (void)ignore_viewport_pos; + (void)immediate_flip_support; + (void)dlg_sys_param; const display_pipe_source_params_st *src = &e2e_pipe_param[pipe_idx].pipe.src; const display_pipe_dest_params_st *dst = &e2e_pipe_param[pipe_idx].pipe.dest; const display_clocks_and_cfg_st *clks = &e2e_pipe_param[pipe_idx].clks_cfg; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c index 8a0f128722b0..e29497204df7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c @@ -3488,6 +3488,7 @@ bool dcn32_allow_subvp_high_refresh_rate(struct dc *dc, struct dc_state *context */ double dcn32_determine_max_vratio_prefetch(struct dc *dc, struct dc_state *context) { + (void)dc; double max_vratio_pre = __DML_MAX_BW_RATIO_PRE__; // Default value is 4 int i; @@ -3593,6 +3594,7 @@ bool dcn32_find_vactive_pipe(struct dc *dc, const struct dc_state *context, stru void dcn32_set_clock_limits(const struct _vcs_dpi_soc_bounding_box_st *soc_bb) { + (void)soc_bb; dc_assert_fp_enabled(); dcn3_2_soc.clock_limits[0].dcfclk_mhz = 1200.0; } diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c index 19b142412a84..5e72966a8daf 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c @@ -457,6 +457,7 @@ void dml32_CalculateSwathAndDETConfiguration( bool ViewportSizeSupportPerSurface[], bool *ViewportSizeSupport) { + (void)HRatioChroma; unsigned int MaximumSwathHeightY[DC__NUM_DPP__MAX]; unsigned int MaximumSwathHeightC[DC__NUM_DPP__MAX]; unsigned int RoundedUpMaxSwathSizeBytesY[DC__NUM_DPP__MAX] = { 0 }; @@ -716,6 +717,7 @@ void dml32_CalculateSwathWidth( unsigned int swath_width_luma_ub[], // per-pipe unsigned int swath_width_chroma_ub[]) // per-pipe { + (void)BytePerPixY; unsigned int k, j; enum odm_combine_mode MainSurfaceODMMode; @@ -2304,6 +2306,7 @@ unsigned int dml32_CalculateVMAndRowBytes( unsigned int *DPDE0BytesFrame, unsigned int *MetaPTEBytesFrame) { + (void)SourcePixelFormat; unsigned int MPDEBytesFrame; unsigned int DCCMetaSurfaceBytes; unsigned int ExtraDPDEBytesFrame; @@ -2745,6 +2748,7 @@ void dml32_CalculateUrgentBurstFactor( double *UrgentBurstFactorChroma, bool *NotEnoughUrgentLatencyHiding) { + (void)VRatioC; double LinesInDETLuma; double LinesInDETChroma; unsigned int LinesInCursorBuffer; @@ -2900,6 +2904,8 @@ double dml32_CalculateWriteBackDelay( unsigned int WritebackSourceHeight, unsigned int HTotal) { + (void)WritebackPixelFormat; + (void)WritebackHRatio; double CalculateWriteBackDelay; double Line_length; double Output_lines_last_notclamped; @@ -2977,6 +2983,9 @@ void dml32_UseMinimumDCFCLK( /* Output */ double DCFCLKState[][2]) { + (void)MaxAveragePercentOfIdealSDPPortBWDisplayCanUseInNormalSystemOperation; + (void)ReadBandwidthLuma; + (void)ReadBandwidthChroma; unsigned int i, j, k; unsigned int dummy1; double dummy2, dummy3; @@ -3447,6 +3456,8 @@ bool dml32_CalculatePrefetchSchedule( double *VUpdateWidthPix, double *VReadyOffsetPix) { + (void)SwathWidthY; + (void)SwathWidthC; double DPPCLKDelaySubtotalPlusCNVCFormater = v->DPPCLKDelaySubtotal + v->DPPCLKDelayCNVCFormater; bool MyError = false; unsigned int DPPCycles, DISPCLKCycles; @@ -4145,6 +4156,7 @@ void dml32_CalculateFlipSchedule( double *final_flip_bw, bool *ImmediateFlipSupportedForPipe) { + (void)HostVMMinPageSize; double min_row_time = 0.0; unsigned int HostVMDynamicLevelsTrips; double TimeForFetchingMetaPTEImmediateFlip; @@ -4287,6 +4299,8 @@ void dml32_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport( bool *USRRetrainingSupport, double ActiveDRAMClockChangeLatencyMargin[]) { + (void)DCFCLK; + (void)ReturnBW; unsigned int i, j, k; unsigned int SurfaceWithMinActiveFCLKChangeMargin = 0; unsigned int DRAMClockChangeSupportNumber = 0; @@ -4655,6 +4669,8 @@ double dml32_CalculateWriteBackDISPCLK( unsigned int WritebackLineBufferSize, double DISPCLKDPPCLKVCOSpeed) { + (void)WritebackPixelFormat; + (void)WritebackVRatio; double DISPCLK_H, DISPCLK_V, DISPCLK_HB; DISPCLK_H = PixelClock * dml_ceil(WritebackHTaps / 8.0, 1) / WritebackHRatio; @@ -5166,6 +5182,8 @@ void dml32_CalculateVMGroupAndRequestTimes( double TimePerVMRequestVBlank[], double TimePerVMRequestFlip[]) { + (void)dpte_row_width_luma_ub; + (void)dpte_row_width_chroma_ub; unsigned int k; unsigned int num_group_per_lower_vm_stage; unsigned int num_req_per_lower_vm_stage; @@ -5321,6 +5339,11 @@ void dml32_CalculateDCCConfiguration( unsigned int *IndependentBlockLuma, unsigned int *IndependentBlockChroma) { + (void)SurfaceWidthChroma; + (void)SurfaceHeightChroma; + (void)TilingFormat; + (void)BytePerPixelDETY; + (void)BytePerPixelDETC; typedef enum { REQ_256Bytes, REQ_128BytesNonContiguous, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c index 6713cd8ba86a..7f40048dd67d 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c @@ -202,6 +202,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_5_soc = { void dcn35_build_wm_range_table_fpu(struct clk_mgr *clk_mgr) { + (void)clk_mgr; //TODO } diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c b/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c index da0cfbb071e6..684779ee54a3 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c @@ -162,6 +162,7 @@ void dml_log_pipe_params( display_e2e_pipe_params_st *pipes, int pipe_cnt) { + (void)mode_lib; display_pipe_source_params_st *pipe_src; display_pipe_dest_params_st *pipe_dest; scaler_ratio_depth_st *scale_ratio_depth; diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c index 12ff65b6a7e5..3f27293a41cb 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c +++ b/drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c @@ -49,6 +49,8 @@ void print__rq_params_st(struct display_mode_lib *mode_lib, const struct _vcs_dp void print__data_rq_sizing_params_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_display_data_rq_sizing_params_st *rq_sizing) { + (void)mode_lib; + (void)rq_sizing; dml_print("DML_RQ_DLG_CALC: =====================================\n"); dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_SIZING_PARAM_ST\n"); dml_print("DML_RQ_DLG_CALC: chunk_bytes = %0d\n", rq_sizing->chunk_bytes); @@ -64,6 +66,8 @@ void print__data_rq_sizing_params_st(struct display_mode_lib *mode_lib, const st void print__data_rq_dlg_params_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_display_data_rq_dlg_params_st *rq_dlg_param) { + (void)mode_lib; + (void)rq_dlg_param; dml_print("DML_RQ_DLG_CALC: =====================================\n"); dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_DLG_PARAM_ST\n"); dml_print( @@ -107,6 +111,8 @@ void print__data_rq_dlg_params_st(struct display_mode_lib *mode_lib, const struc void print__data_rq_misc_params_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_display_data_rq_misc_params_st *rq_misc_param) { + (void)mode_lib; + (void)rq_misc_param; dml_print("DML_RQ_DLG_CALC: =====================================\n"); dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_MISC_PARAM_ST\n"); dml_print( @@ -124,6 +130,8 @@ void print__data_rq_misc_params_st(struct display_mode_lib *mode_lib, const stru void print__dlg_sys_params_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_display_dlg_sys_params_st *dlg_sys_param) { + (void)dlg_sys_param; + (void)mode_lib; dml_print("DML_RQ_DLG_CALC: =====================================\n"); dml_print("DML_RQ_DLG_CALC: DISPLAY_RQ_DLG_PARAM_ST\n"); dml_print("DML_RQ_DLG_CALC: t_mclk_wm_us = %3.2f\n", dlg_sys_param->t_mclk_wm_us); @@ -144,6 +152,8 @@ void print__dlg_sys_params_st(struct display_mode_lib *mode_lib, const struct _v void print__data_rq_regs_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_display_data_rq_regs_st *rq_regs) { + (void)mode_lib; + (void)rq_regs; dml_print("DML_RQ_DLG_CALC: =====================================\n"); dml_print("DML_RQ_DLG_CALC: DISPLAY_DATA_RQ_REGS_ST\n"); dml_print("DML_RQ_DLG_CALC: chunk_size = 0x%0x\n", rq_regs->chunk_size); @@ -179,6 +189,8 @@ void print__rq_regs_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_ void print__dlg_regs_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_display_dlg_regs_st *dlg_regs) { + (void)dlg_regs; + (void)mode_lib; dml_print("DML_RQ_DLG_CALC: =====================================\n"); dml_print("DML_RQ_DLG_CALC: DISPLAY_DLG_REGS_ST\n"); dml_print( @@ -316,6 +328,8 @@ void print__dlg_regs_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi void print__ttu_regs_st(struct display_mode_lib *mode_lib, const struct _vcs_dpi_display_ttu_regs_st *ttu_regs) { + (void)mode_lib; + (void)ttu_regs; dml_print("DML_RQ_DLG_CALC: =====================================\n"); dml_print("DML_RQ_DLG_CALC: DISPLAY_TTU_REGS_ST\n"); dml_print( diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c b/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c index 88dc2b97e7bf..cf194bcba455 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c @@ -104,6 +104,7 @@ static double get_refcyc_per_delivery( unsigned int delivery_width, unsigned int req_per_swath_ub) { + (void)mode_lib; double refcyc_per_delivery = 0.0; if (vratio <= 1.0) { @@ -133,6 +134,7 @@ static double get_vratio_pre( double vinit, double l_sw) { + (void)mode_lib; double prefill = dml_floor(vinit, 1); double vratio_pre = 1.0; @@ -174,6 +176,7 @@ static void get_swath_need( unsigned int swath_height, double vinit) { + (void)mode_lib; double prefill = dml_floor(vinit, 1); unsigned int max_partial_sw_int; diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c index 0e70ffc784b1..ef605e0a75e3 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c @@ -288,6 +288,7 @@ void dpp1_cnv_setup ( enum dc_color_space input_color_space, struct cnv_alpha_2bit_lut *alpha_2bit_lut) { + (void)alpha_2bit_lut; uint32_t pixel_format; uint32_t alpha_en; enum pixel_format_description fmt ; diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp.c index c433f4b876e9..8d5000790904 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp.c @@ -92,7 +92,10 @@ void dpp2_power_on_obuf( void dpp2_dummy_program_input_lut( struct dpp *dpp_base, const struct dc_gamma *gamma) -{} +{ + (void)dpp_base; + (void)gamma; +} static void dpp2_cnv_setup ( struct dpp *dpp_base, @@ -369,7 +372,11 @@ void oppn20_dummy_program_regamma_pwl( struct dpp *dpp, const struct pwl_params *params, enum opp_regamma mode) -{} +{ + (void)dpp; + (void)params; + (void)mode; +} static struct dpp_funcs dcn20_dpp_funcs = { .dpp_read_state = dpp20_read_state, diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c index 31613372e214..26f9485f165d 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c @@ -1016,6 +1016,7 @@ static void dpp20_set_3dlut_mode( bool is_color_channel_12bits, bool is_lut_size17x17x17) { + (void)is_color_channel_12bits; uint32_t lut_mode; struct dcn20_dpp *dpp = TO_DCN20_DPP(dpp_base); diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c index 8a146968ee15..e7880fc61b4a 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c @@ -1307,6 +1307,7 @@ static void dpp3_set_3dlut_mode( bool is_color_channel_12bits, bool is_lut_size17x17x17) { + (void)is_color_channel_12bits; uint32_t lut_mode; struct dcn3_dpp *dpp = TO_DCN30_DPP(dpp_base); diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c index 3284084ca7ad..8170a86ad0ea 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c @@ -80,6 +80,7 @@ static void dpp3_program_gammcor_lut( uint32_t num, bool is_ram_a) { + (void)is_ram_a; uint32_t i; struct dcn3_dpp *dpp = TO_DCN30_DPP(dpp_base); uint32_t last_base_value_red = rgb[num-1].red_reg + rgb[num-1].delta_red_reg; diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c index 62bf7cea21d8..821d5173b59f 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c @@ -132,6 +132,9 @@ void dpp401_set_cursor_position( uint32_t width, uint32_t height) { + (void)param; + (void)width; + (void)height; struct dcn401_dpp *dpp = TO_DCN401_DPP(dpp_base); uint32_t cur_en = pos->enable ? 1 : 0; @@ -237,6 +240,8 @@ void dpp401_set_cursor_matrix( enum dc_color_space color_space, struct dc_csc_transform cursor_csc_color_matrix) { + (void)color_space; + (void)cursor_csc_color_matrix; //Since we don't have cursor matrix information, force bypass mode by passing in unknown color space dpp401_program_cursor_csc(dpp_base, COLOR_SPACE_UNKNOWN, NULL); } diff --git a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb.c b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb.c index bc058f682438..0ee4f83a02eb 100644 --- a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb.c +++ b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb.c @@ -45,6 +45,7 @@ static bool dwb3_get_caps(struct dwbc *dwbc, struct dwb_caps *caps) { + (void)dwbc; if (caps) { caps->adapter_id = 0; /* we only support 1 adapter currently */ caps->hw_version = DCN_VERSION_3_0; diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c index dcbcf6b85abf..e7e1d9979876 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c @@ -45,6 +45,7 @@ static bool offset_to_id( enum gpio_id *id, uint32_t *en) { + (void)mask; switch (offset) { /* HPD */ case REG(HPD0_DC_HPD_INT_STATUS): diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c index f3d562c8df4c..d81a71ac00d2 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c @@ -60,6 +60,7 @@ bool dal_hw_factory_init( enum dce_version dce_version, enum dce_environment dce_environment) { + (void)dce_environment; switch (dce_version) { #if defined(CONFIG_DRM_AMD_DC_SI) case DCE_VERSION_6_0: diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c index 660510842ecf..f0d400972897 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_gpio.c @@ -199,5 +199,6 @@ void dal_hw_gpio_construct( void dal_hw_gpio_destruct( struct hw_gpio *pin) { + (void)pin; ASSERT(!pin->base.opened); } diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c index 1c977fc4d0e3..e6e36a912b13 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c @@ -64,6 +64,7 @@ bool dal_hw_translate_init( enum dce_version dce_version, enum dce_environment dce_environment) { + (void)dce_environment; switch (dce_version) { #if defined(CONFIG_DRM_AMD_DC_SI) case DCE_VERSION_6_0: diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c index 5a03758e3de6..3c298192f359 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c @@ -943,6 +943,7 @@ static void hubbub31_get_dchub_ref_freq(struct hubbub *hubbub, unsigned int dccg_ref_freq_inKhz, unsigned int *dchub_ref_freq_inKhz) { + (void)dccg_ref_freq_inKhz; struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); uint32_t ref_div = 0; uint32_t ref_en = 0; diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c index 43ba399f4822..82d4e3e0e5e8 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c @@ -259,6 +259,7 @@ void hubbub35_get_dchub_ref_freq(struct hubbub *hubbub, unsigned int dccg_ref_freq_inKhz, unsigned int *dchub_ref_freq_inKhz) { + (void)dccg_ref_freq_inKhz; struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); uint32_t ref_div = 0; uint32_t ref_en = 0; diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c index b0a4b68cf359..3b9542c08f3d 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c @@ -70,6 +70,7 @@ bool hubbub401_program_urgent_watermarks( unsigned int refclk_mhz, bool safe_to_lower) { + (void)refclk_mhz; struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); bool wm_pending = false; @@ -188,6 +189,7 @@ bool hubbub401_program_stutter_watermarks( unsigned int refclk_mhz, bool safe_to_lower) { + (void)refclk_mhz; struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); bool wm_pending = false; @@ -287,6 +289,7 @@ bool hubbub401_program_pstate_watermarks( unsigned int refclk_mhz, bool safe_to_lower) { + (void)refclk_mhz; struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); bool wm_pending = false; @@ -414,6 +417,7 @@ bool hubbub401_program_usr_watermarks( unsigned int refclk_mhz, bool safe_to_lower) { + (void)refclk_mhz; struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); bool wm_pending = false; diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c index a436fa71d4b4..73b6b0ffcb74 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c @@ -488,6 +488,8 @@ static bool hubbub42_program_watermarks( static void hubbub42_set_request_limit(struct hubbub *hubbub, int memory_channel_count, int words_per_channel) { + (void)memory_channel_count; + (void)words_per_channel; struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); uint32_t request_limit = 96; //MAX(12 * memory_channel_count, 96); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c index 6378e3fd7249..7c97a774141f 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c @@ -143,6 +143,7 @@ void hubp1_program_tiling( const struct dc_tiling_info *info, const enum surface_pixel_format pixel_format) { + (void)pixel_format; struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); REG_UPDATE_6(DCSURF_ADDR_CONFIG, @@ -563,6 +564,7 @@ void hubp1_program_surface_config( bool horizontal_mirror, unsigned int compat_level) { + (void)compat_level; hubp1_dcc_control(hubp, dcc->enable, dcc->independent_64b_blks); hubp1_program_tiling(hubp, tiling_info, format); hubp1_program_size(hubp, format, plane_size, dcc); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c index 92288de4cc10..ceee5165fd6a 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c @@ -313,6 +313,7 @@ static void hubp2_program_tiling( const struct dc_tiling_info *info, const enum surface_pixel_format pixel_format) { + (void)pixel_format; REG_UPDATE_3(DCSURF_ADDR_CONFIG, NUM_PIPES, log_2(info->gfx9.num_pipes), PIPE_INTERLEAVE, info->gfx9.pipe_interleave, @@ -557,6 +558,7 @@ void hubp2_program_surface_config( bool horizontal_mirror, unsigned int compat_level) { + (void)compat_level; struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); hubp2_dcc_control(hubp, dcc->enable, dcc->independent_64b_blks); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c index 0cc6f4558989..e2708e30eb1b 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c @@ -321,6 +321,7 @@ void hubp3_program_tiling( const struct dc_tiling_info *info, const enum surface_pixel_format pixel_format) { + (void)pixel_format; REG_UPDATE_4(DCSURF_ADDR_CONFIG, NUM_PIPES, log_2(info->gfx9.num_pipes), PIPE_INTERLEAVE, info->gfx9.pipe_interleave, @@ -418,6 +419,7 @@ void hubp3_program_surface_config( bool horizontal_mirror, unsigned int compat_level) { + (void)compat_level; struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); hubp3_dcc_control_sienna_cichlid(hubp, dcc); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c index 79c583e258c7..c879f4901c7d 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c @@ -179,6 +179,7 @@ void hubp35_program_surface_config( bool horizontal_mirror, unsigned int compat_level) { + (void)compat_level; struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); hubp3_dcc_control_sienna_cichlid(hubp, dcc); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c index 263e0c4d34f6..5a816442deee 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c @@ -657,6 +657,7 @@ void hubp401_program_tiling( const struct dc_tiling_info *info, const enum surface_pixel_format pixel_format) { + (void)pixel_format; /* DCSURF_ADDR_CONFIG still shows up in reg spec, but does not need to be programmed for DCN4x * All 4 fields NUM_PIPES, PIPE_INTERLEAVE, MAX_COMPRESSED_FRAGS and NUM_PKRS are irrelevant. * @@ -671,6 +672,7 @@ void hubp401_program_size( const struct plane_size *plane_size, struct dc_plane_dcc_param *dcc) { + (void)dcc; struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); uint32_t pitch, pitch_c; bool use_pitch_c = false; @@ -709,6 +711,7 @@ void hubp401_program_surface_config( bool horizontal_mirror, unsigned int compat_level) { + (void)compat_level; struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); hubp401_dcc_control(hubp, dcc); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c index d85a4ab957a4..ad6badcceb12 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c @@ -301,6 +301,7 @@ static void hubp42_program_surface_config( bool horizontal_mirror, unsigned int compat_level) { + (void)compat_level; struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); hubp3_dcc_control_sienna_cichlid(hubp, dcc); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index 8f56f164d567..5273ca09fe12 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -201,6 +201,8 @@ static void enable_display_pipe_clock_gating( struct dc_context *ctx, bool clock_gating) { + (void)ctx; + (void)clock_gating; /*TODO*/ } @@ -284,6 +286,7 @@ static bool dce110_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) { + (void)dc; struct input_pixel_processor *ipp = pipe_ctx->plane_res.ipp; const struct dc_transfer_func *tf = NULL; struct ipp_prescale_params prescale_params = { 0 }; @@ -607,6 +610,7 @@ static bool dce110_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_stream_state *stream) { + (void)dc; struct transform *xfm = pipe_ctx->plane_res.xfm; xfm->funcs->opp_power_on_regamma_lut(xfm, true); @@ -1539,6 +1543,7 @@ static enum dc_status dce110_enable_stream_timing( struct dc_state *context, struct dc *dc) { + (void)context; struct dc_stream_state *stream = pipe_ctx->stream; struct pipe_ctx *pipe_ctx_old = &dc->current_state->res_ctx. pipe_ctx[pipe_ctx->pipe_idx]; @@ -2728,6 +2733,7 @@ static void program_gamut_remap(struct pipe_ctx *pipe_ctx) static void update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) { + (void)dc; struct dc_plane_state *plane_state = pipe_ctx->plane_state; if (plane_state == NULL) @@ -2814,6 +2820,8 @@ static void dce110_enable_timing_synchronization( int group_size, struct pipe_ctx *grouped_pipes[]) { + (void)state; + (void)group_index; struct dcp_gsl_params gsl_params = { 0 }; int i; DC_LOGGER_INIT(dc->ctx); @@ -2889,6 +2897,8 @@ static void dce110_enable_per_frame_crtc_position_reset( static void dce110_init_pipes(struct dc *dc, struct dc_state *context) { + (void)context; + (void)dc; // Do nothing } @@ -3154,10 +3164,13 @@ static void dce110_post_unlock_program_front_end( struct dc *dc, struct dc_state *context) { + (void)dc; + (void)context; } static void dce110_power_down_fe(struct dc *dc, struct dc_state *state, struct pipe_ctx *pipe_ctx) { + (void)state; struct dce_hwseq *hws = dc->hwseq; int fe_idx = pipe_ctx->plane_res.mi ? pipe_ctx->plane_res.mi->inst : pipe_ctx->pipe_idx; @@ -3178,6 +3191,9 @@ static void dce110_wait_for_mpcc_disconnect( struct resource_pool *res_pool, struct pipe_ctx *pipe_ctx) { + (void)dc; + (void)res_pool; + (void)pipe_ctx; /* do nothing*/ } @@ -3187,6 +3203,10 @@ static void program_output_csc(struct dc *dc, uint16_t *matrix, int opp_id) { + (void)dc; + (void)colorspace; + (void)matrix; + (void)opp_id; int i; struct out_csc_color_matrix tbl_entry; @@ -3331,6 +3351,7 @@ void dce110_enable_lvds_link_output(struct dc_link *link, enum clock_source_id clock_source, uint32_t pixel_clock) { + (void)link_res; link->link_enc->funcs->enable_lvds_output( link->link_enc, clock_source, @@ -3345,6 +3366,7 @@ void dce110_enable_tmds_link_output(struct dc_link *link, enum dc_color_depth color_depth, uint32_t pixel_clock) { + (void)link_res; link->link_enc->funcs->enable_tmds_output( link->link_enc, clock_source, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce120/dce120_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce120/dce120_hwseq.c index 2a62f63d0357..0689bbf12ad8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce120/dce120_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce120/dce120_hwseq.c @@ -154,6 +154,10 @@ static bool dce120_enable_display_power_gating( struct dc_bios *dcb, enum pipe_gating_control power_gating) { + (void)dc; + (void)controller_id; + (void)dcb; + (void)power_gating; /* disable for bringup */ #if 0 enum bp_result bp_result = BP_RESULT_OK; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 996ec85f9727..756ce8379538 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -86,6 +86,7 @@ static void print_microsec(struct dc_context *dc_ctx, struct dc_log_buffer_ctx *log_ctx, uint32_t ref_cycle) { + (void)log_ctx; const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; static const unsigned int frac = 1000; uint32_t us_x10 = (ref_cycle * frac) / ref_clk_mhz; @@ -252,6 +253,7 @@ void dcn10_lock_all_pipes(struct dc *dc, static void log_mpc_crc(struct dc *dc, struct dc_log_buffer_ctx *log_ctx) { + (void)log_ctx; struct dc_context *dc_ctx = dc->ctx; struct dce_hwseq *hws = dc->hwseq; @@ -450,6 +452,7 @@ static void dcn10_log_hubp_states(struct dc *dc, void *log_ctx) static void dcn10_log_color_state(struct dc *dc, struct dc_log_buffer_ctx *log_ctx) { + (void)log_ctx; struct dc_context *dc_ctx = dc->ctx; struct resource_pool *pool = dc->res_pool; bool is_gamut_remap_available = false; @@ -813,6 +816,7 @@ void dcn10_log_hw_state(struct dc *dc, bool dcn10_did_underflow_occur(struct dc *dc, struct pipe_ctx *pipe_ctx) { + (void)dc; struct hubp *hubp = pipe_ctx->plane_res.hubp; struct timing_generator *tg = pipe_ctx->stream_res.tg; @@ -1181,6 +1185,7 @@ enum dc_status dcn10_enable_stream_timing( struct dc_state *context, struct dc *dc) { + (void)context; struct dc_stream_state *stream = pipe_ctx->stream; enum dc_color_space color_space; struct tg_color black_color = {0}; @@ -1284,6 +1289,7 @@ static void dcn10_reset_back_end_for_pipe( struct pipe_ctx *pipe_ctx, struct dc_state *context) { + (void)context; int i; struct dc_link *link; DC_LOGGER_INIT(dc->ctx); @@ -1551,6 +1557,7 @@ void dcn10_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx) void dcn10_disable_plane(struct dc *dc, struct dc_state *state, struct pipe_ctx *pipe_ctx) { + (void)state; struct dce_hwseq *hws = dc->hwseq; DC_LOGGER_INIT(dc->ctx); @@ -2004,6 +2011,7 @@ static bool patch_address_for_sbs_tb_stereo( void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) { + (void)dc; bool addr_patched = false; PHYSICAL_ADDRESS_LOC addr; struct dc_plane_state *plane_state = pipe_ctx->plane_state; @@ -2030,6 +2038,7 @@ void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) bool dcn10_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) { + (void)dc; struct dpp *dpp_base = pipe_ctx->plane_res.dpp; const struct dc_transfer_func *tf = NULL; bool result = true; @@ -2472,6 +2481,7 @@ void dcn10_enable_vblanks_synchronization( int group_size, struct pipe_ctx *grouped_pipes[]) { + (void)group_index; struct output_pixel_processor *opp; struct timing_generator *tg; int i, width = 0, height = 0, master; @@ -2537,6 +2547,7 @@ void dcn10_enable_timing_synchronization( int group_size, struct pipe_ctx *grouped_pipes[]) { + (void)group_index; struct output_pixel_processor *opp; struct timing_generator *tg; int i, width = 0, height = 0; @@ -2641,6 +2652,7 @@ static void mmhub_read_vm_system_aperture_settings(struct dcn10_hubp *hubp1, struct vm_system_aperture_param *apt, struct dce_hwseq *hws) { + (void)hubp1; PHYSICAL_ADDRESS_LOC physical_page_number; uint32_t logical_addr_low; uint32_t logical_addr_high; @@ -2666,6 +2678,7 @@ static void mmhub_read_vm_context0_settings(struct dcn10_hubp *hubp1, struct vm_context0_param *vm0, struct dce_hwseq *hws) { + (void)hubp1; PHYSICAL_ADDRESS_LOC fb_base; PHYSICAL_ADDRESS_LOC fb_offset; uint32_t fb_base_value; @@ -2724,6 +2737,7 @@ static void dcn10_enable_plane( struct pipe_ctx *pipe_ctx, struct dc_state *context) { + (void)context; struct dce_hwseq *hws = dc->hwseq; if (dc->debug.sanity_checks) { @@ -2821,6 +2835,8 @@ void dcn10_program_output_csc(struct dc *dc, uint16_t *matrix, int opp_id) { + (void)dc; + (void)opp_id; if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) { if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment != NULL) { @@ -3586,6 +3602,10 @@ bool dcn10_dummy_display_power_gating( struct dc_bios *dcb, enum pipe_gating_control power_gating) { + (void)dc; + (void)controller_id; + (void)dcb; + (void)power_gating; return true; } @@ -4052,6 +4072,7 @@ enum dc_status dcn10_set_clock(struct dc *dc, uint32_t clk_khz, uint32_t stepping) { + (void)stepping; struct dc_state *context = dc->current_state; struct dc_clock_config clock_cfg = {0}; struct dc_clocks *current_clocks = &context->bw_ctx.bw.dcn.clk; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index a673ab0803a8..288e4edaa9a2 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -75,6 +75,7 @@ void dcn20_log_color_state(struct dc *dc, struct dc_log_buffer_ctx *log_ctx) { + (void)log_ctx; struct dc_context *dc_ctx = dc->ctx; struct resource_pool *pool = dc->res_pool; bool is_gamut_remap_available = false; @@ -379,6 +380,7 @@ void dcn20_program_triple_buffer( struct pipe_ctx *pipe_ctx, bool enable_triple_buffer) { + (void)dc; if (pipe_ctx->plane_res.hubp && pipe_ctx->plane_res.hubp->funcs) { pipe_ctx->plane_res.hubp->funcs->hubp_enable_tripleBuffer( pipe_ctx->plane_res.hubp, @@ -1175,6 +1177,8 @@ bool dcn20_set_input_transfer_func(struct dc *dc, void dcn20_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx) { + (void)context; + (void)dc; struct pipe_ctx *odm_pipe; int opp_cnt = 1; int opp_inst[MAX_PIPES] = { pipe_ctx->stream_res.opp->inst }; @@ -1297,6 +1301,7 @@ static void dcn20_power_on_plane_resources( void dcn20_enable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context) { + (void)context; //if (dc->debug.sanity_checks) { // dcn10_verify_allow_pstate_change_high(dc); //} @@ -2652,6 +2657,7 @@ void dcn20_init_vm_ctx( struct dc_virtual_addr_space_config *va_config, int vmid) { + (void)hws; struct dcn_hubbub_virt_addr_config config; if (vmid == 0) { @@ -2670,6 +2676,7 @@ void dcn20_init_vm_ctx( int dcn20_init_sys_ctx(struct dce_hwseq *hws, struct dc *dc, struct dc_phy_addr_space_config *pa_config) { + (void)hws; struct dcn_hubbub_phys_addr_config config; config.system_aperture.fb_top = pa_config->system_aperture.fb_top; @@ -2799,6 +2806,7 @@ void dcn20_reset_back_end_for_pipe( struct pipe_ctx *pipe_ctx, struct dc_state *context) { + (void)context; struct dc_link *link = pipe_ctx->stream->link; const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); struct dccg *dccg = dc->res_pool->dccg; @@ -3245,6 +3253,7 @@ void dcn20_set_disp_pattern_generator(const struct dc *dc, const struct tg_color *solid_color, int width, int height, int offset) { + (void)dc; pipe_ctx->stream_res.opp->funcs->opp_set_disp_pattern_generator(pipe_ctx->stream_res.opp, test_pattern, color_space, color_depth, solid_color, width, height, offset); } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index 34d4519b3a17..2aa0f1de8103 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -75,6 +75,7 @@ void dcn30_log_color_state(struct dc *dc, struct dc_log_buffer_ctx *log_ctx) { + (void)log_ctx; struct dc_context *dc_ctx = dc->ctx; struct resource_pool *pool = dc->res_pool; bool is_gamut_remap_available = false; @@ -1183,6 +1184,7 @@ void dcn30_set_disp_pattern_generator(const struct dc *dc, const struct tg_color *solid_color, int width, int height, int offset) { + (void)dc; pipe_ctx->stream_res.opp->funcs->opp_set_disp_pattern_generator(pipe_ctx->stream_res.opp, test_pattern, color_space, color_depth, solid_color, width, height, offset); } @@ -1237,6 +1239,7 @@ void dcn30_get_underflow_debug_data(const struct dc *dc, struct timing_generator *tg, struct dc_underflow_debug_data *out_data) { + (void)tg; struct hubbub *hubbub = dc->res_pool->hubbub; if (hubbub) { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c index 3bc56ac346f3..6e1877a8682d 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c @@ -45,20 +45,31 @@ void dcn303_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on) { + (void)dpp_inst; + (void)hws; + (void)power_on; /*DCN303 removes PG registers*/ } void dcn303_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on) { + (void)hubp_inst; + (void)hws; + (void)power_on; /*DCN303 removes PG registers*/ } void dcn303_dsc_pg_control(struct dce_hwseq *hws, unsigned int dsc_inst, bool power_on) { + (void)dsc_inst; + (void)hws; + (void)power_on; /*DCN303 removes PG registers*/ } void dcn303_enable_power_gating_plane(struct dce_hwseq *hws, bool enable) { + (void)enable; + (void)hws; /*DCN303 removes PG registers*/ } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c index 1fba44aecdd3..b4afb2bc4493 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c @@ -484,6 +484,7 @@ void dcn31_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool p int dcn31_init_sys_ctx(struct dce_hwseq *hws, struct dc *dc, struct dc_phy_addr_space_config *pa_config) { + (void)hws; struct dcn_hubbub_phys_addr_config config = {0}; config.system_aperture.fb_top = pa_config->system_aperture.fb_top; @@ -511,6 +512,7 @@ static void dcn31_reset_back_end_for_pipe( struct pipe_ctx *pipe_ctx, struct dc_state *context) { + (void)context; struct dc_link *link; if (pipe_ctx->stream_res.stream_enc == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c index 3e239124c17d..858a06b03b57 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c @@ -172,6 +172,7 @@ static unsigned int get_odm_config(struct pipe_ctx *pipe_ctx, unsigned int *opp_ void dcn314_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx) { + (void)context; struct pipe_ctx *odm_pipe; int opp_cnt = 0; int opp_inst[MAX_PIPES] = {0}; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index ef08c98b11e9..b45ceb570a5c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -570,6 +570,7 @@ bool dcn32_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_stream_state *stream) { + (void)dc; int mpcc_id = pipe_ctx->plane_res.hubp->inst; struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; const struct pwl_params *params = NULL; @@ -1143,6 +1144,7 @@ static unsigned int get_odm_config(struct pipe_ctx *pipe_ctx, unsigned int *opp_ void dcn32_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx) { + (void)context; struct pipe_ctx *odm_pipe; int opp_cnt = 0; int opp_inst[MAX_PIPES] = {0}; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index 7c25911089b8..f133b52ea958 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -428,6 +428,7 @@ static unsigned int get_odm_config(struct pipe_ctx *pipe_ctx, unsigned int *opp_ void dcn35_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx) { + (void)context; struct pipe_ctx *odm_pipe; int opp_cnt = 0; int opp_inst[MAX_PIPES] = {0}; @@ -816,6 +817,7 @@ void dcn35_init_pipes(struct dc *dc, struct dc_state *context) void dcn35_enable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context) { + (void)context; struct dpp *dpp = pipe_ctx->plane_res.dpp; /* enable DCFCLK current DCHUB */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 60ad606c06ec..7e6bdefb5471 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -557,6 +557,7 @@ bool dcn401_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_stream_state *stream) { + (void)dc; int mpcc_id = pipe_ctx->plane_res.hubp->inst; struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; const struct pwl_params *params = NULL; @@ -618,6 +619,7 @@ static void enable_stream_timing_calc( struct drr_params *params, unsigned int *event_triggers) { + (void)dc; struct dc_stream_state *stream = pipe_ctx->stream; int i; @@ -1395,6 +1397,7 @@ void dcn401_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) { + (void)context; /* use always for now */ union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; @@ -1869,6 +1872,7 @@ void dcn401_reset_back_end_for_pipe( struct pipe_ctx *pipe_ctx, struct dc_state *context) { + (void)context; struct dc_link *link = pipe_ctx->stream->link; const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); @@ -3244,6 +3248,7 @@ void dcn401_update_writeback_sequence( struct dc_state *context, struct block_sequence_state *seq_state) { + (void)context; struct dwbc *dwb; struct mcif_wb *mcif_wb; @@ -3449,6 +3454,7 @@ void dcn401_enable_plane_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context, struct block_sequence_state *seq_state) { + (void)context; struct dce_hwseq *hws = dc->hwseq; uint32_t org_ip_request_cntl = 0; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index ba1813eb3c6c..46f2f9833d9e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -386,6 +386,7 @@ void dcn42_program_cm_hist( struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) { + (void)dc; struct dpp *dpp = pipe_ctx->plane_res.dpp; if (dpp && dpp->funcs->dpp_cm_hist_control) @@ -1000,6 +1001,7 @@ void dcn42_root_clock_control(struct dc *dc, } void dcn42_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) { + (void)dc; struct crtc_stereo_flags flags = { 0 }; struct dc_stream_state *stream = pipe_ctx->stream; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c index 002b09740fc3..015f3659cf77 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c @@ -183,6 +183,7 @@ bool dal_irq_service_dummy_set(struct irq_service *irq_service, const struct irq_source_info *info, bool enable) { + (void)enable; DC_LOG_ERROR("%s: called for non-implemented irq source, src_id=%u, ext_id=%u\n", __func__, info->src_id, info->ext_id); @@ -328,6 +329,7 @@ enum dc_irq_source to_dal_irq_source_dce110( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; switch (src_id) { case VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c index 113bd76c95db..0bdb62b883aa 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c @@ -42,6 +42,9 @@ static enum dc_irq_source to_dal_irq_source_dcn10(struct irq_service *irq_servic uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c b/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c index 98eedcac1247..8a0f4b5d6956 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c @@ -43,6 +43,9 @@ static enum dc_irq_source to_dal_irq_source_dcn20( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c b/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c index be02ca2861b3..9d13c0cc91f0 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c @@ -42,6 +42,9 @@ static enum dc_irq_source to_dal_irq_source_dcn21(struct irq_service *irq_servic uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c b/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c index fe830a55f320..78338af86666 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c @@ -50,6 +50,9 @@ static enum dc_irq_source to_dal_irq_source_dcn30( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c b/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c index d77d51ed5717..2f47a9fbcd43 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c @@ -37,6 +37,9 @@ static enum dc_irq_source to_dal_irq_source_dcn302(struct irq_service *irq_service, uint32_t src_id, uint32_t ext_id) { + (void)ext_id; + (void)irq_service; + (void)src_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c b/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c index afe3d7d4a56f..236a7278a8cf 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c @@ -38,6 +38,9 @@ static enum dc_irq_source to_dal_irq_source_dcn303(struct irq_service *irq_servi uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c b/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c index 5c86e950adfd..213e5da31b19 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c @@ -40,6 +40,9 @@ static enum dc_irq_source to_dal_irq_source_dcn31(struct irq_service *irq_servic uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c b/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c index 34aa7a004454..8aacc229b002 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c @@ -42,6 +42,9 @@ static enum dc_irq_source to_dal_irq_source_dcn314(struct irq_service *irq_servi uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c b/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c index f63990a6c6c4..b3bddc87afed 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c @@ -47,6 +47,9 @@ static enum dc_irq_source to_dal_irq_source_dcn315( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c b/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c index 5d4d5ed0589c..f407ba72acdb 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c @@ -41,6 +41,9 @@ static enum dc_irq_source to_dal_irq_source_dcn32( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c b/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c index 05aeb6ed676e..2f2985075f88 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c @@ -39,6 +39,9 @@ static enum dc_irq_source to_dal_irq_source_dcn35( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c b/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c index 9d835b6ffe1c..1ed75b53e131 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c @@ -18,6 +18,9 @@ static enum dc_irq_source to_dal_irq_source_dcn351( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c b/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c index 3da9f01dd511..4c321c26f02f 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c @@ -17,6 +17,9 @@ static enum dc_irq_source to_dal_irq_source_dcn36( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c b/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c index a12bb3cc4c43..059c5c636fd9 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c @@ -20,6 +20,9 @@ static enum dc_irq_source to_dal_irq_source_dcn401( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c b/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c index bdf733d37a76..f4d1ce9079de 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c @@ -19,6 +19,9 @@ static enum dc_irq_source to_dal_irq_source_dcn42( uint32_t src_id, uint32_t ext_id) { + (void)irq_service; + (void)src_id; + (void)ext_id; switch (src_id) { case DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP: return DC_IRQ_SOURCE_VBLANK1; diff --git a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c index 693d852b1c40..060460abc377 100644 --- a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c +++ b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c @@ -66,6 +66,7 @@ static void dp_retrain_link_dp_test(struct dc_link *link, struct dc_link_settings *link_setting, bool skip_video_pattern) { + (void)skip_video_pattern; struct pipe_ctx *pipes[MAX_PIPES]; struct dc_state *state = link->dc->current_state; struct dc_stream_update stream_update = { 0 }; @@ -483,6 +484,7 @@ static void set_crtc_test_pattern(struct dc_link *link, enum dp_test_pattern test_pattern, enum dp_test_pattern_color_space test_pattern_color_space) { + (void)test_pattern_color_space; enum controller_dp_test_pattern controller_test_pattern; enum dc_color_depth color_depth = pipe_ctx-> stream->timing.display_color_depth; diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c index 5d708039c7cf..2a87b23582f3 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c @@ -81,6 +81,10 @@ static void set_dio_dpia_lane_settings(struct dc_link *link, const struct dc_link_settings *link_settings, const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) { + (void)link; + (void)link_res; + (void)link_settings; + (void)lane_settings; } static void enable_dpia_link_output(struct dc_link *link, diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c index cec68c5dba13..dbbedeeed298 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c @@ -110,6 +110,8 @@ void enable_hpo_dp_link_output(struct dc_link *link, enum clock_source_id clock_source, const struct dc_link_settings *link_settings) { + (void)signal; + (void)clock_source; if (!link_res->hpo_dp_link_enc) { DC_LOG_ERROR("%s: invalid hpo_dp_link_enc\n", __func__); return; @@ -160,6 +162,7 @@ static void set_hpo_dp_lane_settings(struct dc_link *link, const struct dc_link_settings *link_settings, const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) { + (void)link; link_res->hpo_dp_link_enc->funcs->set_ffe( link_res->hpo_dp_link_enc, link_settings, @@ -170,6 +173,7 @@ void update_hpo_dp_stream_allocation_table(struct dc_link *link, const struct link_resource *link_res, const struct link_mst_stream_allocation_table *table) { + (void)link; link_res->hpo_dp_link_enc->funcs->update_stream_allocation_table( link_res->hpo_dp_link_enc, table); @@ -178,6 +182,7 @@ void update_hpo_dp_stream_allocation_table(struct dc_link *link, void setup_hpo_dp_audio_output(struct pipe_ctx *pipe_ctx, struct audio_output *audio_output, uint32_t audio_inst) { + (void)audio_output; pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_audio_setup( pipe_ctx->stream_res.hpo_dp_stream_enc, audio_inst, @@ -218,6 +223,7 @@ static const struct link_hwss hpo_dp_link_hwss = { bool can_use_hpo_dp_link_hwss(const struct dc_link *link, const struct link_resource *link_res) { + (void)link; return link_res->hpo_dp_link_enc != NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.c index 55c5148de800..6d5b7450b205 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.c @@ -74,6 +74,7 @@ static void dp_hpo_fixed_vs_pe_retimer_set_tx_ffe(struct dc_link *link, static void dp_hpo_fixed_vs_pe_retimer_program_override_test_pattern(struct dc_link *link, struct encoder_set_dp_phy_pattern_param *tp_params) { + (void)tp_params; uint8_t clk_src = 0xC4; uint8_t pattern = 0x4F; /* SQ128 */ diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_virtual.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_virtual.c index 64742c24f7e6..3aa1375cec71 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_virtual.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_virtual.c @@ -27,20 +27,26 @@ void virtual_setup_stream_encoder(struct pipe_ctx *pipe_ctx) { + (void)pipe_ctx; } void virtual_setup_stream_attribute(struct pipe_ctx *pipe_ctx) { + (void)pipe_ctx; } void virtual_reset_stream_encoder(struct pipe_ctx *pipe_ctx) { + (void)pipe_ctx; } static void virtual_disable_link_output(struct dc_link *link, const struct link_resource *link_res, enum signal_type signal) { + (void)link; + (void)link_res; + (void)signal; } static const struct link_hwss virtual_link_hwss = { diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index b761f330311f..59851924bfcd 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -823,6 +823,7 @@ static void verify_link_capability_non_destructive(struct dc_link *link) static bool should_verify_link_capability_destructively(struct dc_link *link, enum dc_detect_reason reason) { + (void)reason; bool destrictive = false; struct dc_link_settings max_link_cap; bool is_link_enc_unavailable = false; diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.c b/drivers/gpu/drm/amd/display/dc/link/link_validation.c index acdc162de535..eb791285ed06 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_validation.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.c @@ -391,6 +391,7 @@ static const struct dc_tunnel_settings *get_dp_tunnel_settings(const struct dc_s */ enum dc_status link_validate_dp_tunnel_bandwidth(const struct dc *dc, const struct dc_state *new_ctx) { + (void)dc; struct dc_validation_dpia_set dpia_link_sets[MAX_DPIA_NUM] = { 0 }; uint8_t link_count = 0; enum dc_status result = DC_OK; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c index 08e2b572e0ff..ddff0db4ce70 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c @@ -554,6 +554,7 @@ enum link_training_result dp_check_link_loss_status( struct dc_link *link, const struct link_training_settings *link_training_setting) { + (void)link_training_setting; enum link_training_result status = LINK_TRAINING_SUCCESS; union lane_status lane_status; union lane_align_status_updated dpcd_lane_status_updated; @@ -1387,6 +1388,7 @@ bool dp_set_hw_training_pattern( enum dc_dp_training_pattern pattern, uint32_t offset) { + (void)offset; enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; switch (pattern) { diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c index 11565f187ac7..1a6bfc45927d 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c @@ -158,6 +158,7 @@ static enum link_training_result dp_perform_128b_132b_cds_done_sequence( const struct link_resource *link_res, struct link_training_settings *lt_settings) { + (void)link_res; /* Assumption: assume hardware has transmitted eq pattern */ enum dc_status status = DC_OK; enum link_training_result result = LINK_TRAINING_SUCCESS; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c index 603537ffd128..34fa76d97b83 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c @@ -172,6 +172,7 @@ static uint8_t dpia_build_set_config_data( struct dc_link *link, struct link_training_settings *lt_settings) { + (void)link; union dpia_set_config_data data; data.raw = 0; @@ -290,6 +291,7 @@ static enum link_training_result dpia_training_cr_non_transparent( struct link_training_settings *lt_settings, uint32_t hop) { + (void)link_res; enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0; uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ enum dc_status status = DC_ERROR_UNEXPECTED; @@ -457,6 +459,7 @@ static enum link_training_result dpia_training_cr_transparent( const struct link_resource *link_res, struct link_training_settings *lt_settings) { + (void)link_res; enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0; enum dc_status status; uint32_t retries_cr = 0; /* Number of consecutive attempts with same VS or PE. */ @@ -585,6 +588,7 @@ static enum link_training_result dpia_training_eq_non_transparent( struct link_training_settings *lt_settings, uint32_t hop) { + (void)link_res; enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ; uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ uint32_t retries_eq = 0; @@ -730,6 +734,7 @@ static enum link_training_result dpia_training_eq_transparent( const struct link_resource *link_res, struct link_training_settings *lt_settings) { + (void)link_res; enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ; uint32_t retries_eq = 0; enum dc_status status; @@ -991,6 +996,7 @@ enum link_training_result dpia_perform_link_training( const struct dc_link_settings *link_setting, bool skip_video_pattern) { + (void)skip_video_pattern; enum link_training_result result; struct link_training_settings lt_settings = {0}; uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c index 584b9295a12a..e4c2aa2bc364 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c @@ -180,6 +180,7 @@ static void dpcd_reduce_address_range( uint8_t * const reduced_data, const uint32_t reduced_size) { + (void)extended_size; const uint32_t offset = reduced_address - extended_address; /* diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c index 8b398b9a2b6b..4c7bb0522a8c 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c @@ -117,6 +117,9 @@ void mpc3_set_out_rate_control( bool rate_2x_mode, struct mpc_dwb_flow_control *flow_control) { + (void)enable; + (void)rate_2x_mode; + (void)flow_control; struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc); /* Always disable mpc out rate and flow control. @@ -908,6 +911,7 @@ static void mpc3_set_3dlut_mode( bool is_lut_size17x17x17, uint32_t rmu_idx) { + (void)is_color_channel_12bits; uint32_t lut_mode; struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc); diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c index badcef027b84..1f15ada109b6 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c @@ -884,6 +884,7 @@ void mpc32_set_3dlut_mode( bool is_lut_size17x17x17, uint32_t mpcc_id) { + (void)is_color_channel_12bits; uint32_t lut_mode; struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc); diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c index 45d418636d0c..b49bd155cad4 100644 --- a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c +++ b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c @@ -250,6 +250,7 @@ void opp1_set_dyn_expansion( enum dc_color_depth color_dpth, enum signal_type signal) { + (void)color_sp; struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c index 6f7b0f816f2a..9e66b9b97c63 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c @@ -164,6 +164,7 @@ void optc1_program_timing( const enum signal_type signal, bool use_vbios) { + (void)use_vbios; struct dc_crtc_timing patched_crtc_timing; uint32_t asic_blank_end; uint32_t asic_blank_start; @@ -855,6 +856,8 @@ void optc1_set_early_control( struct timing_generator *optc, uint32_t early_cntl) { + (void)optc; + (void)early_cntl; /* asic design change, do not need this control * empty for share caller logic */ @@ -1249,6 +1252,7 @@ void optc1_get_crtc_scanoutpos( static void optc1_enable_stereo(struct timing_generator *optc, const struct dc_crtc_timing *timing, struct crtc_stereo_flags *flags) { + (void)timing; struct optc *optc1 = DCN10TG_FROM_TG(optc); if (flags) { diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c index e7a90a437fff..39ce4d4a61a1 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c @@ -181,6 +181,7 @@ void optc2_set_odm_bypass(struct timing_generator *optc, void optc2_set_odm_combine(struct timing_generator *optc, int *opp_id, int opp_cnt, int segment_width, int last_segment_width) { + (void)last_segment_width; struct optc *optc1 = DCN10TG_FROM_TG(optc); uint32_t memory_mask; @@ -261,6 +262,7 @@ static void optc2_align_vblanks( uint8_t master_clock_divider, uint8_t slave_clock_divider) { + (void)slave_clock_divider; /* accessing slave OTG registers */ struct optc *optc1 = DCN10TG_FROM_TG(optc_slave); diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c index ee4665aa49e9..d72574db1f07 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c @@ -218,6 +218,7 @@ void optc3_set_odm_bypass(struct timing_generator *optc, void optc3_set_odm_combine(struct timing_generator *optc, int *opp_id, int opp_cnt, int segment_width, int last_segment_width) { + (void)last_segment_width; struct optc *optc1 = DCN10TG_FROM_TG(optc); uint32_t memory_mask = 0; diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c index 893d2aff1f82..5f53f8747812 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c @@ -43,6 +43,7 @@ static void optc31_set_odm_combine(struct timing_generator *optc, int *opp_id, int opp_cnt, int segment_width, int last_segment_width) { + (void)last_segment_width; struct optc *optc1 = DCN10TG_FROM_TG(optc); uint32_t memory_mask = 0; int mem_count_per_opp = (segment_width + 2559) / 2560; diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c index 43ff957288b2..a7cf34937b2f 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c @@ -50,6 +50,7 @@ static void optc314_set_odm_combine(struct timing_generator *optc, int *opp_id, int opp_cnt, int segment_width, int last_segment_width) { + (void)last_segment_width; struct optc *optc1 = DCN10TG_FROM_TG(optc); uint32_t memory_mask = 0; int h_active = segment_width * opp_cnt; diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c index 3dcb0d0c931c..60e546b69a05 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c @@ -45,6 +45,7 @@ static void optc32_set_odm_combine(struct timing_generator *optc, int *opp_id, int opp_cnt, int segment_width, int last_segment_width) { + (void)last_segment_width; struct optc *optc1 = DCN10TG_FROM_TG(optc); uint32_t memory_mask = 0; int h_active = segment_width * opp_cnt; diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c index 5aafd0eedf66..a880e4a6d165 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c @@ -58,6 +58,7 @@ static void optc35_set_odm_combine(struct timing_generator *optc, int *opp_id, int opp_cnt, int segment_width, int last_segment_width) { + (void)last_segment_width; struct optc *optc1 = DCN10TG_FROM_TG(optc); uint32_t memory_mask = 0; int h_active = segment_width * opp_cnt; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c index d83a6bed2ee0..caafebe92129 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c @@ -632,6 +632,7 @@ static struct link_encoder *dce100_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dce110_link_encoder *enc110 = kzalloc_obj(struct dce110_link_encoder); int link_regs_id; @@ -849,6 +850,7 @@ static enum dc_status build_mapped_resource( struct dc_state *context, struct dc_stream_state *stream) { + (void)dc; struct pipe_ctx *pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, stream); if (!pipe_ctx) @@ -866,6 +868,7 @@ enum dc_status dce100_validate_bandwidth( struct dc_state *context, enum dc_validate_mode validate_mode) { + (void)validate_mode; int i; bool at_least_one_pipe = false; struct dc_stream_state *stream = NULL; @@ -926,6 +929,7 @@ enum dc_status dce100_validate_global( struct dc *dc, struct dc_state *context) { + (void)dc; if (!dce100_validate_surface_sets(context)) return DC_FAIL_SURFACE_VALIDATE; @@ -961,6 +965,7 @@ static void dce100_destroy_resource_pool(struct resource_pool **pool) enum dc_status dce100_validate_plane(const struct dc_plane_state *plane_state, struct dc_caps *caps) { + (void)caps; if (plane_state->format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) return DC_OK; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c index ab71f645c90e..f83acfe7a15e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c @@ -667,6 +667,7 @@ static struct link_encoder *dce110_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dce110_link_encoder *enc110 = kzalloc_obj(struct dce110_link_encoder); int link_regs_id; @@ -971,6 +972,7 @@ static enum dc_status dce110_validate_bandwidth( struct dc_state *context, enum dc_validate_mode validate_mode) { + (void)validate_mode; bool result = false; DC_LOG_BANDWIDTH_CALCS( @@ -1043,6 +1045,7 @@ static enum dc_status dce110_validate_bandwidth( static enum dc_status dce110_validate_plane(const struct dc_plane_state *plane_state, struct dc_caps *caps) { + (void)caps; if (((plane_state->dst_rect.width * 2) < plane_state->src_rect.width) || ((plane_state->dst_rect.height * 2) < plane_state->src_rect.height)) return DC_FAIL_SURFACE_VALIDATE; @@ -1099,6 +1102,7 @@ static enum dc_status dce110_validate_global( struct dc *dc, struct dc_state *context) { + (void)dc; if (!dce110_validate_surface_sets(context)) return DC_FAIL_SURFACE_VALIDATE; @@ -1130,6 +1134,7 @@ static struct pipe_ctx *dce110_acquire_underlay( const struct resource_pool *pool, const struct pipe_ctx *opp_head_pipe) { + (void)cur_ctx; struct dc_stream_state *stream = opp_head_pipe->stream; struct dc *dc = stream->ctx->dc; struct dce_hwseq *hws = dc->hwseq; @@ -1354,6 +1359,7 @@ static bool dce110_resource_construct( struct dce110_resource_pool *pool, struct hw_asic_id asic_id) { + (void)asic_id; unsigned int i; struct dc_context *ctx = dc->ctx; struct dc_bios *bp; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c index 85af37c9d922..458b14e4cb97 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c @@ -628,6 +628,7 @@ static struct link_encoder *dce112_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dce110_link_encoder *enc110 = kzalloc_obj(struct dce110_link_encoder); int link_regs_id; @@ -852,6 +853,7 @@ static struct clock_source *find_matching_pll( const struct resource_pool *pool, const struct dc_stream_state *const stream) { + (void)res_ctx; switch (stream->link->link_enc->transmitter) { case TRANSMITTER_UNIPHY_A: return pool->clock_sources[DCE112_CLK_SRC_PLL0]; @@ -875,6 +877,7 @@ static enum dc_status build_mapped_resource( struct dc_state *context, struct dc_stream_state *stream) { + (void)dc; struct pipe_ctx *pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, stream); if (!pipe_ctx) @@ -892,6 +895,7 @@ enum dc_status dce112_validate_bandwidth( struct dc_state *context, enum dc_validate_mode validate_mode) { + (void)validate_mode; bool result = false; DC_LOG_BANDWIDTH_CALCS( @@ -1037,6 +1041,7 @@ static enum dc_status dce112_validate_global( struct dc *dc, struct dc_state *context) { + (void)dc; if (!dce112_validate_surface_sets(context)) return DC_FAIL_SURFACE_VALIDATE; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c index 7d5c7dacaf05..56bbf9dc1691 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c @@ -712,6 +712,7 @@ static struct link_encoder *dce120_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dce110_link_encoder *enc110 = kzalloc_obj(struct dce110_link_encoder); int link_regs_id; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c index fb18312554c7..33be49b3c1b1 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c @@ -734,6 +734,7 @@ static struct link_encoder *dce80_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dce110_link_encoder *enc110 = kzalloc_obj(struct dce110_link_encoder); int link_regs_id; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c index cd4d703e1018..250c3975b9e9 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c @@ -736,6 +736,7 @@ static struct link_encoder *dcn10_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn10_link_encoder *enc10 = kzalloc_obj(struct dcn10_link_encoder); int link_regs_id; @@ -1049,6 +1050,7 @@ static enum dc_status build_mapped_resource( struct dc_state *context, struct dc_stream_state *stream) { + (void)dc; struct pipe_ctx *pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, stream); if (!pipe_ctx) @@ -1083,6 +1085,7 @@ static struct pipe_ctx *dcn10_acquire_free_pipe_for_layer( const struct resource_pool *pool, const struct pipe_ctx *opp_head_pipe) { + (void)cur_ctx; struct resource_context *res_ctx = &new_ctx->res_ctx; struct pipe_ctx *head_pipe = resource_get_otg_master_for_stream(res_ctx, opp_head_pipe->stream); struct pipe_ctx *idle_pipe = resource_find_free_secondary_pipe_legacy(res_ctx, pool, head_pipe); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c index 5ba67e3c2f8f..bd5c18ee35e7 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c @@ -916,6 +916,7 @@ struct link_encoder *dcn20_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); int link_regs_id; @@ -1310,6 +1311,7 @@ static enum dc_status build_pipe_hw_param(struct pipe_ctx *pipe_ctx) enum dc_status dcn20_build_mapped_resource(const struct dc *dc, struct dc_state *context, struct dc_stream_state *stream) { + (void)dc; enum dc_status status = DC_OK; struct pipe_ctx *pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, stream); @@ -1537,6 +1539,7 @@ void dcn20_split_stream_for_mpc( struct pipe_ctx *primary_pipe, struct pipe_ctx *secondary_pipe) { + (void)res_ctx; int pipe_idx = secondary_pipe->pipe_idx; struct pipe_ctx *sec_bot_pipe = secondary_pipe->bottom_pipe; @@ -1682,6 +1685,7 @@ struct pipe_ctx *dcn20_find_secondary_pipe(struct dc *dc, const struct resource_pool *pool, const struct pipe_ctx *primary_pipe) { + (void)pool; struct pipe_ctx *secondary_pipe = NULL; if (dc && primary_pipe) { @@ -2161,6 +2165,7 @@ struct pipe_ctx *dcn20_acquire_free_pipe_for_layer( const struct resource_pool *pool, const struct pipe_ctx *opp_head) { + (void)cur_ctx; struct resource_context *res_ctx = &new_ctx->res_ctx; struct pipe_ctx *otg_master = resource_get_otg_master_for_stream(res_ctx, opp_head->stream); struct pipe_ctx *sec_dpp_pipe = resource_find_free_secondary_pipe_legacy(res_ctx, pool, otg_master); @@ -2343,6 +2348,7 @@ static struct _vcs_dpi_ip_params_st *get_asic_rev_ip_params( static enum dml_project get_dml_project_version(uint32_t hw_internal_rev) { + (void)hw_internal_rev; return DML_PROJECT_NAVI10v2; } diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c index 3a5dc8ca1457..bd19168a3f77 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c @@ -1299,6 +1299,7 @@ static struct link_encoder *dcn21_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn21_link_encoder *enc21 = kzalloc_obj(struct dcn21_link_encoder); int link_regs_id; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 8468c0fe3737..5742effef7ae 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -924,6 +924,7 @@ static struct link_encoder *dcn30_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c index 0a110be2b9da..9773896e0801 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c @@ -880,6 +880,7 @@ static struct link_encoder *dcn301_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index 0b2fc8464ef7..d9f12a6f225f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -894,6 +894,7 @@ static struct link_encoder *dcn302_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); if (!enc20 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index a5000134cd97..f0c75db81b2c 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -839,6 +839,7 @@ static struct link_encoder *dcn303_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); if (!enc20 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 4920bc136282..afcc4dff6abc 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -1090,6 +1090,7 @@ static struct link_encoder *dcn31_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -2243,6 +2244,7 @@ enum dc_status dcn31_update_dc_state_for_encoder_switch(struct dc_link *link, struct pipe_ctx *pipes, struct audio_output *audio_output) { + (void)link_setting; struct dc_state *state = link->dc->current_state; int i; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index b74a167ae5f7..654b4e97807e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -1148,6 +1148,7 @@ static struct link_encoder *dcn31_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index d9818bc2dfdb..f424fd4d5a45 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -1089,6 +1089,7 @@ static struct link_encoder *dcn31_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index c20521d0dd1e..e0dc8aaaaaa1 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -1082,6 +1082,7 @@ static struct link_encoder *dcn31_link_encoder_create( struct dc_context *ctx, const struct encoder_init_data *enc_init_data) { + (void)ctx; struct dcn20_link_encoder *enc20 = kzalloc_obj(struct dcn20_link_encoder); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c index f5a4e97c40ce..4808c793590f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c @@ -41,6 +41,7 @@ uint32_t dcn32_helper_calculate_mall_bytes_for_cursor( struct pipe_ctx *pipe_ctx, bool ignore_cursor_buf) { + (void)dc; struct hubp *hubp = pipe_ctx->plane_res.hubp; uint32_t cursor_size = hubp->curs_attr.pitch * hubp->curs_attr.height; uint32_t cursor_mall_size_bytes = 0; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index b9532ebcced4..c0d37f00fed9 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -1698,6 +1698,7 @@ static struct dc_cap_funcs cap_funcs = { static void dcn42_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) { + (void)bw_params; dc_assert_fp_enabled(); if (dc->current_state && dc->current_state->bw_ctx.dml2) @@ -1774,6 +1775,8 @@ static unsigned int dcn42_get_max_hw_cursor_size(const struct dc *dc, struct dc_state *state, const struct dc_stream_state *stream) { + (void)state; + (void)stream; return dc->caps.max_cursor_size; } static struct resource_funcs dcn42_res_pool_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c index 1c04171b296c..146a6e47934b 100644 --- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c @@ -159,6 +159,7 @@ static void dcn42_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb *soc static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) { + (void)config; /* Individual modification can be overwritten even if it was obtained by a previous function. * Modifications are acquired in order of priority (lowest to highest). */ From c3f327a9a306444cd91b904d7fcdba9406017390 Mon Sep 17 00:00:00 2001 From: Gaghik Khachatrian Date: Sat, 7 Mar 2026 18:37:04 -0500 Subject: [PATCH 469/712] drm/amd/display: Fix Compiler warnings in dmub [Why] Resolve compiler warnings by marking unused parameters explicitly. [How] In .c and .h files, keep parameter names in signatures and add a line with`(void)param;` inside the function body Preserved function signatures and avoids breaking code paths that may reference the parameter under conditional compilation. Reviewed-by: Aric Cyr Signed-off-by: Gaghik Khachatrian Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c | 2 ++ drivers/gpu/drm/amd/display/dmub/src/dmub_dcn30.c | 1 + drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c | 2 ++ drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c | 2 ++ drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c | 1 + drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c | 1 + drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c | 1 + drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c | 1 + 8 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c index 73221ca53b7d..0b152926f75b 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c @@ -194,6 +194,7 @@ void dmub_dcn20_setup_windows(struct dmub_srv *dmub, const struct dmub_window *cw6, const struct dmub_window *region6) { + (void)region6; union dmub_addr offset; uint64_t fb_base, fb_offset; @@ -396,6 +397,7 @@ union dmub_fw_boot_status dmub_dcn20_get_fw_boot_status(struct dmub_srv *dmub) void dmub_dcn20_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmub_srv_hw_params *params) { + (void)params; union dmub_fw_boot_options boot_options = {0}; REG_WRITE(DMCUB_SCRATCH14, boot_options.all); diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn30.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn30.c index 84a6eb3f677d..23a33db07edc 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn30.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn30.c @@ -127,6 +127,7 @@ void dmub_dcn30_setup_windows(struct dmub_srv *dmub, const struct dmub_window *cw6, const struct dmub_window *region6) { + (void)region6; union dmub_addr offset; /* sienna_cichlid has hardwired virtual addressing for CW2-CW7 */ diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c index 244244f3df80..478d79a6e246 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c @@ -195,6 +195,8 @@ void dmub_dcn31_setup_windows(struct dmub_srv *dmub, const struct dmub_window *cw6, const struct dmub_window *region6) { + (void)cw2; + (void)region6; union dmub_addr offset; offset = cw3->offset; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c index 5d86f649db4b..3f9fb9e05b79 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c @@ -237,6 +237,8 @@ void dmub_dcn32_setup_windows(struct dmub_srv *dmub, const struct dmub_window *cw6, const struct dmub_window *region6) { + (void)cw2; + (void)region6; union dmub_addr offset; offset = cw3->offset; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c index f9b16eb8ef8e..69fb6084232e 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c @@ -222,6 +222,7 @@ void dmub_dcn35_setup_windows(struct dmub_srv *dmub, const struct dmub_window *cw6, const struct dmub_window *region6) { + (void)cw2; union dmub_addr offset; offset = cw3->offset; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c index 3d2307d0ce49..e5a78df80d72 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c @@ -213,6 +213,7 @@ void dmub_dcn401_setup_windows(struct dmub_srv *dmub, const struct dmub_window *cw6, const struct dmub_window *region6) { + (void)cw2; union dmub_addr offset; offset = cw3->offset; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c index 7b870b831199..a09aa19ad379 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn42.c @@ -229,6 +229,7 @@ void dmub_dcn42_setup_windows(struct dmub_srv *dmub, const struct dmub_window *cw6, const struct dmub_window *region6) { + (void)cw2; union dmub_addr offset; offset = cw3->offset; diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c index 94f4931d3d44..b5566ef8d4f3 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c @@ -45,6 +45,7 @@ static void set_reg_field_values(struct dmub_reg_value_masks *field_value_mask, uint32_t mask1, uint32_t field_value1, va_list ap) { + (void)addr; uint32_t shift, mask, field_value; int i = 1; From 3722df98c2e724121c40ea7ad59988262dbdb98f Mon Sep 17 00:00:00 2001 From: Gaghik Khachatrian Date: Mon, 16 Mar 2026 11:34:49 -0400 Subject: [PATCH 470/712] drm/amd/display: Silence type conversion warnings in dml2 [Why] Compiler build generates type conversion warnings throughout dc/dml2_0 where values are implicitly narrowed (e.g. int/uint32_t/uint64_t assigned to uint8_t, unsigned char, char, bool, or dml_bool_t), cluttering build output and masking genuine issues. [How] Add explicit casts at each narrowing assignment with ASSERT guards to catch out-of-range values in debug builds: - uint8_t: otg_inst, num_planes, pipe_idx, vblank_index fields - unsigned char: pipe_dlg_param.otg_inst from tg->inst - char: mcache num_pipes from num_dpps_required - bool/dml_bool_t: INTERLACE bitfield and fams2 enable flag use != 0 - uint64_t: widen min_hardware_refresh_in_uhz to hold div64_u64 result, then cast to unsigned long for min_refresh_uhz with ASSERT Reviewed-by: Austin Zheng Signed-off-by: Gaghik Khachatrian Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- .../dc/dml2_0/dml21/dml21_translation_helper.c | 13 +++++++++---- .../amd/display/dc/dml2_0/dml21/dml21_utils.c | 15 +++++++++++---- .../dc/dml2_0/dml21/dml21_wrapper_fpu.c | 6 ++++-- .../display/dc/dml2_0/dml2_dc_resource_mgmt.c | 18 ++++++++++++------ .../amd/display/dc/dml2_0/dml2_mall_phantom.c | 3 ++- .../dc/dml2_0/dml2_translation_helper.c | 2 +- .../gpu/drm/amd/display/dc/dml2_0/dml2_utils.c | 3 ++- 7 files changed, 41 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c index eadf84842ca0..2f0e0048bea8 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c @@ -90,7 +90,8 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf struct pipe_ctx *pipe_ctx, struct dml2_context *dml_ctx) { - unsigned int hblank_start, vblank_start, min_hardware_refresh_in_uhz; + unsigned int hblank_start, vblank_start; + uint64_t min_hardware_refresh_in_uhz; uint32_t pix_clk_100hz; timing->h_active = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right + pipe_ctx->dsc_padding_params.dsc_hactive_padding; @@ -105,7 +106,7 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf timing->h_total = stream->timing.h_total + pipe_ctx->dsc_padding_params.dsc_htotal_padding; timing->v_total = stream->timing.v_total; timing->h_sync_width = stream->timing.h_sync_width; - timing->interlaced = stream->timing.flags.INTERLACE; + timing->interlaced = (stream->timing.flags.INTERLACE != 0); hblank_start = stream->timing.h_total - stream->timing.h_front_porch; @@ -137,7 +138,11 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf (timing->h_total * (long long)calc_max_hardware_v_total(stream))); } - timing->drr_config.min_refresh_uhz = max(stream->timing.min_refresh_in_uhz, min_hardware_refresh_in_uhz); + { + uint64_t min_refresh = max((uint64_t)stream->timing.min_refresh_in_uhz, min_hardware_refresh_in_uhz); + ASSERT(min_refresh <= ULONG_MAX); + timing->drr_config.min_refresh_uhz = (unsigned long)min_refresh; + } if (dml_ctx->config.callbacks.get_max_flickerless_instant_vtotal_increase && stream->ctx->dc->config.enable_fpo_flicker_detection == 1) @@ -697,7 +702,7 @@ unsigned int map_plane_to_dml21_display_cfg(const struct dml2_context *dml_ctx, if (!dml21_wrapper_get_plane_id(context, stream_id, plane, &plane_id)) { ASSERT(false); - return -1; + return UINT_MAX; } for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c index ab7ec24268be..4724b08c77e1 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c @@ -420,8 +420,12 @@ static unsigned int dml21_build_fams2_stream_programming_v2(const struct dc *dc, type = static_base_state->stream_v1.base.type; /* get information from context */ - static_base_state->stream_v1.base.num_planes = context->stream_status[dc_stream_idx].plane_count; - static_base_state->stream_v1.base.otg_inst = context->stream_status[dc_stream_idx].primary_otg_inst; + ASSERT(context->stream_status[dc_stream_idx].plane_count >= 0 && + context->stream_status[dc_stream_idx].plane_count <= 0xFF); + ASSERT(context->stream_status[dc_stream_idx].primary_otg_inst >= 0 && + context->stream_status[dc_stream_idx].primary_otg_inst <= 0xFF); + static_base_state->stream_v1.base.num_planes = (uint8_t)context->stream_status[dc_stream_idx].plane_count; + static_base_state->stream_v1.base.otg_inst = (uint8_t)context->stream_status[dc_stream_idx].primary_otg_inst; /* populate pipe masks for planes */ for (dc_plane_idx = 0; dc_plane_idx < context->stream_status[dc_stream_idx].plane_count; dc_plane_idx++) { @@ -458,7 +462,9 @@ static unsigned int dml21_build_fams2_stream_programming_v2(const struct dc *dc, switch (dc->debug.fams_version.minor) { case 1: default: - static_sub_state->stream_v1.sub_state.subvp.phantom_otg_inst = phantom_status->primary_otg_inst; + ASSERT(phantom_status->primary_otg_inst >= 0 && + phantom_status->primary_otg_inst <= 0xFF); + static_sub_state->stream_v1.sub_state.subvp.phantom_otg_inst = (uint8_t)phantom_status->primary_otg_inst; /* populate pipe masks for phantom planes */ for (dc_plane_idx = 0; dc_plane_idx < phantom_status->plane_count; dc_plane_idx++) { @@ -516,7 +522,8 @@ void dml21_build_fams2_programming(const struct dc *dc, context->bw_ctx.bw.dcn.fams2_global_config.num_streams = num_fams2_streams; } - context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = context->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable; + context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = + (context->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable != 0); } bool dml21_is_plane1_enabled(enum dml2_source_format_class source_format) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c index f3abfdbe6805..cc992af6ac9c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c @@ -297,7 +297,8 @@ void dml21_prepare_mcache_programming(struct dc *in_dc, struct dc_state *context memset(mcache_config, 0, sizeof(struct dml2_plane_mcache_configuration_descriptor)); mcache_config->plane_descriptor = pln_prog->plane_descriptor; mcache_config->mcache_allocation = &context->bw_ctx.bw.dcn.mcache_allocations[dml_prog_idx]; - mcache_config->num_pipes = pln_prog->num_dpps_required; + ASSERT(pln_prog->num_dpps_required <= 0x7F); + mcache_config->num_pipes = (char)pln_prog->num_dpps_required; l->build_mcache_programming_params.num_configurations++; if (pln_prog->num_dpps_required == 0) { @@ -324,7 +325,8 @@ void dml21_prepare_mcache_programming(struct dc *in_dc, struct dc_state *context memset(mcache_config, 0, sizeof(struct dml2_plane_mcache_configuration_descriptor)); mcache_config->plane_descriptor = pln_prog->plane_descriptor; mcache_config->mcache_allocation = &context->bw_ctx.bw.dcn.mcache_allocations[dml_phantom_prog_idx]; - mcache_config->num_pipes = pln_prog->num_dpps_required; + ASSERT(pln_prog->num_dpps_required <= 0x7F); + mcache_config->num_pipes = (char)pln_prog->num_dpps_required; l->build_mcache_programming_params.num_configurations++; for (dc_pipe_index = 0; dc_pipe_index < num_pipes; dc_pipe_index++) { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c index 40f2f1ebab3a..6ef93c6fc1cd 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c @@ -366,7 +366,8 @@ static bool find_more_pipes_for_stream(struct dml2_context *ctx, if (!is_plane_using_pipe(pipe)) { pipes_needed--; // TODO: This doens't make sense really, pipe_idx should always be valid - pipe->pipe_idx = preferred_pipe_candidates[i]; + ASSERT(preferred_pipe_candidates[i] <= 0xFF); + pipe->pipe_idx = (uint8_t)preferred_pipe_candidates[i]; assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; } } @@ -382,7 +383,8 @@ static bool find_more_pipes_for_stream(struct dml2_context *ctx, if (!is_plane_using_pipe(pipe)) { pipes_needed--; // TODO: This doens't make sense really, pipe_idx should always be valid - pipe->pipe_idx = i; + ASSERT(i >= 0 && i <= 0xFF); + pipe->pipe_idx = (uint8_t)i; assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; } } @@ -393,7 +395,8 @@ static bool find_more_pipes_for_stream(struct dml2_context *ctx, if (!is_plane_using_pipe(pipe)) { pipes_needed--; // TODO: This doens't make sense really, pipe_idx should always be valid - pipe->pipe_idx = last_resort_pipe_candidates[i]; + ASSERT(last_resort_pipe_candidates[i] <= 0xFF); + pipe->pipe_idx = (uint8_t)last_resort_pipe_candidates[i]; assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; } } @@ -432,7 +435,8 @@ static bool find_more_free_pipes(struct dml2_context *ctx, if (is_pipe_free(pipe)) { pipes_needed--; // TODO: This doens't make sense really, pipe_idx should always be valid - pipe->pipe_idx = preferred_pipe_candidates[i]; + ASSERT(preferred_pipe_candidates[i] <= 0xFF); + pipe->pipe_idx = (uint8_t)preferred_pipe_candidates[i]; assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; } } @@ -448,7 +452,8 @@ static bool find_more_free_pipes(struct dml2_context *ctx, if (is_pipe_free(pipe)) { pipes_needed--; // TODO: This doens't make sense really, pipe_idx should always be valid - pipe->pipe_idx = i; + ASSERT(i >= 0 && i <= 0xFF); + pipe->pipe_idx = (uint8_t)i; assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; } } @@ -459,7 +464,8 @@ static bool find_more_free_pipes(struct dml2_context *ctx, if (is_pipe_free(pipe)) { pipes_needed--; // TODO: This doens't make sense really, pipe_idx should always be valid - pipe->pipe_idx = last_resort_pipe_candidates[i]; + ASSERT(last_resort_pipe_candidates[i] <= 0xFF); + pipe->pipe_idx = (uint8_t)last_resort_pipe_candidates[i]; assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx; } } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c index d56e58ce26c7..9bbe4e058be7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c @@ -555,7 +555,8 @@ static bool subvp_vblank_schedulable(struct dml2_context *ctx, struct dc_state * if (!found && pipe_mall_type == SUBVP_NONE) { // Found pipe which is not SubVP or Phantom (i.e. the VBLANK pipe). - vblank_index = i; + ASSERT(i <= 0xFF); + vblank_index = (uint8_t)i; found = true; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c index 57f45b27de1d..cf3a69aba638 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c @@ -765,7 +765,7 @@ static void populate_dml_timing_cfg_from_stream_state(struct dml_timing_cfg_st * out->PixelClock[location] *= 2; out->HTotal[location] = in->timing.h_total; out->VTotal[location] = in->timing.v_total; - out->Interlace[location] = in->timing.flags.INTERLACE; + out->Interlace[location] = (in->timing.flags.INTERLACE != 0); hblank_start = in->timing.h_total - in->timing.h_front_porch; out->HBlankEnd[location] = hblank_start - in->timing.h_addressable diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c index 9a33158b63bf..6c7cdf102906 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c @@ -255,7 +255,8 @@ static void populate_pipe_ctx_dlg_params_from_dml(struct pipe_ctx *pipe_ctx, str pipe_ctx->pipe_dlg_param.vupdate_width = dml_get_vupdate_width(mode_lib, pipe_idx); pipe_ctx->pipe_dlg_param.vready_offset = dml_get_vready_offset(mode_lib, pipe_idx); - pipe_ctx->pipe_dlg_param.otg_inst = pipe_ctx->stream_res.tg->inst; + ASSERT(pipe_ctx->stream_res.tg->inst >= 0 && pipe_ctx->stream_res.tg->inst <= 0xFF); + pipe_ctx->pipe_dlg_param.otg_inst = (unsigned char)pipe_ctx->stream_res.tg->inst; pipe_ctx->pipe_dlg_param.hactive = hactive; pipe_ctx->pipe_dlg_param.vactive = vactive; From 1b0f1459ab0b5816a2974b1ad33746bdfb333352 Mon Sep 17 00:00:00 2001 From: Taimur Hassan Date: Fri, 20 Mar 2026 16:24:47 -0400 Subject: [PATCH 471/712] drm/amd/display: [FW Promotion] Release 0.1.53.0 [Why] dmu: Parse freesync mccs vcp code Acked-by: Wayne Lin Signed-off-by: Taimur Hassan Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h index 8fbd179a4c87..fe9431cea3e5 100644 --- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h +++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h @@ -6272,6 +6272,7 @@ struct dmub_cmd_edid_cea_amd_vsdb { uint16_t amd_vsdb_version; /**< AMD VSDB version */ uint16_t min_frame_rate; /**< Maximum frame rate */ uint16_t max_frame_rate; /**< Minimum frame rate */ + uint8_t freesync_mccs_vcp_code; /**< Freesync MCCS VCP code */ }; /** From c842980a2126f1e2a856aa0db8b090dd011cca74 Mon Sep 17 00:00:00 2001 From: Taimur Hassan Date: Fri, 20 Mar 2026 17:38:34 -0500 Subject: [PATCH 472/712] drm/amd/display: Promote DC to 3.2.376 This version brings along following fixes: - correct unknown plane state patch - Revert "Refactor DC update checks" - Revert "Add 3DLUT DMA broadcast support" - Remove invalid DPSTREAMCLK mask usage - enable eDP DSC seamless boot support - Revert "Rework HDMI link training and YCbCr422 with DSC policy" - Disable PSR & Replay CRTC disable by default - Fix Silence Compiler Warnings - Add link output control for DPIA - eliminate clock manager code duplication - Don't set 4to1MPC config dynamically - Merge pipes for validate - Fix bounds checking in dml2_0 clock table array - Avoid turning off the PHY when OTG is running for DVI - Should support p-state under dcn21 - Enable Replay support for dcn42 - Remove check for DC_DMCUB_ENABLE on DCN42 Acked-by: Wayne Lin Signed-off-by: Taimur Hassan Signed-off-by: Chuanyu Tseng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index afc06dfc161f..55ec281db3b7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -63,7 +63,7 @@ struct dcn_dsc_reg_state; struct dcn_optc_reg_state; struct dcn_dccg_reg_state; -#define DC_VER "3.2.375" +#define DC_VER "3.2.376" /** * MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC From 6b0a6116286ef25f7916e7affed9de246be80a81 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Fri, 20 Mar 2026 11:01:02 +0530 Subject: [PATCH 473/712] drm/amd/pm: Unify version check in SMUv11 Use common helper function for firmware version check and logging in SMUv11 Signed-off-by: Lijo Lazar Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h | 14 ---- .../gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c | 5 +- .../amd/pm/swsmu/smu11/cyan_skillfish_ppt.c | 3 +- .../gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c | 21 +++++- .../amd/pm/swsmu/smu11/sienna_cichlid_ppt.c | 25 ++++++- .../gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c | 75 ------------------- .../gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c | 3 +- 7 files changed, 52 insertions(+), 94 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h index 7c1701ed3e11..dd94e8a9e218 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h @@ -25,18 +25,6 @@ #include "amdgpu_smu.h" -#define SMU11_DRIVER_IF_VERSION_INV 0xFFFFFFFF -#define SMU11_DRIVER_IF_VERSION_ARCT 0x17 -#define SMU11_DRIVER_IF_VERSION_NV10 0x37 -#define SMU11_DRIVER_IF_VERSION_NV12 0x38 -#define SMU11_DRIVER_IF_VERSION_NV14 0x38 -#define SMU11_DRIVER_IF_VERSION_Sienna_Cichlid 0x40 -#define SMU11_DRIVER_IF_VERSION_Navy_Flounder 0xE -#define SMU11_DRIVER_IF_VERSION_VANGOGH 0x03 -#define SMU11_DRIVER_IF_VERSION_Dimgrey_Cavefish 0xF -#define SMU11_DRIVER_IF_VERSION_Beige_Goby 0xD -#define SMU11_DRIVER_IF_VERSION_Cyan_Skillfish 0x8 - /* MP Apertures */ #define MP0_Public 0x03800000 #define MP0_SRAM 0x03900000 @@ -148,8 +136,6 @@ int smu_v11_0_setup_pptable(struct smu_context *smu); int smu_v11_0_get_vbios_bootup_values(struct smu_context *smu); -int smu_v11_0_check_fw_version(struct smu_context *smu); - int smu_v11_0_set_driver_table_location(struct smu_context *smu); int smu_v11_0_set_tool_table_location(struct smu_context *smu); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index 74c818e3fbd0..54d3dba7d354 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -65,6 +65,8 @@ #define SMU_FEATURES_HIGH_MASK 0xFFFFFFFF00000000 #define SMU_FEATURES_HIGH_SHIFT 32 +#define SMU11_DRIVER_IF_VERSION_ARCT 0x17 + static const struct smu_feature_bits arcturus_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_DPM_PREFETCHER_BIT), SMU_FEATURE_BIT_INIT(FEATURE_DPM_GFXCLK_BIT), @@ -1905,7 +1907,7 @@ static const struct pptable_funcs arcturus_ppt_funcs = { /* pptable related */ .setup_pptable = arcturus_setup_pptable, .get_vbios_bootup_values = smu_v11_0_get_vbios_bootup_values, - .check_fw_version = smu_v11_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .write_pptable = smu_cmn_write_pptable, .set_driver_table_location = smu_v11_0_set_driver_table_location, .set_tool_table_location = smu_v11_0_set_tool_table_location, @@ -1958,5 +1960,6 @@ void arcturus_set_ppt_funcs(struct smu_context *smu) smu->table_map = arcturus_table_map; smu->pwr_src_map = arcturus_pwr_src_map; smu->workload_map = arcturus_workload_map; + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_ARCT; smu_v11_0_init_msg_ctl(smu, arcturus_message_map); } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c index 4e70308a455e..e6e009df9840 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c @@ -582,7 +582,7 @@ cyan_skillfish_get_enabled_mask(struct smu_context *smu, static const struct pptable_funcs cyan_skillfish_ppt_funcs = { .check_fw_status = smu_v11_0_check_fw_status, - .check_fw_version = smu_v11_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .init_power = smu_v11_0_init_power, .fini_power = smu_v11_0_fini_power, .init_smc_tables = cyan_skillfish_init_smc_tables, @@ -605,5 +605,6 @@ void cyan_skillfish_set_ppt_funcs(struct smu_context *smu) smu->ppt_funcs = &cyan_skillfish_ppt_funcs; smu->table_map = cyan_skillfish_table_map; smu->is_apu = true; + smu->smc_driver_if_version = MP1_DRIVER_IF_VERSION; smu_v11_0_init_msg_ctl(smu, cyan_skillfish_message_map); } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index 163e09ca0730..cd0457e13f54 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -73,6 +73,10 @@ static const struct smu_feature_bits navi10_dpm_features = { #define SMU_11_0_GFX_BUSY_THRESHOLD 15 +#define SMU11_DRIVER_IF_VERSION_NV10 0x37 +#define SMU11_DRIVER_IF_VERSION_NV12 0x38 +#define SMU11_DRIVER_IF_VERSION_NV14 0x38 + static struct cmn2asic_msg_mapping navi10_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1), MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1), @@ -3308,7 +3312,7 @@ static const struct pptable_funcs navi10_ppt_funcs = { .check_fw_status = smu_v11_0_check_fw_status, .setup_pptable = navi10_setup_pptable, .get_vbios_bootup_values = smu_v11_0_get_vbios_bootup_values, - .check_fw_version = smu_v11_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .write_pptable = smu_cmn_write_pptable, .set_driver_table_location = smu_v11_0_set_driver_table_location, .set_tool_table_location = smu_v11_0_set_tool_table_location, @@ -3361,11 +3365,26 @@ static const struct pptable_funcs navi10_ppt_funcs = { void navi10_set_ppt_funcs(struct smu_context *smu) { + struct amdgpu_device *adev = smu->adev; + smu->ppt_funcs = &navi10_ppt_funcs; smu->clock_map = navi10_clk_map; smu->feature_map = navi10_feature_mask_map; smu->table_map = navi10_table_map; smu->pwr_src_map = navi10_pwr_src_map; smu->workload_map = navi10_workload_map; + + switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { + case IP_VERSION(11, 0, 0): + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV10; + break; + case IP_VERSION(11, 0, 9): + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV12; + break; + case IP_VERSION(11, 0, 5): + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV14; + break; + } + smu_v11_0_init_msg_ctl(smu, navi10_message_map); } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index cf030af18aad..f799e489b481 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -3119,7 +3119,7 @@ static const struct pptable_funcs sienna_cichlid_ppt_funcs = { .check_fw_status = smu_v11_0_check_fw_status, .setup_pptable = sienna_cichlid_setup_pptable, .get_vbios_bootup_values = smu_v11_0_get_vbios_bootup_values, - .check_fw_version = smu_v11_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .write_pptable = smu_cmn_write_pptable, .set_driver_table_location = smu_v11_0_set_driver_table_location, .set_tool_table_location = smu_v11_0_set_tool_table_location, @@ -3176,13 +3176,36 @@ static const struct pptable_funcs sienna_cichlid_ppt_funcs = { .mode2_reset = sienna_cichlid_mode2_reset, }; +#define SMU11_DRIVER_IF_VERSION_Sienna_Cichlid 0x40 +#define SMU11_DRIVER_IF_VERSION_Navy_Flounder 0xE +#define SMU11_DRIVER_IF_VERSION_Dimgrey_Cavefish 0xF +#define SMU11_DRIVER_IF_VERSION_Beige_Goby 0xD + void sienna_cichlid_set_ppt_funcs(struct smu_context *smu) { + struct amdgpu_device *adev = smu->adev; + smu->ppt_funcs = &sienna_cichlid_ppt_funcs; smu->clock_map = sienna_cichlid_clk_map; smu->feature_map = sienna_cichlid_feature_mask_map; smu->table_map = sienna_cichlid_table_map; smu->pwr_src_map = sienna_cichlid_pwr_src_map; smu->workload_map = sienna_cichlid_workload_map; + + switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { + case IP_VERSION(11, 0, 7): + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Sienna_Cichlid; + break; + case IP_VERSION(11, 0, 11): + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Navy_Flounder; + break; + case IP_VERSION(11, 0, 12): + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Dimgrey_Cavefish; + break; + case IP_VERSION(11, 0, 13): + smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Beige_Goby; + break; + } + smu_v11_0_init_msg_ctl(smu, sienna_cichlid_message_map); } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index 7ca8fdd23206..d68ceee16d8f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -192,81 +192,6 @@ int smu_v11_0_check_fw_status(struct smu_context *smu) return -EIO; } -int smu_v11_0_check_fw_version(struct smu_context *smu) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t if_version = 0xff, smu_version = 0xff; - uint8_t smu_program, smu_major, smu_minor, smu_debug; - int ret = 0; - - ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version); - if (ret) - return ret; - - smu_program = (smu_version >> 24) & 0xff; - smu_major = (smu_version >> 16) & 0xff; - smu_minor = (smu_version >> 8) & 0xff; - smu_debug = (smu_version >> 0) & 0xff; - if (smu->is_apu) - adev->pm.fw_version = smu_version; - - switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { - case IP_VERSION(11, 0, 0): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV10; - break; - case IP_VERSION(11, 0, 9): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV12; - break; - case IP_VERSION(11, 0, 5): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV14; - break; - case IP_VERSION(11, 0, 7): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Sienna_Cichlid; - break; - case IP_VERSION(11, 0, 11): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Navy_Flounder; - break; - case IP_VERSION(11, 5, 0): - case IP_VERSION(11, 5, 2): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_VANGOGH; - break; - case IP_VERSION(11, 0, 12): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Dimgrey_Cavefish; - break; - case IP_VERSION(11, 0, 13): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Beige_Goby; - break; - case IP_VERSION(11, 0, 8): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Cyan_Skillfish; - break; - case IP_VERSION(11, 0, 2): - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_ARCT; - break; - default: - dev_err(smu->adev->dev, "smu unsupported IP version: 0x%x.\n", - amdgpu_ip_version(adev, MP1_HWIP, 0)); - smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_INV; - break; - } - - /* - * 1. if_version mismatch is not critical as our fw is designed - * to be backward compatible. - * 2. New fw usually brings some optimizations. But that's visible - * only on the paired driver. - * Considering above, we just leave user a verbal message instead - * of halt driver loading. - */ - if (if_version != smu->smc_driver_if_version) { - dev_info(smu->adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, " - "smu fw program = %d, version = 0x%08x (%d.%d.%d)\n", - smu->smc_driver_if_version, if_version, - smu_program, smu_version, smu_major, smu_minor, smu_debug); - } - - return ret; -} - static int smu_v11_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index 5eabaf55dfc5..d269b505aefb 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -2511,7 +2511,7 @@ static u32 vangogh_get_gfxoff_entrycount(struct smu_context *smu, uint64_t *entr static const struct pptable_funcs vangogh_ppt_funcs = { .check_fw_status = smu_v11_0_check_fw_status, - .check_fw_version = smu_v11_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .init_smc_tables = vangogh_init_smc_tables, .fini_smc_tables = smu_v11_0_fini_smc_tables, .init_power = smu_v11_0_init_power, @@ -2561,5 +2561,6 @@ void vangogh_set_ppt_funcs(struct smu_context *smu) smu->table_map = vangogh_table_map; smu->workload_map = vangogh_workload_map; smu->is_apu = true; + smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION; smu_v11_0_init_msg_ctl(smu, vangogh_message_map); } From 353f200825051920ac81dcba2d4d5ccd1501acad Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Fri, 20 Mar 2026 11:06:40 +0530 Subject: [PATCH 474/712] drm/amd/pm: Unify version check in SMUv12 Use common helper function for firmware version check and logging in SMUv12. Signed-off-by: Lijo Lazar Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/inc/smu_v12_0.h | 2 -- .../gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c | 2 +- .../gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c | 36 ------------------- 3 files changed, 1 insertion(+), 39 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v12_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v12_0.h index fd3937b08662..2346d9c6e162 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v12_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v12_0.h @@ -35,8 +35,6 @@ int smu_v12_0_check_fw_status(struct smu_context *smu); -int smu_v12_0_check_fw_version(struct smu_context *smu); - int smu_v12_0_powergate_sdma(struct smu_context *smu, bool gate); int smu_v12_0_powergate_vcn(struct smu_context *smu, bool gate); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index 186020ed6708..75335da224c7 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -1457,7 +1457,7 @@ static const struct pptable_funcs renoir_ppt_funcs = { .get_power_profile_mode = renoir_get_power_profile_mode, .read_sensor = renoir_read_sensor, .check_fw_status = smu_v12_0_check_fw_status, - .check_fw_version = smu_v12_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .powergate_sdma = smu_v12_0_powergate_sdma, .set_gfx_cgpg = smu_v12_0_set_gfx_cgpg, .gfx_off_control = smu_v12_0_gfx_off_control, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c index ac5e44dff6c9..f09da4d14510 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c @@ -70,42 +70,6 @@ int smu_v12_0_check_fw_status(struct smu_context *smu) return -EIO; } -int smu_v12_0_check_fw_version(struct smu_context *smu) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t if_version = 0xff, smu_version = 0xff; - uint8_t smu_program, smu_major, smu_minor, smu_debug; - int ret = 0; - - ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version); - if (ret) - return ret; - - smu_program = (smu_version >> 24) & 0xff; - smu_major = (smu_version >> 16) & 0xff; - smu_minor = (smu_version >> 8) & 0xff; - smu_debug = (smu_version >> 0) & 0xff; - if (smu->is_apu) - adev->pm.fw_version = smu_version; - - /* - * 1. if_version mismatch is not critical as our fw is designed - * to be backward compatible. - * 2. New fw usually brings some optimizations. But that's visible - * only on the paired driver. - * Considering above, we just leave user a verbal message instead - * of halt driver loading. - */ - if (if_version != smu->smc_driver_if_version) { - dev_info(smu->adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, " - "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n", - smu->smc_driver_if_version, if_version, - smu_program, smu_version, smu_major, smu_minor, smu_debug); - } - - return ret; -} - int smu_v12_0_powergate_sdma(struct smu_context *smu, bool gate) { if (!smu->is_apu) From 054695c0e236fd12a634b87c63d1ffa72ed59426 Mon Sep 17 00:00:00 2001 From: Lang Yu Date: Thu, 26 Mar 2026 21:38:58 +0800 Subject: [PATCH 475/712] drm/amdkfd: Switch to dev_* printk stuff in kfd_int_process_v12_1.c dev_* printk stuff is multi-GPU friendly. Use dev_warn_ratelimited() for print_sq_intr_info_error() which is consistent with previous IPs. Use dev_dbg_ratelimited() for irrelevant node interrupt print to avoid too much noise. Signed-off-by: Lang Yu Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher --- .../drm/amd/amdkfd/kfd_int_process_v12_1.c | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v12_1.c index 47947b94926b..0da7e1db55c9 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v12_1.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v12_1.c @@ -144,9 +144,10 @@ enum SQ_INTERRUPT_ERROR_TYPE { #define KFD_CTXID0_DOORBELL_ID(ctxid0) ((ctxid0) & \ KFD_CTXID0_DOORBELL_ID_MASK) -static void print_sq_intr_info_auto(uint32_t context_id0, uint32_t context_id1) +static void print_sq_intr_info_auto(struct kfd_node *dev, uint32_t context_id0, uint32_t context_id1) { - pr_debug_ratelimited( + dev_dbg_ratelimited( + dev->adev->dev, "sq_intr: auto, ttrace %d, wlt %d, ttrace_buf0_full %d, ttrace_buf1_full %d ttrace_utc_err %d\n", REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_AUTO_CTXID0, THREAD_TRACE), REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_AUTO_CTXID0, WLT), @@ -155,9 +156,10 @@ static void print_sq_intr_info_auto(uint32_t context_id0, uint32_t context_id1) REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_AUTO_CTXID0, THREAD_TRACE_UTC_ERROR)); } -static void print_sq_intr_info_inst(uint32_t context_id0, uint32_t context_id1) +static void print_sq_intr_info_inst(struct kfd_node *dev, uint32_t context_id0, uint32_t context_id1) { - pr_debug_ratelimited( + dev_dbg_ratelimited( + dev->adev->dev, "sq_intr: inst, data 0x%08x, sh %d, priv %d, wave_id %d, simd_id %d, wgp_id %d\n", REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_WAVE_CTXID0, DATA), REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_WAVE_CTXID0, SA_ID), @@ -167,9 +169,10 @@ static void print_sq_intr_info_inst(uint32_t context_id0, uint32_t context_id1) REG_GET_FIELD(context_id1, SQ_INTERRUPT_WORD_WAVE_CTXID1, WGP_ID)); } -static void print_sq_intr_info_error(uint32_t context_id0, uint32_t context_id1) +static void print_sq_intr_info_error(struct kfd_node *dev, uint32_t context_id0, uint32_t context_id1) { - pr_debug_ratelimited( + dev_warn_ratelimited( + dev->adev->dev, "sq_intr: error, detail 0x%08x, type %d, sh %d, priv %d, wave_id %d, simd_id %d, wgp_id %d\n", REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_ERROR_CTXID0, DETAIL), REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_ERROR_CTXID0, TYPE), @@ -246,7 +249,8 @@ static bool event_interrupt_isr_v12_1(struct kfd_node *node, vmid = SOC15_VMID_FROM_IH_ENTRY(ih_ring_entry); if (!kfd_irq_is_from_node(node, node_id, vmid)) { - pr_debug("Interrupt not for Node, node_id: %d, vmid: %d\n", node_id, vmid); + dev_dbg_ratelimited(node->adev->dev, + "Interrupt not for Node, node_id: %d, vmid: %d\n", node_id, vmid); return false; } @@ -266,9 +270,9 @@ static bool event_interrupt_isr_v12_1(struct kfd_node *node, (context_id0 & AMDGPU_FENCE_MES_QUEUE_FLAG)) return false; - pr_debug("client id 0x%x, source id %d, vmid %d, pasid 0x%x. raw data:\n", + dev_dbg(node->adev->dev, "client id 0x%x, source id %d, vmid %d, pasid 0x%x. raw data:\n", client_id, source_id, vmid, pasid); - pr_debug("%8X, %8X, %8X, %8X, %8X, %8X, %8X, %8X.\n", + dev_dbg(node->adev->dev, "%8X, %8X, %8X, %8X, %8X, %8X, %8X, %8X.\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); @@ -361,10 +365,10 @@ static void event_interrupt_wq_v12_1(struct kfd_node *node, SQ_INTERRUPT_WORD_WAVE_CTXID1, ENCODING); switch (sq_int_enc) { case SQ_INTERRUPT_WORD_ENCODING_AUTO: - print_sq_intr_info_auto(context_id0, context_id1); + print_sq_intr_info_auto(node, context_id0, context_id1); break; case SQ_INTERRUPT_WORD_ENCODING_INST: - print_sq_intr_info_inst(context_id0, context_id1); + print_sq_intr_info_inst(node, context_id0, context_id1); sq_int_priv = REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_WAVE_CTXID0, PRIV); if (sq_int_priv && (kfd_set_dbg_ev_from_interrupt(node, pasid, @@ -374,7 +378,7 @@ static void event_interrupt_wq_v12_1(struct kfd_node *node, return; break; case SQ_INTERRUPT_WORD_ENCODING_ERROR: - print_sq_intr_info_error(context_id0, context_id1); + print_sq_intr_info_error(node, context_id0, context_id1); sq_int_errtype = REG_GET_FIELD(context_id0, SQ_INTERRUPT_WORD_ERROR_CTXID0, TYPE); if (sq_int_errtype != SQ_INTERRUPT_ERROR_TYPE_ILLEGAL_INST && From 2fb4883b884a437d760bd7bdf7695a7e5a60bba3 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Fri, 27 Mar 2026 14:29:17 +0530 Subject: [PATCH 476/712] drm/amdgpu: Fix wait after reset sequence in S4 For a mode-1 reset done at the end of S4 on PSPv11 dGPUs, only check if TOS is unloaded. Fixes: 32f73741d6ee ("drm/amdgpu: Wait for bootloader after PSPv11 reset") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/4853 Signed-off-by: Lijo Lazar Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 8 ++++++-- drivers/gpu/drm/amd/amdgpu/psp_v11_0.c | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index a44baa9ee78d..8ed637f92322 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2683,8 +2683,12 @@ static int amdgpu_pmops_freeze(struct device *dev) if (r) return r; - if (amdgpu_acpi_should_gpu_reset(adev)) - return amdgpu_asic_reset(adev); + if (amdgpu_acpi_should_gpu_reset(adev)) { + amdgpu_device_lock_reset_domain(adev->reset_domain); + r = amdgpu_asic_reset(adev); + amdgpu_device_unlock_reset_domain(adev->reset_domain); + return r; + } return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c index 9aa988982304..fb7aaf5ae05c 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c @@ -170,7 +170,8 @@ static int psp_v11_0_wait_for_bootloader(struct psp_context *psp) int retry_loop; /* For a reset done at the end of S3, only wait for TOS to be unloaded */ - if (adev->in_s3 && !(adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev)) + if ((adev->in_s4 || adev->in_s3) && !(adev->flags & AMD_IS_APU) && + amdgpu_in_reset(adev)) return psp_v11_wait_for_tos_unload(psp); for (retry_loop = 0; retry_loop < 20; retry_loop++) { From e4b1715a87ab1e9ef1aa1f6ea82889eafba4a119 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Thu, 26 Mar 2026 09:55:08 +0530 Subject: [PATCH 477/712] drm/amdgpu/gfx11: Add Cleaner Shader Support for GFX11.5.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Cleaner Shader is responsible for clearing LDS, VGPRs and SGPRs between GPU workloads to enforce process isolation and avoid data leakage. The cleaner shader clears per-wave GPU state (LDS, VGPRs and SGPRs) between workloads, improving process isolation and preventing stale data from being observed by subsequent tasks. This reuses the existing cleaner shader used on GFX11.0.3 and enables it for GFX11.5.4 GPUs when firmware requirements are met. Cc: Muhammad Adam Cc: Mario Sopena-Novales Cc: Tom Wu Cc: Christian König Cc: Alex Deucher Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 78d1f3eb522e..ae39b9e1f7d6 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -1722,6 +1722,20 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) } } break; + case IP_VERSION(11, 5, 4): + adev->gfx.cleaner_shader_ptr = gfx_11_0_3_cleaner_shader_hex; + adev->gfx.cleaner_shader_size = sizeof(gfx_11_0_3_cleaner_shader_hex); + if (adev->gfx.me_fw_version >= 4 && + adev->gfx.pfp_fw_version >= 7 && + adev->gfx.mec_fw_version >= 5) { + adev->gfx.enable_cleaner_shader = true; + r = amdgpu_gfx_cleaner_shader_sw_init(adev, adev->gfx.cleaner_shader_size); + if (r) { + adev->gfx.enable_cleaner_shader = false; + dev_err(adev->dev, "Failed to initialize cleaner shader\n"); + } + } + break; default: adev->gfx.enable_cleaner_shader = false; break; From 754003486c3cc95f2fcb9d6b71a779047d6db95c Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Wed, 17 Dec 2025 16:36:02 +0800 Subject: [PATCH 478/712] drm/amd/display: Add Idle state manager(ISM) [Why] Rapid allow/disallow of idle optimization calls, whether it be IPS or self-refresh features, can end up using more power if actual time-in-idle is low. It can also spam DMUB command submission in a way that prevents it from servicing other requestors. [How] Introduce the Idle State Manager (ISM) to amdgpu. It maintains a finite state machine that uses a hysteresis to determine if a delay should be inserted between a caller allowing idle, and when the actual idle optimizations are programmed. A second timer is also introduced to enable static screen optimizations (SSO) such as PSR1 and Replay low HZ idle mode. Rapid SSO enable/disable can have a negative power impact on some low hz video playback, and can introduce user lag for PSR1 (due to up to 3 frames of sync latency). This effectively rate-limits idle optimizations, based on hysteresis. This also replaces the existing delay logic used for PSR1, allowing drm_vblank_crtc_config.disable_immediate = true, and thus allowing drm_crtc_vblank_restore(). v2: * Loosen criteria for ISM to exit idle optimizations; it failed to exit idle correctly on cursor updates when there are no drm_vblank requestors, * Document default_ism_config * Convert pr_debug to trace events to reduce overhead on frequent codepaths * checkpatch.pl fixes Link: https://gitlab.freedesktop.org/drm/amd/-/issues/4527 Link: https://gitlab.freedesktop.org/drm/amd/-/issues/3709 Fixes: 58a261bfc967 ("drm/amd/display: use a more lax vblank enable policy for older ASICs") Signed-off-by: Ray Wu Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 5 + .../gpu/drm/amd/display/amdgpu_dm/Makefile | 3 +- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 34 +- .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 90 +-- .../amd/display/amdgpu_dm/amdgpu_dm_crtc.h | 6 + .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c | 598 ++++++++++++++++++ .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h | 151 +++++ .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 16 + .../amd/display/amdgpu_dm/amdgpu_dm_trace.h | 63 ++ 9 files changed, 903 insertions(+), 63 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 90352284c5ee..51ab1a332615 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -44,6 +44,7 @@ #include #include "modules/inc/mod_freesync.h" #include "amdgpu_dm_irq_params.h" +#include "amdgpu_dm_ism.h" struct amdgpu_bo; struct amdgpu_device; @@ -486,6 +487,10 @@ struct amdgpu_crtc { int deferred_flip_completion; /* parameters access from DM IRQ handler */ struct dm_irq_params dm_irq_params; + + /* DM idle state manager */ + struct amdgpu_dm_ism ism; + /* pll sharing */ struct amdgpu_atom_ss ss; bool ss_enabled; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile index 8e949fe77312..89350aa9ca7e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -40,7 +40,8 @@ AMDGPUDM = \ amdgpu_dm_replay.o \ amdgpu_dm_quirks.o \ amdgpu_dm_wb.o \ - amdgpu_dm_colorop.o + amdgpu_dm_colorop.o \ + amdgpu_dm_ism.o ifdef CONFIG_DRM_AMD_DC_FP AMDGPUDM += dc_fpu.o diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 8b161c9d649c..21635e80349a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3283,6 +3283,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) mutex_lock(&dm->dc_lock); + amdgpu_dm_ism_disable(dm); dc_allow_idle_optimizations(adev->dm.dc, false); dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state); @@ -3316,6 +3317,9 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) amdgpu_dm_irq_suspend(adev); + scoped_guard(mutex, &dm->dc_lock) + amdgpu_dm_ism_disable(dm); + hpd_rx_irq_work_suspend(dm); dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3); @@ -3606,6 +3610,7 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) dc_resume(dm->dc); + amdgpu_dm_ism_enable(dm); amdgpu_dm_irq_resume_early(adev); for (i = 0; i < dc_state->stream_count; i++) { @@ -3666,6 +3671,9 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) /* program HPD filter */ dc_resume(dm->dc); + scoped_guard(mutex, &dm->dc_lock) + amdgpu_dm_ism_enable(dm); + /* * early enable HPD Rx IRQ, should be done before set mode as short * pulse interrupts are used for MST @@ -9334,31 +9342,7 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, if (acrtc_state) { timing = &acrtc_state->stream->timing; - /* - * Depending on when the HW latching event of double-buffered - * registers happen relative to the PSR SDP deadline, and how - * bad the Panel clock has drifted since the last ALPM off - * event, there can be up to 3 frames of delay between sending - * the PSR exit cmd to DMUB fw, and when the panel starts - * displaying live frames. - * - * We can set: - * - * 20/100 * offdelay_ms = 3_frames_ms - * => offdelay_ms = 5 * 3_frames_ms - * - * This ensures that `3_frames_ms` will only be experienced as a - * 20% delay on top how long the display has been static, and - * thus make the delay less perceivable. - */ - if (acrtc_state->stream->link->psr_settings.psr_version < - DC_PSR_VERSION_UNSUPPORTED) { - offdelay = DIV64_U64_ROUND_UP((u64)5 * 3 * 10 * - timing->v_total * - timing->h_total, - timing->pix_clk_100hz); - config.offdelay_ms = offdelay ?: 30; - } else if (amdgpu_ip_version(adev, DCE_HWIP, 0) < + if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0) || !(adev->flags & AMD_IS_APU)) { /* diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 304437c2284d..c3c588294665 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -124,37 +124,37 @@ bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state) * - Enable condition same as above * - Disable when vblank counter is enabled */ -static void amdgpu_dm_crtc_set_panel_sr_feature( - struct vblank_control_work *vblank_work, +void amdgpu_dm_crtc_set_panel_sr_feature( + struct amdgpu_display_manager *dm, + struct amdgpu_crtc *acrtc, + struct dc_stream_state *stream, bool vblank_enabled, bool allow_sr_entry) { - struct dc_link *link = vblank_work->stream->link; + struct dc_link *link = stream->link; bool is_sr_active = (link->replay_settings.replay_allow_active || link->psr_settings.psr_allow_active); bool is_crc_window_active = false; - bool vrr_active = amdgpu_dm_crtc_vrr_active_irq(vblank_work->acrtc); + bool vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY is_crc_window_active = - amdgpu_dm_crc_window_is_activated(&vblank_work->acrtc->base); + amdgpu_dm_crc_window_is_activated(&acrtc->base); #endif if (link->replay_settings.replay_feature_enabled && !vrr_active && allow_sr_entry && !is_sr_active && !is_crc_window_active) { - amdgpu_dm_replay_enable(vblank_work->stream, true); + amdgpu_dm_replay_enable(stream, true); } else if (vblank_enabled) { if (link->psr_settings.psr_version < DC_PSR_VERSION_SU_1 && is_sr_active) - amdgpu_dm_psr_disable(vblank_work->stream, false); + amdgpu_dm_psr_disable(stream, false); } else if (link->psr_settings.psr_feature_enabled && !vrr_active && allow_sr_entry && !is_sr_active && !is_crc_window_active) { struct amdgpu_dm_connector *aconn = - (struct amdgpu_dm_connector *) vblank_work->stream->dm_stream_context; + (struct amdgpu_dm_connector *) stream->dm_stream_context; if (!aconn->disallow_edp_enter_psr) { - struct amdgpu_display_manager *dm = vblank_work->dm; - - amdgpu_dm_psr_enable(vblank_work->stream); + amdgpu_dm_psr_enable(stream); if (dm->idle_workqueue && (dm->dc->config.disable_ips == DMUB_IPS_ENABLE) && dm->dc->idle_optimizations_allowed && @@ -251,33 +251,15 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work) mutex_lock(&dm->dc_lock); - if (vblank_work->enable) + if (vblank_work->enable) { dm->active_vblank_irq_count++; - else if (dm->active_vblank_irq_count) - dm->active_vblank_irq_count--; - - if (dm->active_vblank_irq_count > 0) - dc_allow_idle_optimizations(dm->dc, false); - - /* - * Control PSR based on vblank requirements from OS - * - * If panel supports PSR SU, there's no need to disable PSR when OS is - * submitting fast atomic commits (we infer this by whether the OS - * requests vblank events). Fast atomic commits will simply trigger a - * full-frame-update (FFU); a specific case of selective-update (SU) - * where the SU region is the full hactive*vactive region. See - * fill_dc_dirty_rects(). - */ - if (vblank_work->stream && vblank_work->stream->link && vblank_work->acrtc) { - amdgpu_dm_crtc_set_panel_sr_feature( - vblank_work, vblank_work->enable, - vblank_work->acrtc->dm_irq_params.allow_sr_entry); - } - - if (dm->active_vblank_irq_count == 0) { - dc_post_update_surfaces_to_stream(dm->dc); - dc_allow_idle_optimizations(dm->dc, true); + amdgpu_dm_ism_commit_event(&vblank_work->acrtc->ism, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED); + } else { + if (dm->active_vblank_irq_count > 0) + dm->active_vblank_irq_count--; + amdgpu_dm_ism_commit_event(&vblank_work->acrtc->ism, + DM_ISM_EVENT_ENTER_IDLE_REQUESTED); } mutex_unlock(&dm->dc_lock); @@ -476,6 +458,9 @@ static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *cr static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc) { + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + amdgpu_dm_ism_fini(&acrtc->ism); drm_crtc_cleanup(crtc); kfree(crtc); } @@ -719,6 +704,35 @@ static const struct drm_crtc_helper_funcs amdgpu_dm_crtc_helper_funcs = { .get_scanout_position = amdgpu_crtc_get_scanout_position, }; +/* + * This hysteresis filter as configured will: + * + * * Search through the latest 8[filter_history_size] entries in history, + * skipping entries that are older than [filter_old_history_threshold] frames + * (0 means ignore age) + * * Searches for short-idle-periods that lasted shorter than + * 4[filter_num_frames] frames-times + * * If there is at least 1[filter_entry_count] short-idle-period, then a delay + * of 4[activation_num_delay_frames] will applied before allowing idle + * optimizations again. + * * An additional delay of 11[sso_num_frames] is applied before enabling + * panel-specific optimizations. + * + * The values were determined empirically on another OS, optimizing for Z8 + * residency on APUs when running a productivity + web browsing test. + * + * TODO: Run similar tests to determine if these values are also optimal for + * Linux, and if each APU generation benefits differently. + */ +static struct amdgpu_dm_ism_config default_ism_config = { + .filter_num_frames = 4, + .filter_history_size = 8, + .filter_entry_count = 1, + .activation_num_delay_frames = 4, + .filter_old_history_threshold = 0, + .sso_num_frames = 11, +}; + int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm, struct drm_plane *plane, uint32_t crtc_index) @@ -749,6 +763,8 @@ int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm, if (res) goto fail; + amdgpu_dm_ism_init(&acrtc->ism, &default_ism_config); + drm_crtc_helper_add(&acrtc->base, &amdgpu_dm_crtc_helper_funcs); /* Create (reset) the plane state */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h index c1212947a77b..3a8094013a5d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h @@ -27,6 +27,12 @@ #ifndef __AMDGPU_DM_CRTC_H__ #define __AMDGPU_DM_CRTC_H__ +void amdgpu_dm_crtc_set_panel_sr_feature( + struct amdgpu_display_manager *dm, + struct amdgpu_crtc *acrtc, + struct dc_stream_state *stream, + bool vblank_enabled, bool allow_sr_entry); + void amdgpu_dm_crtc_handle_vblank(struct amdgpu_crtc *acrtc); bool amdgpu_dm_crtc_modeset_required(struct drm_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c new file mode 100644 index 000000000000..65a5cfe1e106 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include +#include + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_dm_ism.h" +#include "amdgpu_dm_crtc.h" +#include "amdgpu_dm_trace.h" + +/** + * dm_ism_next_state - Get next state based on current state and event + * + * This function defines the idle state management FSM. Invalid transitions + * are ignored and will not progress the FSM. + */ +static bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state, + enum amdgpu_dm_ism_event event, + enum amdgpu_dm_ism_state *next_state) +{ + switch (STATE_EVENT(current_state, event)) { + case STATE_EVENT(DM_ISM_STATE_FULL_POWER_RUNNING, + DM_ISM_EVENT_ENTER_IDLE_REQUESTED): + *next_state = DM_ISM_STATE_HYSTERESIS_WAITING; + break; + case STATE_EVENT(DM_ISM_STATE_FULL_POWER_RUNNING, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE): + *next_state = DM_ISM_STATE_FULL_POWER_BUSY; + break; + + case STATE_EVENT(DM_ISM_STATE_FULL_POWER_BUSY, + DM_ISM_EVENT_ENTER_IDLE_REQUESTED): + *next_state = DM_ISM_STATE_HYSTERESIS_BUSY; + break; + case STATE_EVENT(DM_ISM_STATE_FULL_POWER_BUSY, + DM_ISM_EVENT_END_CURSOR_UPDATE): + *next_state = DM_ISM_STATE_FULL_POWER_RUNNING; + break; + + case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED): + *next_state = DM_ISM_STATE_TIMER_ABORTED; + break; + case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE): + *next_state = DM_ISM_STATE_HYSTERESIS_BUSY; + break; + case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_TIMER_ELAPSED): + *next_state = DM_ISM_STATE_OPTIMIZED_IDLE; + break; + case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_IMMEDIATE): + *next_state = DM_ISM_STATE_OPTIMIZED_IDLE; + break; + + case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_BUSY, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED): + *next_state = DM_ISM_STATE_FULL_POWER_BUSY; + break; + case STATE_EVENT(DM_ISM_STATE_HYSTERESIS_BUSY, + DM_ISM_EVENT_END_CURSOR_UPDATE): + *next_state = DM_ISM_STATE_HYSTERESIS_WAITING; + break; + + case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED): + *next_state = DM_ISM_STATE_FULL_POWER_RUNNING; + break; + case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE): + *next_state = DM_ISM_STATE_HYSTERESIS_BUSY; + break; + case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_SSO_TIMER_ELAPSED): + case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_IMMEDIATE): + *next_state = DM_ISM_STATE_OPTIMIZED_IDLE_SSO; + break; + + case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE_SSO, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED): + *next_state = DM_ISM_STATE_FULL_POWER_RUNNING; + break; + case STATE_EVENT(DM_ISM_STATE_OPTIMIZED_IDLE_SSO, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE): + *next_state = DM_ISM_STATE_HYSTERESIS_BUSY; + break; + + case STATE_EVENT(DM_ISM_STATE_TIMER_ABORTED, + DM_ISM_EVENT_IMMEDIATE): + *next_state = DM_ISM_STATE_FULL_POWER_RUNNING; + break; + + default: + return false; + } + return true; +} + +static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, + const struct dc_stream_state *stream) +{ + const struct amdgpu_dm_ism_config *config = &ism->config; + uint32_t v_total, h_total; + uint64_t one_frame_ns, sso_delay_ns; + + if (!stream) + return 0; + + if (!config->sso_num_frames) + return 0; + + v_total = stream->timing.v_total; + h_total = stream->timing.h_total; + + one_frame_ns = div64_u64(v_total * h_total * 10000000ull, + stream->timing.pix_clk_100hz); + sso_delay_ns = config->sso_num_frames * one_frame_ns; + + return sso_delay_ns; +} + +/** + * dm_ism_get_idle_allow_delay - Calculate hysteresis-based idle allow delay + */ +static uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, + const struct dc_stream_state *stream) +{ + const struct amdgpu_dm_ism_config *config = &ism->config; + uint32_t v_total, h_total; + uint64_t one_frame_ns, short_idle_ns, old_hist_ns; + uint32_t history_size; + int pos; + uint32_t short_idle_count = 0; + uint64_t ret_ns = 0; + + if (!stream) + return 0; + + if (!config->filter_num_frames) + return 0; + if (!config->filter_entry_count) + return 0; + if (!config->activation_num_delay_frames) + return 0; + + v_total = stream->timing.v_total; + h_total = stream->timing.h_total; + + one_frame_ns = div64_u64(v_total * h_total * 10000000ull, + stream->timing.pix_clk_100hz); + + short_idle_ns = config->filter_num_frames * one_frame_ns; + old_hist_ns = config->filter_old_history_threshold * one_frame_ns; + + /* + * Look back into the recent history and count how many times we entered + * idle power state for a short duration of time + */ + history_size = min( + max(config->filter_history_size, config->filter_entry_count), + AMDGPU_DM_IDLE_HIST_LEN); + pos = ism->next_record_idx; + + for (int k = 0; k < history_size; k++) { + if (pos <= 0 || pos > AMDGPU_DM_IDLE_HIST_LEN) + pos = AMDGPU_DM_IDLE_HIST_LEN; + pos -= 1; + + if (ism->records[pos].duration_ns <= short_idle_ns) + short_idle_count += 1; + + if (short_idle_count >= config->filter_entry_count) + break; + + if (old_hist_ns > 0 && + ism->last_idle_timestamp_ns - ism->records[pos].timestamp_ns > old_hist_ns) + break; + } + + if (short_idle_count >= config->filter_entry_count) + ret_ns = config->activation_num_delay_frames * one_frame_ns; + + return ret_ns; +} + +/** + * dm_ism_insert_record - Insert a record into the circular history buffer + */ +static void dm_ism_insert_record(struct amdgpu_dm_ism *ism) +{ + struct amdgpu_dm_ism_record *record; + + if (ism->next_record_idx < 0 || + ism->next_record_idx >= AMDGPU_DM_IDLE_HIST_LEN) + ism->next_record_idx = 0; + + record = &ism->records[ism->next_record_idx]; + ism->next_record_idx += 1; + + record->timestamp_ns = ktime_get_ns(); + record->duration_ns = + record->timestamp_ns - ism->last_idle_timestamp_ns; +} + + +static void dm_ism_set_last_idle_ts(struct amdgpu_dm_ism *ism) +{ + ism->last_idle_timestamp_ns = ktime_get_ns(); +} + + +static bool dm_ism_trigger_event(struct amdgpu_dm_ism *ism, + enum amdgpu_dm_ism_event event) +{ + enum amdgpu_dm_ism_state next_state; + + bool gotNextState = dm_ism_next_state(ism->current_state, event, + &next_state); + + if (gotNextState) { + ism->previous_state = ism->current_state; + ism->current_state = next_state; + } + + return gotNextState; +} + + +static void dm_ism_commit_idle_optimization_state(struct amdgpu_dm_ism *ism, + struct dc_stream_state *stream, + bool vblank_enabled, + bool allow_panel_sso) +{ + struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism); + struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev); + struct amdgpu_display_manager *dm = &adev->dm; + int r; + + trace_amdgpu_dm_ism_commit(dm->active_vblank_irq_count, + vblank_enabled, + allow_panel_sso); + + /* + * If there is an active vblank requestor, or if SSO is being engaged, + * then disallow idle optimizations. + */ + if (vblank_enabled || allow_panel_sso) + dc_allow_idle_optimizations(dm->dc, false); + + /* + * Control PSR based on vblank requirements from OS + * + * If panel supports PSR SU/Replay, there's no need to exit self-refresh + * when OS is submitting fast atomic commits, as they can allow + * self-refresh during vblank periods. + */ + if (stream && stream->link) { + /* + * If allow_panel_sso is true when disabling vblank, allow + * deeper panel sleep states such as PSR1 and Replay static + * screen optimization. + */ + if (!vblank_enabled && allow_panel_sso) { + amdgpu_dm_crtc_set_panel_sr_feature( + dm, acrtc, stream, false, + acrtc->dm_irq_params.allow_sr_entry); + } else if (vblank_enabled) { + /* Make sure to exit SSO on vblank enable */ + amdgpu_dm_crtc_set_panel_sr_feature( + dm, acrtc, stream, true, + acrtc->dm_irq_params.allow_sr_entry); + } + /* + * Else, vblank_enabled == false and allow_panel_sso == false; + * do nothing here. + */ + } + + /* + * Check for any active drm vblank requestors on other CRTCs + * (dm->active_vblank_irq_count) before allowing HW-wide idle + * optimizations. + * + * There's no need to have a "balanced" check when disallowing idle + * optimizations at the start of this func -- we should disallow + * whenever there's *an* active CRTC. + */ + if (!vblank_enabled && dm->active_vblank_irq_count == 0) { + dc_post_update_surfaces_to_stream(dm->dc); + + r = amdgpu_dpm_pause_power_profile(adev, true); + if (r) + dev_warn(adev->dev, "failed to set default power profile mode\n"); + + dc_allow_idle_optimizations(dm->dc, true); + + r = amdgpu_dpm_pause_power_profile(adev, false); + if (r) + dev_warn(adev->dev, "failed to restore the power profile mode\n"); + } +} + + +static enum amdgpu_dm_ism_event dm_ism_dispatch_power_state( + struct amdgpu_dm_ism *ism, + struct dm_crtc_state *acrtc_state, + enum amdgpu_dm_ism_event event) +{ + enum amdgpu_dm_ism_event ret = event; + const struct amdgpu_dm_ism_config *config = &ism->config; + uint64_t delay_ns, sso_delay_ns; + + switch (ism->previous_state) { + case DM_ISM_STATE_HYSTERESIS_WAITING: + /* + * Stop the timer if it was set, and we're not running from the + * idle allow worker. + */ + if (ism->current_state != DM_ISM_STATE_OPTIMIZED_IDLE && + ism->current_state != DM_ISM_STATE_OPTIMIZED_IDLE_SSO) + cancel_delayed_work(&ism->delayed_work); + break; + case DM_ISM_STATE_OPTIMIZED_IDLE: + if (ism->current_state == DM_ISM_STATE_OPTIMIZED_IDLE_SSO) + break; + /* If idle disallow, cancel SSO work and insert record */ + cancel_delayed_work(&ism->sso_delayed_work); + dm_ism_insert_record(ism); + dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream, + true, false); + break; + case DM_ISM_STATE_OPTIMIZED_IDLE_SSO: + /* Disable idle optimization */ + dm_ism_insert_record(ism); + dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream, + true, false); + break; + default: + break; + } + + switch (ism->current_state) { + case DM_ISM_STATE_HYSTERESIS_WAITING: + dm_ism_set_last_idle_ts(ism); + + /* CRTC can be disabled; allow immediate idle */ + if (!acrtc_state->stream) { + ret = DM_ISM_EVENT_IMMEDIATE; + break; + } + + delay_ns = dm_ism_get_idle_allow_delay(ism, + acrtc_state->stream); + if (delay_ns == 0) { + ret = DM_ISM_EVENT_IMMEDIATE; + break; + } + + /* Schedule worker */ + mod_delayed_work(system_unbound_wq, &ism->delayed_work, + nsecs_to_jiffies(delay_ns)); + + break; + case DM_ISM_STATE_OPTIMIZED_IDLE: + sso_delay_ns = dm_ism_get_sso_delay(ism, acrtc_state->stream); + if (sso_delay_ns == 0) + ret = DM_ISM_EVENT_IMMEDIATE; + else if (config->sso_num_frames < config->filter_num_frames) { + /* + * If sso_num_frames is less than hysteresis frames, it + * indicates that allowing idle here, then disallowing + * idle after sso_num_frames has expired, will likely + * have a negative power impact. Skip idle allow here, + * and let the sso_delayed_work handle it. + */ + mod_delayed_work(system_unbound_wq, + &ism->sso_delayed_work, + nsecs_to_jiffies(sso_delay_ns)); + } else { + /* Enable idle optimization without SSO */ + dm_ism_commit_idle_optimization_state( + ism, acrtc_state->stream, false, false); + mod_delayed_work(system_unbound_wq, + &ism->sso_delayed_work, + nsecs_to_jiffies(sso_delay_ns)); + } + break; + case DM_ISM_STATE_OPTIMIZED_IDLE_SSO: + /* Enable static screen optimizations. */ + dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream, + false, true); + break; + case DM_ISM_STATE_TIMER_ABORTED: + dm_ism_insert_record(ism); + dm_ism_commit_idle_optimization_state(ism, acrtc_state->stream, + true, false); + ret = DM_ISM_EVENT_IMMEDIATE; + break; + default: + break; + } + + return ret; +} + +static char *dm_ism_events_str[DM_ISM_NUM_EVENTS] = { + [DM_ISM_EVENT_IMMEDIATE] = "IMMEDIATE", + [DM_ISM_EVENT_ENTER_IDLE_REQUESTED] = "ENTER_IDLE_REQUESTED", + [DM_ISM_EVENT_EXIT_IDLE_REQUESTED] = "EXIT_IDLE_REQUESTED", + [DM_ISM_EVENT_BEGIN_CURSOR_UPDATE] = "BEGIN_CURSOR_UPDATE", + [DM_ISM_EVENT_END_CURSOR_UPDATE] = "END_CURSOR_UPDATE", + [DM_ISM_EVENT_TIMER_ELAPSED] = "TIMER_ELAPSED", + [DM_ISM_EVENT_SSO_TIMER_ELAPSED] = "SSO_TIMER_ELAPSED", +}; + +static char *dm_ism_states_str[DM_ISM_NUM_STATES] = { + [DM_ISM_STATE_FULL_POWER_RUNNING] = "FULL_POWER_RUNNING", + [DM_ISM_STATE_FULL_POWER_BUSY] = "FULL_POWER_BUSY", + [DM_ISM_STATE_HYSTERESIS_WAITING] = "HYSTERESIS_WAITING", + [DM_ISM_STATE_HYSTERESIS_BUSY] = "HYSTERESIS_BUSY", + [DM_ISM_STATE_OPTIMIZED_IDLE] = "OPTIMIZED_IDLE", + [DM_ISM_STATE_OPTIMIZED_IDLE_SSO] = "OPTIMIZED_IDLE_SSO", + [DM_ISM_STATE_TIMER_ABORTED] = "TIMER_ABORTED", +}; + + +void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism, + enum amdgpu_dm_ism_event event) +{ + enum amdgpu_dm_ism_event next_event = event; + struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism); + struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev); + struct amdgpu_display_manager *dm = &adev->dm; + struct dm_crtc_state *acrtc_state = to_dm_crtc_state(acrtc->base.state); + + /* ISM transitions must be called with mutex acquired */ + ASSERT(mutex_is_locked(&dm->dc_lock)); + + if (!acrtc_state) { + trace_amdgpu_dm_ism_event(acrtc->crtc_id, "NO_STATE", + "NO_STATE", "N/A"); + return; + } + + do { + bool transition = dm_ism_trigger_event(ism, event); + + next_event = DM_ISM_NUM_EVENTS; + if (transition) { + trace_amdgpu_dm_ism_event( + acrtc->crtc_id, + dm_ism_states_str[ism->previous_state], + dm_ism_states_str[ism->current_state], + dm_ism_events_str[event]); + next_event = dm_ism_dispatch_power_state( + ism, acrtc_state, next_event); + } else { + trace_amdgpu_dm_ism_event( + acrtc->crtc_id, + dm_ism_states_str[ism->current_state], + dm_ism_states_str[ism->current_state], + dm_ism_events_str[event]); + } + + event = next_event; + + } while (next_event < DM_ISM_NUM_EVENTS); +} + + +static void dm_ism_delayed_work_func(struct work_struct *work) +{ + struct amdgpu_dm_ism *ism = + container_of(work, struct amdgpu_dm_ism, delayed_work.work); + struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism); + struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev); + struct amdgpu_display_manager *dm = &adev->dm; + + guard(mutex)(&dm->dc_lock); + + amdgpu_dm_ism_commit_event(ism, DM_ISM_EVENT_TIMER_ELAPSED); +} + +static void dm_ism_sso_delayed_work_func(struct work_struct *work) +{ + struct amdgpu_dm_ism *ism = + container_of(work, struct amdgpu_dm_ism, sso_delayed_work.work); + struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism); + struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev); + struct amdgpu_display_manager *dm = &adev->dm; + + guard(mutex)(&dm->dc_lock); + + amdgpu_dm_ism_commit_event(ism, DM_ISM_EVENT_SSO_TIMER_ELAPSED); +} + +/** + * amdgpu_dm_ism_disable - Disable the ISM + * + * @dm: The amdgpu display manager + * + * Disable the idle state manager by disabling any ISM work, canceling pending + * work, and waiting for in-progress work to finish. After disabling, the system + * is left in DM_ISM_STATE_FULL_POWER_RUNNING state. + */ +void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm) +{ + struct drm_crtc *crtc; + struct amdgpu_crtc *acrtc; + struct amdgpu_dm_ism *ism; + + drm_for_each_crtc(crtc, dm->ddev) { + acrtc = to_amdgpu_crtc(crtc); + ism = &acrtc->ism; + + /* Cancel and disable any pending work */ + disable_delayed_work_sync(&ism->delayed_work); + disable_delayed_work_sync(&ism->sso_delayed_work); + + /* + * When disabled, leave in FULL_POWER_RUNNING state. + * EXIT_IDLE will not queue any work + */ + amdgpu_dm_ism_commit_event(ism, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED); + } +} + +/** + * amdgpu_dm_ism_enable - enable the ISM + * + * @dm: The amdgpu display manager + * + * Re-enable the idle state manager by enabling work that was disabled by + * amdgpu_dm_ism_disable. + */ +void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm) +{ + struct drm_crtc *crtc; + struct amdgpu_crtc *acrtc; + struct amdgpu_dm_ism *ism; + + drm_for_each_crtc(crtc, dm->ddev) { + acrtc = to_amdgpu_crtc(crtc); + ism = &acrtc->ism; + + enable_delayed_work(&ism->delayed_work); + enable_delayed_work(&ism->sso_delayed_work); + } +} + +void amdgpu_dm_ism_init(struct amdgpu_dm_ism *ism, + struct amdgpu_dm_ism_config *config) +{ + ism->config = *config; + + ism->current_state = DM_ISM_STATE_FULL_POWER_RUNNING; + ism->previous_state = DM_ISM_STATE_FULL_POWER_RUNNING; + ism->next_record_idx = 0; + ism->last_idle_timestamp_ns = 0; + + INIT_DELAYED_WORK(&ism->delayed_work, dm_ism_delayed_work_func); + INIT_DELAYED_WORK(&ism->sso_delayed_work, dm_ism_sso_delayed_work_func); +} + + +void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism) +{ + cancel_delayed_work_sync(&ism->sso_delayed_work); + cancel_delayed_work_sync(&ism->delayed_work); +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h new file mode 100644 index 000000000000..fde0ddc8d4e4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __AMDGPU_DM_ISM_H__ +#define __AMDGPU_DM_ISM_H__ + +#include + +struct amdgpu_crtc; +struct amdgpu_display_manager; + +#define AMDGPU_DM_IDLE_HIST_LEN 16 + +enum amdgpu_dm_ism_state { + DM_ISM_STATE_FULL_POWER_RUNNING, + DM_ISM_STATE_FULL_POWER_BUSY, + DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_STATE_HYSTERESIS_BUSY, + DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_STATE_OPTIMIZED_IDLE_SSO, + DM_ISM_STATE_TIMER_ABORTED, + DM_ISM_NUM_STATES, +}; + +enum amdgpu_dm_ism_event { + DM_ISM_EVENT_IMMEDIATE, + DM_ISM_EVENT_ENTER_IDLE_REQUESTED, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, + DM_ISM_EVENT_END_CURSOR_UPDATE, + DM_ISM_EVENT_TIMER_ELAPSED, + DM_ISM_EVENT_SSO_TIMER_ELAPSED, + DM_ISM_NUM_EVENTS, +}; + +#define STATE_EVENT(state, event) (((state) << 8) | (event)) + +struct amdgpu_dm_ism_config { + + /** + * @filter_num_frames: Idle periods shorter than this number of frames + * will be considered a "short idle period" for filtering. + * + * 0 indicates no filtering (i.e. no idle allow delay will be applied) + */ + unsigned int filter_num_frames; + + /** + * @filter_history_size: Number of recent idle periods to consider when + * counting the number of short idle periods. + */ + unsigned int filter_history_size; + + /** + * @filter_entry_count: When the number of short idle periods within + * recent &filter_history_size reaches this count, the idle allow delay + * will be applied. + * + * 0 indicates no filtering (i.e. no idle allow delay will be applied) + */ + unsigned int filter_entry_count; + + /** + * @activation_num_delay_frames: Defines the number of frames to wait + * for the idle allow delay. + * + * 0 indicates no filtering (i.e. no idle allow delay will be applied) + */ + unsigned int activation_num_delay_frames; + + /** + * @filter_old_history_threshold: A time-based restriction on top of + * &filter_history_size. Idle periods older than this threshold (in + * number of frames) will be ignored when counting the number of short + * idle periods. + * + * 0 indicates no time-based restriction, i.e. history is limited only + * by &filter_history_size. + */ + unsigned int filter_old_history_threshold; + + /** + * @sso_num_frames: Number of frames to delay before enabling static + * screen optimizations, such as PSR1 and Replay low HZ idle mode. + * + * 0 indicates immediate SSO enable upon allowing idle. + */ + unsigned int sso_num_frames; +}; + +struct amdgpu_dm_ism_record { + /** + * @timestamp_ns: When idle was allowed + */ + unsigned long long timestamp_ns; + + /** + * @duration_ns: How long idle was allowed + */ + unsigned long long duration_ns; +}; + +struct amdgpu_dm_ism { + struct amdgpu_dm_ism_config config; + unsigned long long last_idle_timestamp_ns; + + enum amdgpu_dm_ism_state current_state; + enum amdgpu_dm_ism_state previous_state; + + struct amdgpu_dm_ism_record records[AMDGPU_DM_IDLE_HIST_LEN]; + int next_record_idx; + + struct delayed_work delayed_work; + struct delayed_work sso_delayed_work; +}; + +#define ism_to_amdgpu_crtc(ism_ptr) \ + container_of(ism_ptr, struct amdgpu_crtc, ism) + +void amdgpu_dm_ism_init(struct amdgpu_dm_ism *ism, + struct amdgpu_dm_ism_config *config); +void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism); +void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism, + enum amdgpu_dm_ism_event event); +void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm); +void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm); + +#endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 54ae1c371511..3ba71e2d259c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1374,8 +1374,16 @@ void amdgpu_dm_plane_handle_cursor_update(struct drm_plane *plane, /* turn off cursor */ if (crtc_state && crtc_state->stream) { mutex_lock(&adev->dm.dc_lock); + amdgpu_dm_ism_commit_event( + &amdgpu_crtc->ism, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE); + dc_stream_program_cursor_position(crtc_state->stream, &position); + + amdgpu_dm_ism_commit_event( + &amdgpu_crtc->ism, + DM_ISM_EVENT_END_CURSOR_UPDATE); mutex_unlock(&adev->dm.dc_lock); } return; @@ -1405,6 +1413,10 @@ void amdgpu_dm_plane_handle_cursor_update(struct drm_plane *plane, if (crtc_state->stream) { mutex_lock(&adev->dm.dc_lock); + amdgpu_dm_ism_commit_event( + &amdgpu_crtc->ism, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE); + if (!dc_stream_program_cursor_attributes(crtc_state->stream, &attributes)) DRM_ERROR("DC failed to set cursor attributes\n"); @@ -1412,6 +1424,10 @@ void amdgpu_dm_plane_handle_cursor_update(struct drm_plane *plane, if (!dc_stream_program_cursor_position(crtc_state->stream, &position)) DRM_ERROR("DC failed to set cursor position\n"); + + amdgpu_dm_ism_commit_event( + &amdgpu_crtc->ism, + DM_ISM_EVENT_END_CURSOR_UPDATE); mutex_unlock(&adev->dm.dc_lock); } } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h index aa56fd6d56c3..e0fab8878d19 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h @@ -753,6 +753,69 @@ TRACE_EVENT(amdgpu_dm_brightness, ) ); +TRACE_EVENT(amdgpu_dm_ism_commit, + TP_PROTO( + int active_vblank_irq_count, + bool vblank_enabled, + bool allow_panel_sso + ), + TP_ARGS( + active_vblank_irq_count, + vblank_enabled, + allow_panel_sso + ), + TP_STRUCT__entry( + __field(int, active_vblank_irq_count) + __field(bool, vblank_enabled) + __field(bool, allow_panel_sso) + ), + TP_fast_assign( + __entry->active_vblank_irq_count = active_vblank_irq_count; + __entry->vblank_enabled = vblank_enabled; + __entry->allow_panel_sso = allow_panel_sso; + ), + TP_printk( + "active_vblank_irq_count=%d vblank_enabled=%d allow_panel_sso=%d", + __entry->active_vblank_irq_count, + __entry->vblank_enabled, + __entry->allow_panel_sso + ) +); + +TRACE_EVENT(amdgpu_dm_ism_event, + TP_PROTO( + int crtc_id, + const char *prev_state, + const char *curr_state, + const char *event + ), + TP_ARGS( + crtc_id, + prev_state, + curr_state, + event + ), + TP_STRUCT__entry( + __field(int, crtc_id) + __string(prev_state, prev_state) + __string(curr_state, curr_state) + __string(event, event) + ), + TP_fast_assign( + __entry->crtc_id = crtc_id; + __assign_str(prev_state); + __assign_str(curr_state); + __assign_str(event); + ), + TP_printk( + "[CRTC %d] %s -> %s on event %s", + __entry->crtc_id, + __get_str(prev_state), + __get_str(curr_state), + __get_str(event)) +); + + #endif /* _AMDGPU_DM_TRACE_H_ */ #undef TRACE_INCLUDE_PATH From 964e532d58378e6b942d2ba12fc3e25920fd4f80 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Fri, 20 Mar 2026 11:20:44 +0530 Subject: [PATCH 479/712] drm/amd/pm: Unify version check in SMUv14 Use common helper function for firmware version check and logging in SMUv14 Signed-off-by: Lijo Lazar Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h | 7 --- .../gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c | 60 ------------------- .../drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 18 +++++- .../drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 5 +- 4 files changed, 21 insertions(+), 69 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h index b453e6efc7c9..4eb40ff8aff2 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h @@ -25,11 +25,6 @@ #include "amdgpu_smu.h" -#define SMU14_DRIVER_IF_VERSION_INV 0xFFFFFFFF -#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_0 0x7 -#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_1 0x6 -#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_2 0x2E - #define FEATURE_MASK(feature) (1ULL << feature) /* MP Apertures */ @@ -124,8 +119,6 @@ int smu_v14_0_setup_pptable(struct smu_context *smu); int smu_v14_0_get_vbios_bootup_values(struct smu_context *smu); -int smu_v14_0_check_fw_version(struct smu_context *smu); - int smu_v14_0_set_driver_table_location(struct smu_context *smu); int smu_v14_0_set_tool_table_location(struct smu_context *smu); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c index e38354c694c9..d0a8df1aa6b6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c @@ -229,66 +229,6 @@ int smu_v14_0_check_fw_status(struct smu_context *smu) return -EIO; } -int smu_v14_0_check_fw_version(struct smu_context *smu) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t if_version = 0xff, smu_version = 0xff; - uint8_t smu_program, smu_major, smu_minor, smu_debug; - int ret = 0; - - ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version); - if (ret) - return ret; - - smu_program = (smu_version >> 24) & 0xff; - smu_major = (smu_version >> 16) & 0xff; - smu_minor = (smu_version >> 8) & 0xff; - smu_debug = (smu_version >> 0) & 0xff; - if (smu->is_apu) - adev->pm.fw_version = smu_version; - - switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { - case IP_VERSION(14, 0, 0): - case IP_VERSION(14, 0, 4): - case IP_VERSION(14, 0, 5): - smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_0; - break; - case IP_VERSION(14, 0, 1): - smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_1; - break; - case IP_VERSION(14, 0, 2): - case IP_VERSION(14, 0, 3): - smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_2; - break; - default: - dev_err(adev->dev, "smu unsupported IP version: 0x%x.\n", - amdgpu_ip_version(adev, MP1_HWIP, 0)); - smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_INV; - break; - } - - if (adev->pm.fw) - dev_dbg(smu->adev->dev, "smu fw reported program %d, version = 0x%08x (%d.%d.%d)\n", - smu_program, smu_version, smu_major, smu_minor, smu_debug); - - /* - * 1. if_version mismatch is not critical as our fw is designed - * to be backward compatible. - * 2. New fw usually brings some optimizations. But that's visible - * only on the paired driver. - * Considering above, we just leave user a verbal message instead - * of halt driver loading. - */ - if (if_version != smu->smc_driver_if_version) { - dev_info(adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, " - "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n", - smu->smc_driver_if_version, if_version, - smu_program, smu_version, smu_major, smu_minor, smu_debug); - } - - return ret; -} - static int smu_v14_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index 2353524b8821..a28624d4847a 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -65,6 +65,9 @@ #define SMU_MALL_PG_CONFIG_DEFAULT SMU_MALL_PG_CONFIG_DRIVER_CONTROL_ALWAYS_ON +#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_0 0x7 +#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_1 0x6 + #define SMU_14_0_0_UMD_PSTATE_GFXCLK 700 #define SMU_14_0_0_UMD_PSTATE_SOCCLK 678 #define SMU_14_0_0_UMD_PSTATE_FCLK 1800 @@ -1699,7 +1702,7 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu) static const struct pptable_funcs smu_v14_0_0_ppt_funcs = { .check_fw_status = smu_v14_0_check_fw_status, - .check_fw_version = smu_v14_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .init_smc_tables = smu_v14_0_0_init_smc_tables, .fini_smc_tables = smu_v14_0_0_fini_smc_tables, .get_vbios_bootup_values = smu_v14_0_get_vbios_bootup_values, @@ -1750,10 +1753,23 @@ static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu) void smu_v14_0_0_set_ppt_funcs(struct smu_context *smu) { + struct amdgpu_device *adev = smu->adev; + smu->ppt_funcs = &smu_v14_0_0_ppt_funcs; smu->feature_map = smu_v14_0_0_feature_mask_map; smu->table_map = smu_v14_0_0_table_map; smu->is_apu = true; + switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { + case IP_VERSION(14, 0, 0): + case IP_VERSION(14, 0, 4): + case IP_VERSION(14, 0, 5): + smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_0; + break; + case IP_VERSION(14, 0, 1): + smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_1; + break; + } + smu_v14_0_0_init_msg_ctl(smu); } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index c3ebfac062a7..31f9566f7979 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -68,6 +68,8 @@ static const struct smu_feature_bits smu_v14_0_2_dpm_features = { SMU_FEATURE_BIT_INIT(FEATURE_DPM_FCLK_BIT) } }; +#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_2 0x2E + #define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000 #define DEBUGSMC_MSG_Mode1Reset 2 #define LINK_SPEED_MAX 3 @@ -2798,7 +2800,7 @@ static const struct pptable_funcs smu_v14_0_2_ppt_funcs = { .fini_power = smu_v14_0_fini_power, .check_fw_status = smu_v14_0_check_fw_status, .setup_pptable = smu_v14_0_2_setup_pptable, - .check_fw_version = smu_v14_0_check_fw_version, + .check_fw_version = smu_cmn_check_fw_version, .set_driver_table_location = smu_v14_0_set_driver_table_location, .system_features_control = smu_v14_0_system_features_control, .set_allowed_mask = smu_v14_0_set_allowed_mask, @@ -2863,5 +2865,6 @@ void smu_v14_0_2_set_ppt_funcs(struct smu_context *smu) smu->table_map = smu_v14_0_2_table_map; smu->pwr_src_map = smu_v14_0_2_pwr_src_map; smu->workload_map = smu_v14_0_2_workload_map; + smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_2; smu_v14_0_2_init_msg_ctl(smu); } From 4ea64d482fc2cc85009fce5abdf4780ece00c31c Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Fri, 27 Mar 2026 09:46:42 -0400 Subject: [PATCH 480/712] drm/amdkfd: fix kernel crash on releasing NULL sysfs entry there is an abnormal case that When a process re-opens kfd with different mm_struct(execve() called by user), the allocated p->kobj will be freed, but missed setting it to NULL, that will cause sysfs/kernel crash with NULL pointers in p->kobj on kfd_process_remove_sysfs() when releasing process, and the similar error on kfd_procfs_del_queue() as well. Signed-off-by: Eric Huang Reviewed-by: Kent Russell Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_process.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index a031166f270c..bcd21204aa50 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -679,7 +679,7 @@ static void kfd_procfs_add_sysfs_files(struct kfd_process *p) void kfd_procfs_del_queue(struct queue *q) { - if (!q) + if (!q || !q->process->kobj) return; kobject_del(&q->kobj); @@ -858,6 +858,7 @@ int kfd_create_process_sysfs(struct kfd_process *process) if (ret) { pr_warn("Creating procfs pid directory failed"); kobject_put(process->kobj); + process->kobj = NULL; return ret; } From 0f1fbe746cf8bad0a5af9b62f17aad35193d44fc Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Tue, 3 Feb 2026 11:22:08 +0100 Subject: [PATCH 481/712] drm/amdgpu: allocate clear entities dynamically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional change for now, as we always allocate a single entity and use it everywhere. Signed-off-by: Pierre-Eric Pelloux-Prayer Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 55 +++++++++++++++------- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 3 +- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index cc004830a8a2..029a4bde91e7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -1325,7 +1325,7 @@ void amdgpu_bo_release_notify(struct ttm_buffer_object *bo) if (r) goto out; - r = amdgpu_fill_buffer(&adev->mman.clear_entity, abo, 0, &bo->base._resv, + r = amdgpu_fill_buffer(&adev->mman.clear_entities[0], abo, 0, &bo->base._resv, &fence, AMDGPU_KERNEL_JOB_ID_CLEAR_ON_RELEASE); if (WARN_ON(r)) goto out; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 4e0469938e90..b578c28f8d3a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2335,8 +2335,9 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) { struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM); + u32 num_clear_entities; uint64_t size; - int r; + int r, i, j; if (!adev->mman.initialized || amdgpu_in_reset(adev) || adev->mman.buffer_funcs_enabled == enable || adev->gmc.is_app_apu) @@ -2351,6 +2352,7 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) return; } + num_clear_entities = 1; ring = adev->mman.buffer_funcs_ring; sched = &ring->sched; r = amdgpu_ttm_buffer_entity_init(&adev->mman.gtt_mgr, @@ -2363,14 +2365,28 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) return; } - r = amdgpu_ttm_buffer_entity_init(&adev->mman.gtt_mgr, - &adev->mman.clear_entity, - DRM_SCHED_PRIORITY_NORMAL, - &sched, 1, 1); - if (r < 0) { - dev_err(adev->dev, - "Failed setting up TTM BO clear entity (%d)\n", r); + adev->mman.clear_entities = kcalloc(num_clear_entities, + sizeof(struct amdgpu_ttm_buffer_entity), + GFP_KERNEL); + if (!adev->mman.clear_entities) goto error_free_default_entity; + + adev->mman.num_clear_entities = num_clear_entities; + + for (i = 0; i < num_clear_entities; i++) { + r = amdgpu_ttm_buffer_entity_init( + &adev->mman.gtt_mgr, &adev->mman.clear_entities[i], + DRM_SCHED_PRIORITY_NORMAL, &sched, 1, 1); + + if (r < 0) { + for (j = 0; j < i; j++) + amdgpu_ttm_buffer_entity_fini( + &adev->mman.gtt_mgr, &adev->mman.clear_entities[j]); + kfree(adev->mman.clear_entities); + adev->mman.num_clear_entities = 0; + adev->mman.clear_entities = NULL; + goto error_free_default_entity; + } } r = amdgpu_ttm_buffer_entity_init(&adev->mman.gtt_mgr, @@ -2380,19 +2396,23 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) if (r < 0) { dev_err(adev->dev, "Failed setting up TTM BO move entity (%d)\n", r); - goto error_free_clear_entity; + goto error_free_clear_entities; } } else { amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, &adev->mman.default_entity); - amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, - &adev->mman.clear_entity); + for (i = 0; i < adev->mman.num_clear_entities; i++) + amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, + &adev->mman.clear_entities[i]); amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, &adev->mman.move_entity); /* Drop all the old fences since re-creating the scheduler entities * will allocate new contexts. */ ttm_resource_manager_cleanup(man); + kfree(adev->mman.clear_entities); + adev->mman.clear_entities = NULL; + adev->mman.num_clear_entities = 0; } /* this just adjusts TTM size idea, which sets lpfn to the correct value */ @@ -2405,9 +2425,13 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) return; -error_free_clear_entity: - amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, - &adev->mman.clear_entity); +error_free_clear_entities: + for (i = 0; i < adev->mman.num_clear_entities; i++) + amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, + &adev->mman.clear_entities[i]); + kfree(adev->mman.clear_entities); + adev->mman.clear_entities = NULL; + adev->mman.num_clear_entities = 0; error_free_default_entity: amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, &adev->mman.default_entity); @@ -2557,8 +2581,7 @@ int amdgpu_ttm_clear_buffer(struct amdgpu_bo *bo, if (!fence) return -EINVAL; - - entity = &adev->mman.clear_entity; + entity = &adev->mman.clear_entities[0]; *fence = dma_fence_get_stub(); amdgpu_res_first(bo->tbo.resource, 0, amdgpu_bo_size(bo), &cursor); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index bf101215757e..e98d458b8029 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -72,8 +72,9 @@ struct amdgpu_mman { /* @default_entity: for workarounds, has no gart windows */ struct amdgpu_ttm_buffer_entity default_entity; - struct amdgpu_ttm_buffer_entity clear_entity; struct amdgpu_ttm_buffer_entity move_entity; + struct amdgpu_ttm_buffer_entity *clear_entities; + u32 num_clear_entities; struct amdgpu_vram_mgr vram_mgr; struct amdgpu_gtt_mgr gtt_mgr; From ab5dd4dcc57effc8ab8b4185740a0e3441754f13 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Tue, 3 Feb 2026 11:22:09 +0100 Subject: [PATCH 482/712] drm/amdgpu: allocate move entities dynamically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional change for now, as we always allocate a single entity. Signed-off-by: Pierre-Eric Pelloux-Prayer Acked-by: Felix Kuehling Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 35 +++++++++++++++--------- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 3 +- drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index b578c28f8d3a..97448a055125 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -399,7 +399,7 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo, dst.offset = 0; r = amdgpu_ttm_copy_mem_to_mem(adev, - &adev->mman.move_entity, + &adev->mman.move_entities[0], &src, &dst, new_mem->size, amdgpu_bo_encrypted(abo), @@ -412,7 +412,7 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo, (abo->flags & AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE)) { struct dma_fence *wipe_fence = NULL; - r = amdgpu_fill_buffer(&adev->mman.move_entity, + r = amdgpu_fill_buffer(&adev->mman.move_entities[0], abo, 0, NULL, &wipe_fence, AMDGPU_KERNEL_JOB_ID_MOVE_BLIT); if (r) { @@ -2335,7 +2335,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) { struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM); - u32 num_clear_entities; + u32 num_clear_entities, num_move_entities; uint64_t size; int r, i, j; @@ -2353,6 +2353,7 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) } num_clear_entities = 1; + num_move_entities = 1; ring = adev->mman.buffer_funcs_ring; sched = &ring->sched; r = amdgpu_ttm_buffer_entity_init(&adev->mman.gtt_mgr, @@ -2389,14 +2390,20 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) } } - r = amdgpu_ttm_buffer_entity_init(&adev->mman.gtt_mgr, - &adev->mman.move_entity, - DRM_SCHED_PRIORITY_NORMAL, - &sched, 1, 2); - if (r < 0) { - dev_err(adev->dev, - "Failed setting up TTM BO move entity (%d)\n", r); - goto error_free_clear_entities; + adev->mman.num_move_entities = num_move_entities; + for (i = 0; i < num_move_entities; i++) { + r = amdgpu_ttm_buffer_entity_init( + &adev->mman.gtt_mgr, + &adev->mman.move_entities[i], + DRM_SCHED_PRIORITY_NORMAL, &sched, 1, 2); + + if (r < 0) { + for (j = 0; j < i; j++) + amdgpu_ttm_buffer_entity_fini( + &adev->mman.gtt_mgr, &adev->mman.move_entities[j]); + adev->mman.num_move_entities = 0; + goto error_free_clear_entities; + } } } else { amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, @@ -2404,8 +2411,9 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) for (i = 0; i < adev->mman.num_clear_entities; i++) amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, &adev->mman.clear_entities[i]); - amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, - &adev->mman.move_entity); + for (i = 0; i < adev->mman.num_move_entities; i++) + amdgpu_ttm_buffer_entity_fini(&adev->mman.gtt_mgr, + &adev->mman.move_entities[i]); /* Drop all the old fences since re-creating the scheduler entities * will allocate new contexts. */ @@ -2413,6 +2421,7 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) kfree(adev->mman.clear_entities); adev->mman.clear_entities = NULL; adev->mman.num_clear_entities = 0; + adev->mman.num_move_entities = 0; } /* this just adjusts TTM size idea, which sets lpfn to the correct value */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index e98d458b8029..cd24ca851b6d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -72,9 +72,10 @@ struct amdgpu_mman { /* @default_entity: for workarounds, has no gart windows */ struct amdgpu_ttm_buffer_entity default_entity; - struct amdgpu_ttm_buffer_entity move_entity; struct amdgpu_ttm_buffer_entity *clear_entities; u32 num_clear_entities; + struct amdgpu_ttm_buffer_entity move_entities[TTM_NUM_MOVE_FENCES]; + u32 num_move_entities; struct amdgpu_vram_mgr vram_mgr; struct amdgpu_gtt_mgr gtt_mgr; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 10bc81ce37cb..964efa325908 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -136,7 +136,7 @@ svm_migrate_copy_memory_gart(struct amdgpu_device *adev, dma_addr_t *sys, u64 size; int r; - entity = &adev->mman.move_entity; + entity = &adev->mman.move_entities[0]; mutex_lock(&entity->lock); From e2b0c863d3861c3c564d837ba60cd8765fe0163b Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Tue, 3 Feb 2026 11:22:10 +0100 Subject: [PATCH 483/712] drm/amdgpu: round robin through clear_entities in amdgpu_fill_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes clear of different BOs run in parallel. Partial jobs to clear a single BO still execute sequentially. Signed-off-by: Pierre-Eric Pelloux-Prayer Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 3 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 20 ++++++++++++++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 029a4bde91e7..27f7567df7bf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -1325,7 +1325,8 @@ void amdgpu_bo_release_notify(struct ttm_buffer_object *bo) if (r) goto out; - r = amdgpu_fill_buffer(&adev->mman.clear_entities[0], abo, 0, &bo->base._resv, + r = amdgpu_fill_buffer(amdgpu_ttm_next_clear_entity(adev), + abo, 0, &bo->base._resv, &fence, AMDGPU_KERNEL_JOB_ID_CLEAR_ON_RELEASE); if (WARN_ON(r)) goto out; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 97448a055125..375c3414b0a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2369,6 +2369,7 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) adev->mman.clear_entities = kcalloc(num_clear_entities, sizeof(struct amdgpu_ttm_buffer_entity), GFP_KERNEL); + atomic_set(&adev->mman.next_clear_entity, 0); if (!adev->mman.clear_entities) goto error_free_default_entity; @@ -2642,11 +2643,8 @@ int amdgpu_fill_buffer(struct amdgpu_ttm_buffer_entity *entity, struct amdgpu_res_cursor dst; int r; - if (!adev->mman.buffer_funcs_enabled) { - dev_err(adev->dev, - "Trying to clear memory with ring turned off.\n"); + if (!entity) return -EINVAL; - } amdgpu_res_first(bo->tbo.resource, 0, amdgpu_bo_size(bo), &dst); @@ -2682,6 +2680,20 @@ int amdgpu_fill_buffer(struct amdgpu_ttm_buffer_entity *entity, return r; } +struct amdgpu_ttm_buffer_entity * +amdgpu_ttm_next_clear_entity(struct amdgpu_device *adev) +{ + struct amdgpu_mman *mman = &adev->mman; + u32 i; + + if (mman->num_clear_entities == 0) + return NULL; + + i = atomic_inc_return(&mman->next_clear_entity) % + mman->num_clear_entities; + return &mman->clear_entities[i]; +} + /** * amdgpu_ttm_evict_resources - evict memory buffers * @adev: amdgpu device object diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index cd24ca851b6d..cf32db3defb1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -73,6 +73,7 @@ struct amdgpu_mman { /* @default_entity: for workarounds, has no gart windows */ struct amdgpu_ttm_buffer_entity default_entity; struct amdgpu_ttm_buffer_entity *clear_entities; + atomic_t next_clear_entity; u32 num_clear_entities; struct amdgpu_ttm_buffer_entity move_entities[TTM_NUM_MOVE_FENCES]; u32 num_move_entities; @@ -193,6 +194,7 @@ int amdgpu_fill_buffer(struct amdgpu_ttm_buffer_entity *entity, struct dma_resv *resv, struct dma_fence **f, u64 k_job_id); +struct amdgpu_ttm_buffer_entity *amdgpu_ttm_next_clear_entity(struct amdgpu_device *adev); int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo); void amdgpu_ttm_recover_gart(struct ttm_buffer_object *tbo); From 6ab4054fda924dabc88e78b13c76043d55d257e0 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Tue, 3 Feb 2026 11:22:11 +0100 Subject: [PATCH 484/712] drm/amdgpu: use TTM_NUM_MOVE_FENCES when reserving fences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use TTM_NUM_MOVE_FENCES as an upperbound of how many fences ttm might need to deal with moves/evictions. Signed-off-by: Pierre-Eric Pelloux-Prayer Acked-by: Felix Kuehling Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 5 ++--- drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c | 6 ++---- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 3 +-- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c | 6 ++---- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c | 6 ++---- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index c048217615c1..b24d5d21be5f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -908,9 +908,8 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, goto out_free_user_pages; amdgpu_bo_list_for_each_entry(e, p->bo_list) { - /* One fence for TTM and one for each CS job */ r = drm_exec_prepare_obj(&p->exec, &e->bo->tbo.base, - 1 + p->gang_size); + TTM_NUM_MOVE_FENCES + p->gang_size); drm_exec_retry_on_contention(&p->exec); if (unlikely(r)) goto out_free_user_pages; @@ -920,7 +919,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, if (p->uf_bo) { r = drm_exec_prepare_obj(&p->exec, &p->uf_bo->tbo.base, - 1 + p->gang_size); + TTM_NUM_MOVE_FENCES + p->gang_size); drm_exec_retry_on_contention(&p->exec); if (unlikely(r)) goto out_free_user_pages; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c index 5cef8cd14148..e54295b56282 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c @@ -328,11 +328,9 @@ static int amdgpu_vkms_prepare_fb(struct drm_plane *plane, return r; } - r = dma_resv_reserve_fences(rbo->tbo.base.resv, 1); - if (r) { - dev_err(adev->dev, "allocating fence slot failed (%d)\n", r); + r = dma_resv_reserve_fences(rbo->tbo.base.resv, TTM_NUM_MOVE_FENCES); + if (r) goto error_unlock; - } if (plane->type != DRM_PLANE_TYPE_CURSOR) domain = amdgpu_display_supported_domains(adev, rbo->flags); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 37fdcaf7192f..b120fdb0ef77 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -628,9 +628,8 @@ svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, } } - r = dma_resv_reserve_fences(bo->tbo.base.resv, 1); + r = dma_resv_reserve_fences(bo->tbo.base.resv, TTM_NUM_MOVE_FENCES); if (r) { - pr_debug("failed %d to reserve bo\n", r); amdgpu_bo_unreserve(bo); goto reserve_bo_failed; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 3ba71e2d259c..81e43534ec59 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -954,11 +954,9 @@ static int amdgpu_dm_plane_helper_prepare_fb(struct drm_plane *plane, return r; } - r = dma_resv_reserve_fences(rbo->tbo.base.resv, 1); - if (r) { - drm_err(adev_to_drm(adev), "reserving fence slot failed (%d)\n", r); + r = dma_resv_reserve_fences(rbo->tbo.base.resv, TTM_NUM_MOVE_FENCES); + if (r) goto error_unlock; - } if (plane->type != DRM_PLANE_TYPE_CURSOR) domain = amdgpu_display_supported_domains(adev, rbo->flags); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c index d9527c05fc87..110f0173eee6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c @@ -106,11 +106,9 @@ static int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector return r; } - r = dma_resv_reserve_fences(rbo->tbo.base.resv, 1); - if (r) { - drm_err(adev_to_drm(adev), "reserving fence slot failed (%d)\n", r); + r = dma_resv_reserve_fences(rbo->tbo.base.resv, TTM_NUM_MOVE_FENCES); + if (r) goto error_unlock; - } domain = amdgpu_display_supported_domains(adev, rbo->flags); From a3e14436304392fbada359edd0f1d1659850c9b7 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:39 +0530 Subject: [PATCH 485/712] drm/amdkfd: Fix queue preemption/eviction failures by aligning control stack size to GPU page size The control stack size is calculated based on the number of CUs and waves, and is then aligned to PAGE_SIZE. When the resulting control stack size is aligned to 64 KB, GPU hangs and queue preemption failures are observed while running RCCL unit tests on systems with more than two GPUs. amdgpu 0048:0f:00.0: amdgpu: Queue preemption failed for queue with doorbell_id: 80030008 amdgpu 0048:0f:00.0: amdgpu: Failed to evict process queues amdgpu 0048:0f:00.0: amdgpu: GPU reset begin!. Source: 4 amdgpu 0048:0f:00.0: amdgpu: Queue preemption failed for queue with doorbell_id: 80030008 amdgpu 0048:0f:00.0: amdgpu: Failed to evict process queues amdgpu 0048:0f:00.0: amdgpu: Failed to restore process queues This issue is observed on both 4 KB and 64 KB system page-size configurations. This patch fixes the issue by aligning the control stack size to AMDGPU_GPU_PAGE_SIZE instead of PAGE_SIZE, so the control stack size will not be 64 KB on systems with a 64 KB page size and queue preemption works correctly. Additionally, In the current code, wg_data_size is aligned to PAGE_SIZE, which can waste memory if the system page size is large. In this patch, wg_data_size is aligned to AMDGPU_GPU_PAGE_SIZE. The cwsr_size, calculated from wg_data_size and the control stack size, is aligned to PAGE_SIZE. Reviewed-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index e1a922bb2ab7..28354a4e5dd5 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -492,10 +492,11 @@ void kfd_queue_ctx_save_restore_size(struct kfd_topology_device *dev) cu_num = props->simd_count / props->simd_per_cu / NUM_XCC(dev->gpu->xcc_mask); wave_num = get_num_waves(props, gfxv, cu_num); - wg_data_size = ALIGN(cu_num * WG_CONTEXT_DATA_SIZE_PER_CU(gfxv, props), PAGE_SIZE); + wg_data_size = ALIGN(cu_num * WG_CONTEXT_DATA_SIZE_PER_CU(gfxv, props), + AMDGPU_GPU_PAGE_SIZE); ctl_stack_size = wave_num * CNTL_STACK_BYTES_PER_WAVE(gfxv) + 8; ctl_stack_size = ALIGN(SIZEOF_HSA_USER_CONTEXT_SAVE_AREA_HEADER + ctl_stack_size, - PAGE_SIZE); + AMDGPU_GPU_PAGE_SIZE); if ((gfxv / 10000 * 10000) == 100000) { /* HW design limits control stack size to 0x7000. @@ -507,7 +508,7 @@ void kfd_queue_ctx_save_restore_size(struct kfd_topology_device *dev) props->ctl_stack_size = ctl_stack_size; props->debug_memory_size = ALIGN(wave_num * DEBUGGER_BYTES_PER_WAVE, DEBUGGER_BYTES_ALIGN); - props->cwsr_size = ctl_stack_size + wg_data_size; + props->cwsr_size = ALIGN(ctl_stack_size + wg_data_size, PAGE_SIZE); if (gfxv == 80002) /* GFX_VERSION_TONGA */ props->eop_buffer_size = 0x8000; From 860fd1dd2d47d9d448e01ab39a9c4016cd068862 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Tue, 3 Feb 2026 11:22:12 +0100 Subject: [PATCH 486/712] drm/amdgpu: use multiple entities in amdgpu_move_blit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to "drm/ttm: rework pipelined eviction fence handling", ttm can deal correctly with moves and evictions being executed from different contexts. Signed-off-by: Pierre-Eric Pelloux-Prayer Acked-by: Felix Kuehling Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 13 +++++++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 375c3414b0a6..afaaab6496de 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -387,9 +387,11 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo, { struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev); struct amdgpu_bo *abo = ttm_to_amdgpu_bo(bo); + struct amdgpu_ttm_buffer_entity *entity; struct amdgpu_copy_mem src, dst; struct dma_fence *fence = NULL; int r; + u32 e; src.bo = bo; dst.bo = bo; @@ -398,8 +400,12 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo, src.offset = 0; dst.offset = 0; + e = atomic_inc_return(&adev->mman.next_move_entity) % + adev->mman.num_move_entities; + entity = &adev->mman.move_entities[e]; + r = amdgpu_ttm_copy_mem_to_mem(adev, - &adev->mman.move_entities[0], + entity, &src, &dst, new_mem->size, amdgpu_bo_encrypted(abo), @@ -411,9 +417,7 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo, if (old_mem->mem_type == TTM_PL_VRAM && (abo->flags & AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE)) { struct dma_fence *wipe_fence = NULL; - - r = amdgpu_fill_buffer(&adev->mman.move_entities[0], - abo, 0, NULL, &wipe_fence, + r = amdgpu_fill_buffer(entity, abo, 0, NULL, &wipe_fence, AMDGPU_KERNEL_JOB_ID_MOVE_BLIT); if (r) { goto error; @@ -2392,6 +2396,7 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable) } adev->mman.num_move_entities = num_move_entities; + atomic_set(&adev->mman.next_move_entity, 0); for (i = 0; i < num_move_entities; i++) { r = amdgpu_ttm_buffer_entity_init( &adev->mman.gtt_mgr, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index cf32db3defb1..3b1973611446 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -76,6 +76,7 @@ struct amdgpu_mman { atomic_t next_clear_entity; u32 num_clear_entities; struct amdgpu_ttm_buffer_entity move_entities[TTM_NUM_MOVE_FENCES]; + atomic_t next_move_entity; u32 num_move_entities; struct amdgpu_vram_mgr vram_mgr; From 9badc2a84e688be1275bb740942d5f6f51746908 Mon Sep 17 00:00:00 2001 From: Changwoo Min Date: Sun, 29 Mar 2026 16:36:15 +0900 Subject: [PATCH 487/712] PM: EM: Fix NULL pointer dereference when perf domain ID is not found dev_energymodel_nl_get_perf_domains_doit() calls em_perf_domain_get_by_id() but does not check the return value before passing it to __em_nl_get_pd_size(). When a caller supplies a non-existent perf domain ID, em_perf_domain_get_by_id() returns NULL, and __em_nl_get_pd_size() immediately dereferences pd->cpus (struct offset 0x30), causing a NULL pointer dereference. The sister handler dev_energymodel_nl_get_perf_table_doit() already handles this correctly via __em_nl_get_pd_table_id(), which returns NULL and causes the caller to return -EINVAL. Add the same NULL check in the get-perf-domains do handler. Fixes: 380ff27af25e ("PM: EM: Add dump to get-perf-domains in the EM YNL spec") Reported-by: Yi Lai Closes: https://lore.kernel.org/lkml/aXiySM79UYfk+ytd@ly-workstation/ Signed-off-by: Changwoo Min Cc: 6.19+ # 6.19+ [ rjw: Subject and changelog edits ] Link: https://patch.msgid.link/20260329073615.649976-1-changwoo@igalia.com Signed-off-by: Rafael J. Wysocki --- kernel/power/em_netlink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/power/em_netlink.c b/kernel/power/em_netlink.c index 5a611d3950fd..4d4fd29bd2be 100644 --- a/kernel/power/em_netlink.c +++ b/kernel/power/em_netlink.c @@ -109,6 +109,8 @@ int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb, id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID]); pd = em_perf_domain_get_by_id(id); + if (!pd) + return -EINVAL; __em_nl_get_pd_size(pd, &msg_sz); msg = genlmsg_new(msg_sz, GFP_KERNEL); From a3ffaa5b397f4df9d6ac16b10583e9df8e6fa471 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 17 Mar 2026 16:34:41 -0400 Subject: [PATCH 488/712] drm/amdgpu/pm: drop SMU driver if version not matched messages It just leads to user confusion. Cc: Yang Wang Cc: Lijo Lazar Reviewed-by: Yang Wang Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit e471627d56272a791972f25e467348b611c31713) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c | 1 - drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c | 1 - drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index 12b052d920f5..7ca8fdd23206 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -262,7 +262,6 @@ int smu_v11_0_check_fw_version(struct smu_context *smu) "smu fw program = %d, version = 0x%08x (%d.%d.%d)\n", smu->smc_driver_if_version, if_version, smu_program, smu_version, smu_major, smu_minor, smu_debug); - dev_info(smu->adev->dev, "SMU driver if version not matched\n"); } return ret; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c index 2c20624caca4..ac5e44dff6c9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c @@ -101,7 +101,6 @@ int smu_v12_0_check_fw_version(struct smu_context *smu) "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n", smu->smc_driver_if_version, if_version, smu_program, smu_version, smu_major, smu_minor, smu_debug); - dev_info(smu->adev->dev, "SMU driver if version not matched\n"); } return ret; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c index cec2df1ad0af..e38354c694c9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c @@ -284,7 +284,6 @@ int smu_v14_0_check_fw_version(struct smu_context *smu) "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n", smu->smc_driver_if_version, if_version, smu_program, smu_version, smu_major, smu_minor, smu_debug); - dev_info(adev->dev, "SMU driver if version not matched\n"); } return ret; From a018d1819f158991b7308e4f74609c6c029b670c Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 24 Mar 2026 17:39:02 +0800 Subject: [PATCH 489/712] drm/amdgpu: validate doorbell_offset in user queue creation amdgpu_userq_get_doorbell_index() passes the user-provided doorbell_offset to amdgpu_doorbell_index_on_bar() without bounds checking. An arbitrarily large doorbell_offset can cause the calculated doorbell index to fall outside the allocated doorbell BO, potentially corrupting kernel doorbell space. Validate that doorbell_offset falls within the doorbell BO before computing the BAR index, using u64 arithmetic to prevent overflow. Fixes: f09c1e6077ab ("drm/amdgpu: generate doorbell index for userqueue") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Signed-off-by: Alex Deucher (cherry picked from commit de1ef4ffd70e1d15f0bf584fd22b1f28cbd5e2ec) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 7c450350847d..0a1b93259887 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -600,6 +600,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, goto unpin_bo; } + /* Validate doorbell_offset is within the doorbell BO */ + if ((u64)db_info->doorbell_offset * db_size + db_size > + amdgpu_bo_size(db_obj->obj)) { + r = -EINVAL; + goto unpin_bo; + } + index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, db_info->doorbell_offset, db_size); drm_dbg_driver(adev_to_drm(uq_mgr->adev), From 62f553d60a801384336f5867967c26ddf3b17038 Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Mon, 23 Mar 2026 16:07:02 +0800 Subject: [PATCH 490/712] drm/amdgpu: fix the idr allocation flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the IDR allocation flags by using atomic GFP flags in non‑sleepable contexts to avoid the __might_sleep() complaint. 268.290239] [drm] Initialized amdgpu 3.64.0 for 0000:03:00.0 on minor 0 [ 268.294900] BUG: sleeping function called from invalid context at ./include/linux/sched/mm.h:323 [ 268.295355] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1744, name: modprobe [ 268.295705] preempt_count: 1, expected: 0 [ 268.295886] RCU nest depth: 0, expected: 0 [ 268.296072] 2 locks held by modprobe/1744: [ 268.296077] #0: ffff8c3a44abd1b8 (&dev->mutex){....}-{4:4}, at: __driver_attach+0xe4/0x210 [ 268.296100] #1: ffffffffc1a6ea78 (amdgpu_pasid_idr_lock){+.+.}-{3:3}, at: amdgpu_pasid_alloc+0x26/0xe0 [amdgpu] [ 268.296494] CPU: 12 UID: 0 PID: 1744 Comm: modprobe Tainted: G U OE 6.19.0-custom #16 PREEMPT(voluntary) [ 268.296498] Tainted: [U]=USER, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE [ 268.296499] Hardware name: AMD Majolica-RN/Majolica-RN, BIOS RMJ1009A 06/13/2021 [ 268.296501] Call Trace: Fixes: 8f1de51f49be ("drm/amdgpu: prevent immediate PASID reuse case") Tested-by: Borislav Petkov (AMD) Signed-off-by: Prike Liang Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit ea56aa2625708eaf96f310032391ff37746310ef) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index d88523568b62..569c5a89ff10 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -68,8 +68,11 @@ int amdgpu_pasid_alloc(unsigned int bits) return -EINVAL; spin_lock(&amdgpu_pasid_idr_lock); + /* TODO: Need to replace the idr with an xarry, and then + * handle the internal locking with ATOMIC safe paths. + */ pasid = idr_alloc_cyclic(&amdgpu_pasid_idr, NULL, 1, - 1U << bits, GFP_KERNEL); + 1U << bits, GFP_ATOMIC); spin_unlock(&amdgpu_pasid_idr_lock); if (pasid >= 0) From 68484a648ade555842e0c75a392f3572b3d51998 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:35 +0530 Subject: [PATCH 491/712] drm/amdkfd: Align expected_queue_size to PAGE_SIZE The AQL queue size can be 4K, but the minimum buffer object (BO) allocation size is PAGE_SIZE. On systems with a page size larger than 4K, the expected queue size does not match the allocated BO size, causing queue creation to fail. Align the expected queue size to PAGE_SIZE so that it matches the allocated BO size and allows queue creation to succeed. Reviewed-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit b01cd158a2f5230b137396c5f8cda3fc780abbc2) --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index bbe869ceae3f..e1a922bb2ab7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -249,10 +249,10 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope topo_dev->node_props.gfx_target_version < 90000) /* metadata_queue_size not supported on GFX7/GFX8 */ expected_queue_size = - properties->queue_size / 2; + PAGE_ALIGN(properties->queue_size / 2); else expected_queue_size = - properties->queue_size + properties->metadata_queue_size; + PAGE_ALIGN(properties->queue_size + properties->metadata_queue_size); vm = drm_priv_to_vm(pdd->drm_priv); err = amdgpu_bo_reserve(vm->root.bo, false); From 6caeace0d1471b33bb43b58893940cc90baca5b9 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:38 +0530 Subject: [PATCH 492/712] drm/amd: Fix MQD and control stack alignment for non-4K MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For gfxV9, due to a hardware bug ("based on the comments in the code here [1]"), the control stack of a user-mode compute queue must be allocated immediately after the page boundary of its regular MQD buffer. To handle this, we allocate an enlarged MQD buffer where the first page is used as the MQD and the remaining pages store the control stack. Although these regions share the same BO, they require different memory types: the MQD must be UC (uncached), while the control stack must be NC (non-coherent), matching the behavior when the control stack is allocated in user space. This logic works correctly on systems where the CPU page size matches the GPU page size (4K). However, the current implementation aligns both the MQD and the control stack to the CPU PAGE_SIZE. On systems with a larger CPU page size, the entire first CPU page is marked UC—even though that page may contain multiple GPU pages. The GPU treats the second 4K GPU page inside that CPU page as part of the control stack, but it is incorrectly mapped as UC. This patch fixes the issue by aligning both the MQD and control stack sizes to the GPU page size (4K). The first 4K page is correctly marked as UC for the MQD, and the remaining GPU pages are marked NC for the control stack. This ensures proper memory type assignment on systems with larger CPU page sizes. [1]: https://elixir.bootlin.com/linux/v6.18/source/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c#L118 Acked-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit 998d6781410de1c4b787fdbf6c56e851ea7fa553) --- drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c | 44 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 16 ++----- .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 23 ++++++---- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index e2d32c29668a..bc772ca3dab7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -403,6 +403,50 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, drm_dev_exit(idx); } +/** + * amdgpu_gart_map_gfx9_mqd - map mqd and ctrl_stack dma_addresses into GART entries + * + * @adev: amdgpu_device pointer + * @offset: offset into the GPU's gart aperture + * @pages: number of pages to bind + * @dma_addr: DMA addresses of pages + * @flags: page table entry flags + * + * Map the MQD and control stack addresses into GART entries with the correct + * memory types on gfxv9. The MQD occupies the first 4KB and is followed by + * the control stack. The MQD uses UC (uncached) memory, while the control stack + * uses NC (non-coherent) memory. + */ +void amdgpu_gart_map_gfx9_mqd(struct amdgpu_device *adev, uint64_t offset, + int pages, dma_addr_t *dma_addr, uint64_t flags) +{ + uint64_t page_base; + unsigned int i, j, t; + int idx; + uint64_t ctrl_flags = AMDGPU_PTE_MTYPE_VG10(flags, AMDGPU_MTYPE_NC); + void *dst; + + if (!adev->gart.ptr) + return; + + if (!drm_dev_enter(adev_to_drm(adev), &idx)) + return; + + t = offset / AMDGPU_GPU_PAGE_SIZE; + dst = adev->gart.ptr; + for (i = 0; i < pages; i++) { + page_base = dma_addr[i]; + for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) { + if ((i == 0) && (j == 0)) + amdgpu_gmc_set_pte_pde(adev, dst, t, page_base, flags); + else + amdgpu_gmc_set_pte_pde(adev, dst, t, page_base, ctrl_flags); + page_base += AMDGPU_GPU_PAGE_SIZE; + } + } + drm_dev_exit(idx); +} + /** * amdgpu_gart_bind - bind pages into the gart page table * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h index d3118275ddae..6ebd2da32ea6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h @@ -62,6 +62,8 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset, void amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset, int pages, dma_addr_t *dma_addr, uint64_t flags, void *dst); +void amdgpu_gart_map_gfx9_mqd(struct amdgpu_device *adev, uint64_t offset, + int pages, dma_addr_t *dma_addr, uint64_t flags); void amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset, int pages, dma_addr_t *dma_addr, uint64_t flags); void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index eeaa56c8d129..0ccb31788b20 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -853,25 +853,15 @@ static void amdgpu_ttm_gart_bind_gfx9_mqd(struct amdgpu_device *adev, int num_xcc = max(1U, adev->gfx.num_xcc_per_xcp); uint64_t page_idx, pages_per_xcc; int i; - uint64_t ctrl_flags = AMDGPU_PTE_MTYPE_VG10(flags, AMDGPU_MTYPE_NC); pages_per_xcc = total_pages; do_div(pages_per_xcc, num_xcc); for (i = 0, page_idx = 0; i < num_xcc; i++, page_idx += pages_per_xcc) { - /* MQD page: use default flags */ - amdgpu_gart_bind(adev, + amdgpu_gart_map_gfx9_mqd(adev, gtt->offset + (page_idx << PAGE_SHIFT), - 1, >t->ttm.dma_address[page_idx], flags); - /* - * Ctrl pages - modify the memory type to NC (ctrl_flags) from - * the second page of the BO onward. - */ - amdgpu_gart_bind(adev, - gtt->offset + ((page_idx + 1) << PAGE_SHIFT), - pages_per_xcc - 1, - >t->ttm.dma_address[page_idx + 1], - ctrl_flags); + pages_per_xcc, >t->ttm.dma_address[page_idx], + flags); } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index d5c234f30e8d..a535f151cb5f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -42,9 +42,16 @@ static uint64_t mqd_stride_v9(struct mqd_manager *mm, struct queue_properties *q) { if (mm->dev->kfd->cwsr_enabled && - q->type == KFD_QUEUE_TYPE_COMPUTE) - return ALIGN(q->ctl_stack_size, PAGE_SIZE) + - ALIGN(sizeof(struct v9_mqd), PAGE_SIZE); + q->type == KFD_QUEUE_TYPE_COMPUTE) { + + /* On gfxv9, the MQD resides in the first 4K page, + * followed by the control stack. Align both to + * AMDGPU_GPU_PAGE_SIZE to maintain the required 4K boundary. + */ + + return ALIGN(ALIGN(q->ctl_stack_size, AMDGPU_GPU_PAGE_SIZE) + + ALIGN(sizeof(struct v9_mqd), AMDGPU_GPU_PAGE_SIZE), PAGE_SIZE); + } return mm->mqd_size; } @@ -151,8 +158,8 @@ static struct kfd_mem_obj *allocate_mqd(struct mqd_manager *mm, if (!mqd_mem_obj) return NULL; retval = amdgpu_amdkfd_alloc_kernel_mem(node->adev, - (ALIGN(q->ctl_stack_size, PAGE_SIZE) + - ALIGN(sizeof(struct v9_mqd), PAGE_SIZE)) * + (ALIGN(ALIGN(q->ctl_stack_size, AMDGPU_GPU_PAGE_SIZE) + + ALIGN(sizeof(struct v9_mqd), AMDGPU_GPU_PAGE_SIZE), PAGE_SIZE)) * NUM_XCC(node->xcc_mask), mqd_on_vram(node->adev) ? AMDGPU_GEM_DOMAIN_VRAM : AMDGPU_GEM_DOMAIN_GTT, @@ -360,7 +367,7 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, struct kfd_context_save_area_header header; /* Control stack is located one page after MQD. */ - void *mqd_ctl_stack = (void *)((uintptr_t)mqd + PAGE_SIZE); + void *mqd_ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); @@ -397,7 +404,7 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi { struct v9_mqd *m; /* Control stack is located one page after MQD. */ - void *ctl_stack = (void *)((uintptr_t)mqd + PAGE_SIZE); + void *ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); @@ -443,7 +450,7 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd, *gart_addr = addr; /* Control stack is located one page after MQD. */ - ctl_stack = (void *)((uintptr_t)*mqd + PAGE_SIZE); + ctl_stack = (void *)((uintptr_t)*mqd + AMDGPU_GPU_PAGE_SIZE); memcpy(ctl_stack, ctl_stack_src, ctl_stack_size); m->cp_hqd_pq_doorbell_control = From ced5c30e47d1cd52d6ae40f809223a6286854086 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Sat, 14 Mar 2026 23:33:53 +0800 Subject: [PATCH 493/712] drm/amdgpu/userq: fix memory leak in MQD creation error paths In mes_userq_mqd_create(), the memdup_user() allocations for IP-specific MQD structs are not freed when subsequent VA validation fails. The goto free_mqd label only cleans up the MQD BO object and userq_props. Fix by adding kfree() before each goto free_mqd on VA validation failure in the COMPUTE, GFX, and SDMA branches. Fixes: 9e46b8bb0539 ("drm/amdgpu: validate userq buffer virtual address and size") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Reviewed-by: Prike Liang Signed-off-by: Alex Deucher (cherry picked from commit 27f5ff9e4a4150d7cf8b4085aedd3b77ddcc5d08) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 8c74894254f7..faac21ee5739 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -324,8 +324,10 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va, 2048); - if (r) + if (r) { + kfree(compute_mqd); goto free_mqd; + } userq_props->eop_gpu_addr = compute_mqd->eop_va; userq_props->hqd_pipe_priority = AMDGPU_GFX_PIPE_PRIO_NORMAL; @@ -365,12 +367,16 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, shadow_info.shadow_size); - if (r) + if (r) { + kfree(mqd_gfx_v11); goto free_mqd; + } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va, shadow_info.csa_size); - if (r) + if (r) { + kfree(mqd_gfx_v11); goto free_mqd; + } kfree(mqd_gfx_v11); } else if (queue->queue_type == AMDGPU_HW_IP_DMA) { @@ -390,8 +396,10 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, } r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va, 32); - if (r) + if (r) { + kfree(mqd_sdma_v11); goto free_mqd; + } userq_props->csa_addr = mqd_sdma_v11->csa_va; kfree(mqd_sdma_v11); From 4487571ef17a30d274600b3bd6965f497a881299 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Thu, 26 Mar 2026 17:51:28 +0530 Subject: [PATCH 494/712] drm/amdgpu: Change AMDGPU_VA_RESERVED_TRAP_SIZE to 64KB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, AMDGPU_VA_RESERVED_TRAP_SIZE is hardcoded to 8KB, while KFD_CWSR_TBA_TMA_SIZE is defined as 2 * PAGE_SIZE. On systems with 4K pages, both values match (8KB), so allocation and reserved space are consistent. However, on 64K page-size systems, KFD_CWSR_TBA_TMA_SIZE becomes 128KB, while the reserved trap area remains 8KB. This mismatch causes the kernel to crash when running rocminfo or rccl unit tests. Kernel attempted to read user page (2) - exploit attempt? (uid: 1001) BUG: Kernel NULL pointer dereference on read at 0x00000002 Faulting instruction address: 0xc0000000002c8a64 Oops: Kernel access of bad area, sig: 11 [#1] LE PAGE_SIZE=64K MMU=Radix SMP NR_CPUS=2048 NUMA pSeries CPU: 34 UID: 1001 PID: 9379 Comm: rocminfo Tainted: G E 6.19.0-rc4-amdgpu-00320-gf23176405700 #56 VOLUNTARY Tainted: [E]=UNSIGNED_MODULE Hardware name: IBM,9105-42A POWER10 (architected) 0x800200 0xf000006 of:IBM,FW1060.30 (ML1060_896) hv:phyp pSeries NIP: c0000000002c8a64 LR: c00000000125dbc8 CTR: c00000000125e730 REGS: c0000001e0957580 TRAP: 0300 Tainted: G E MSR: 8000000000009033 CR: 24008268 XER: 00000036 CFAR: c00000000125dbc4 DAR: 0000000000000002 DSISR: 40000000 IRQMASK: 1 GPR00: c00000000125d908 c0000001e0957820 c0000000016e8100 c00000013d814540 GPR04: 0000000000000002 c00000013d814550 0000000000000045 0000000000000000 GPR08: c00000013444d000 c00000013d814538 c00000013d814538 0000000084002268 GPR12: c00000000125e730 c000007e2ffd5f00 ffffffffffffffff 0000000000020000 GPR16: 0000000000000000 0000000000000002 c00000015f653000 0000000000000000 GPR20: c000000138662400 c00000013d814540 0000000000000000 c00000013d814500 GPR24: 0000000000000000 0000000000000002 c0000001e0957888 c0000001e0957878 GPR28: c00000013d814548 0000000000000000 c00000013d814540 c0000001e0957888 NIP [c0000000002c8a64] __mutex_add_waiter+0x24/0xc0 LR [c00000000125dbc8] __mutex_lock.constprop.0+0x318/0xd00 Call Trace: 0xc0000001e0957890 (unreliable) __mutex_lock.constprop.0+0x58/0xd00 amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu+0x6fc/0xb60 [amdgpu] kfd_process_alloc_gpuvm+0x54/0x1f0 [amdgpu] kfd_process_device_init_cwsr_dgpu+0xa4/0x1a0 [amdgpu] kfd_process_device_init_vm+0xd8/0x2e0 [amdgpu] kfd_ioctl_acquire_vm+0xd0/0x130 [amdgpu] kfd_ioctl+0x514/0x670 [amdgpu] sys_ioctl+0x134/0x180 system_call_exception+0x114/0x300 system_call_vectored_common+0x15c/0x2ec This patch changes AMDGPU_VA_RESERVED_TRAP_SIZE to 64 KB and KFD_CWSR_TBA_TMA_SIZE to the AMD GPU page size. This means we reserve 64 KB for the trap in the address space, but only allocate 8 KB within it. With this approach, the allocation size never exceeds the reserved area. Fixes: 34a1de0f7935 ("drm/amdkfd: Relocate TBA/TMA to opposite side of VM hole") Reviewed-by: Christian König Suggested-by: Felix Kuehling Suggested-by: Christian König Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit 31b8de5e55666f26ea7ece5f412b83eab3f56dbb) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index bb276c0ad06d..d5b7061556ba 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -173,7 +173,7 @@ struct amdgpu_bo_vm; #define AMDGPU_VA_RESERVED_SEQ64_SIZE (2ULL << 20) #define AMDGPU_VA_RESERVED_SEQ64_START(adev) (AMDGPU_VA_RESERVED_CSA_START(adev) \ - AMDGPU_VA_RESERVED_SEQ64_SIZE) -#define AMDGPU_VA_RESERVED_TRAP_SIZE (2ULL << 12) +#define AMDGPU_VA_RESERVED_TRAP_SIZE (1ULL << 16) #define AMDGPU_VA_RESERVED_TRAP_START(adev) (AMDGPU_VA_RESERVED_SEQ64_START(adev) \ - AMDGPU_VA_RESERVED_TRAP_SIZE) #define AMDGPU_VA_RESERVED_BOTTOM (1ULL << 16) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index e5b56412931b..035687a17d89 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -102,8 +102,8 @@ * The first chunk is the TBA used for the CWSR ISA code. The second * chunk is used as TMA for user-mode trap handler setup in daisy-chain mode. */ -#define KFD_CWSR_TBA_TMA_SIZE (PAGE_SIZE * 2) -#define KFD_CWSR_TMA_OFFSET (PAGE_SIZE + 2048) +#define KFD_CWSR_TBA_TMA_SIZE (AMDGPU_GPU_PAGE_SIZE * 2) +#define KFD_CWSR_TMA_OFFSET (AMDGPU_GPU_PAGE_SIZE + 2048) #define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE \ (KFD_MAX_NUM_OF_PROCESSES * \ From e927b36ae18b66b49219eaa9f46edc7b4fdbb25e Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Sat, 21 Mar 2026 17:25:14 +0530 Subject: [PATCH 495/712] drm/amd/display: Fix NULL pointer dereference in dcn401_init_hw() dcn401_init_hw() assumes that update_bw_bounding_box() is valid when entering the update path. However, the existing condition: ((!fams2_enable && update_bw_bounding_box) || freq_changed) does not guarantee this, as the freq_changed branch can evaluate to true independently of the callback pointer. This can result in calling update_bw_bounding_box() when it is NULL. Fix this by separating the update condition from the pointer checks and ensuring the callback, dc->clk_mgr, and bw_params are validated before use. Fixes the below: ../dc/hwss/dcn401/dcn401_hwseq.c:367 dcn401_init_hw() error: we previously assumed 'dc->res_pool->funcs->update_bw_bounding_box' could be null (see line 362) Fixes: ca0fb243c3bb ("drm/amd/display: Underflow Seen on DCN401 eGPU") Cc: Daniel Sa Cc: Alvin Lee Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Dan Carpenter Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher (cherry picked from commit 86117c5ab42f21562fedb0a64bffea3ee5fcd477) Cc: stable@vger.kernel.org --- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index eb198d52a115..4dfb6c865831 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -147,6 +147,7 @@ void dcn401_init_hw(struct dc *dc) int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; + bool dchub_ref_freq_changed; int current_dchub_ref_freq = 0; if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) { @@ -360,14 +361,18 @@ void dcn401_init_hw(struct dc *dc) dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr; dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0; dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver; + + /* sw and fw FAMS versions must match for support */ dc->debug.fams2_config.bits.enable &= - dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; // sw & fw fams versions must match for support - if ((!dc->debug.fams2_config.bits.enable && dc->res_pool->funcs->update_bw_bounding_box) - || res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq) { + dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; + dchub_ref_freq_changed = + res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq; + if ((!dc->debug.fams2_config.bits.enable || dchub_ref_freq_changed) && + dc->res_pool->funcs->update_bw_bounding_box && + dc->clk_mgr && dc->clk_mgr->bw_params) { /* update bounding box if FAMS2 disabled, or if dchub clk has changed */ - if (dc->clk_mgr) - dc->res_pool->funcs->update_bw_bounding_box(dc, - dc->clk_mgr->bw_params); + dc->res_pool->funcs->update_bw_bounding_box(dc, + dc->clk_mgr->bw_params); } } } From 39e2a5bf970402a8530a319cf06122e216ba57b8 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Thu, 26 Mar 2026 22:45:23 +0000 Subject: [PATCH 496/712] hwmon: (occ) Fix division by zero in occ_show_power_1() In occ_show_power_1() case 1, the accumulator is divided by update_tag without checking for zero. If no samples have been collected yet (e.g. during early boot when the sensor block is included but hasn't been updated), update_tag is zero, causing a kernel divide-by-zero crash. The 2019 fix in commit 211186cae14d ("hwmon: (occ) Fix division by zero issue") only addressed occ_get_powr_avg() used by occ_show_power_2() and occ_show_power_a0(). This separate code path in occ_show_power_1() was missed. Fix this by reusing the existing occ_get_powr_avg() helper, which already handles the zero-sample case and uses mul_u64_u32_div() to multiply before dividing for better precision. Move the helper above occ_show_power_1() so it is visible at the call site. Fixes: c10e753d43eb ("hwmon (occ): Add sensor types and versions") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260326224510.294619-2-sanman.pradhan@hpe.com [groeck: Fix alignment problems reported by checkpatch] Signed-off-by: Guenter Roeck --- drivers/hwmon/occ/common.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 89928d38831b..ec7dbb94de0b 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -420,6 +420,12 @@ static ssize_t occ_show_freq_2(struct device *dev, return sysfs_emit(buf, "%u\n", val); } +static u64 occ_get_powr_avg(u64 accum, u32 samples) +{ + return (samples == 0) ? 0 : + mul_u64_u32_div(accum, 1000000UL, samples); +} + static ssize_t occ_show_power_1(struct device *dev, struct device_attribute *attr, char *buf) { @@ -441,9 +447,8 @@ static ssize_t occ_show_power_1(struct device *dev, val = get_unaligned_be16(&power->sensor_id); break; case 1: - val = get_unaligned_be32(&power->accumulator) / - get_unaligned_be32(&power->update_tag); - val *= 1000000ULL; + val = occ_get_powr_avg(get_unaligned_be32(&power->accumulator), + get_unaligned_be32(&power->update_tag)); break; case 2: val = (u64)get_unaligned_be32(&power->update_tag) * @@ -459,12 +464,6 @@ static ssize_t occ_show_power_1(struct device *dev, return sysfs_emit(buf, "%llu\n", val); } -static u64 occ_get_powr_avg(u64 accum, u32 samples) -{ - return (samples == 0) ? 0 : - mul_u64_u32_div(accum, 1000000UL, samples); -} - static ssize_t occ_show_power_2(struct device *dev, struct device_attribute *attr, char *buf) { From daf470b8882b6f7f53cbfe9ec2b93a1b21528cdc Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Fri, 27 Mar 2026 14:29:17 +0530 Subject: [PATCH 497/712] drm/amdgpu: Fix wait after reset sequence in S4 For a mode-1 reset done at the end of S4 on PSPv11 dGPUs, only check if TOS is unloaded. Fixes: 32f73741d6ee ("drm/amdgpu: Wait for bootloader after PSPv11 reset") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/4853 Signed-off-by: Lijo Lazar Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2fb4883b884a437d760bd7bdf7695a7e5a60bba3) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 8 ++++++-- drivers/gpu/drm/amd/amdgpu/psp_v11_0.c | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 95d26f086d54..c91638e65174 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2703,8 +2703,12 @@ static int amdgpu_pmops_freeze(struct device *dev) if (r) return r; - if (amdgpu_acpi_should_gpu_reset(adev)) - return amdgpu_asic_reset(adev); + if (amdgpu_acpi_should_gpu_reset(adev)) { + amdgpu_device_lock_reset_domain(adev->reset_domain); + r = amdgpu_asic_reset(adev); + amdgpu_device_unlock_reset_domain(adev->reset_domain); + return r; + } return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c index 9aa988982304..fb7aaf5ae05c 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c @@ -170,7 +170,8 @@ static int psp_v11_0_wait_for_bootloader(struct psp_context *psp) int retry_loop; /* For a reset done at the end of S3, only wait for TOS to be unloaded */ - if (adev->in_s3 && !(adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev)) + if ((adev->in_s4 || adev->in_s3) && !(adev->flags & AMD_IS_APU) && + amdgpu_in_reset(adev)) return psp_v11_wait_for_tos_unload(psp); for (retry_loop = 0; retry_loop < 20; retry_loop++) { From 09773978879ecf71a7990fe9a28ce4eb92bce645 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Thu, 26 Mar 2026 22:45:29 +0000 Subject: [PATCH 498/712] hwmon: (occ) Fix missing newline in occ_show_extended() In occ_show_extended() case 0, when the EXTN_FLAG_SENSOR_ID flag is set, the sysfs_emit format string "%u" is missing the trailing newline that the sysfs ABI expects. The else branch correctly uses "%4phN\n", and all other show functions in this file include the trailing newline. Add the missing "\n" for consistency and correct sysfs output. Fixes: c10e753d43eb ("hwmon (occ): Add sensor types and versions") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260326224510.294619-3-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/occ/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index ec7dbb94de0b..42cc6068bb08 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -724,7 +724,7 @@ static ssize_t occ_show_extended(struct device *dev, switch (sattr->nr) { case 0: if (extn->flags & EXTN_FLAG_SENSOR_ID) { - rc = sysfs_emit(buf, "%u", + rc = sysfs_emit(buf, "%u\n", get_unaligned_be32(&extn->sensor_id)); } else { rc = sysfs_emit(buf, "%4phN\n", extn->name); From 78746a474e92fc7aaed12219bec7c78ae1bd6156 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:39 +0530 Subject: [PATCH 499/712] drm/amdkfd: Fix queue preemption/eviction failures by aligning control stack size to GPU page size The control stack size is calculated based on the number of CUs and waves, and is then aligned to PAGE_SIZE. When the resulting control stack size is aligned to 64 KB, GPU hangs and queue preemption failures are observed while running RCCL unit tests on systems with more than two GPUs. amdgpu 0048:0f:00.0: amdgpu: Queue preemption failed for queue with doorbell_id: 80030008 amdgpu 0048:0f:00.0: amdgpu: Failed to evict process queues amdgpu 0048:0f:00.0: amdgpu: GPU reset begin!. Source: 4 amdgpu 0048:0f:00.0: amdgpu: Queue preemption failed for queue with doorbell_id: 80030008 amdgpu 0048:0f:00.0: amdgpu: Failed to evict process queues amdgpu 0048:0f:00.0: amdgpu: Failed to restore process queues This issue is observed on both 4 KB and 64 KB system page-size configurations. This patch fixes the issue by aligning the control stack size to AMDGPU_GPU_PAGE_SIZE instead of PAGE_SIZE, so the control stack size will not be 64 KB on systems with a 64 KB page size and queue preemption works correctly. Additionally, In the current code, wg_data_size is aligned to PAGE_SIZE, which can waste memory if the system page size is large. In this patch, wg_data_size is aligned to AMDGPU_GPU_PAGE_SIZE. The cwsr_size, calculated from wg_data_size and the control stack size, is aligned to PAGE_SIZE. Reviewed-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit a3e14436304392fbada359edd0f1d1659850c9b7) --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index e1a922bb2ab7..28354a4e5dd5 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -492,10 +492,11 @@ void kfd_queue_ctx_save_restore_size(struct kfd_topology_device *dev) cu_num = props->simd_count / props->simd_per_cu / NUM_XCC(dev->gpu->xcc_mask); wave_num = get_num_waves(props, gfxv, cu_num); - wg_data_size = ALIGN(cu_num * WG_CONTEXT_DATA_SIZE_PER_CU(gfxv, props), PAGE_SIZE); + wg_data_size = ALIGN(cu_num * WG_CONTEXT_DATA_SIZE_PER_CU(gfxv, props), + AMDGPU_GPU_PAGE_SIZE); ctl_stack_size = wave_num * CNTL_STACK_BYTES_PER_WAVE(gfxv) + 8; ctl_stack_size = ALIGN(SIZEOF_HSA_USER_CONTEXT_SAVE_AREA_HEADER + ctl_stack_size, - PAGE_SIZE); + AMDGPU_GPU_PAGE_SIZE); if ((gfxv / 10000 * 10000) == 100000) { /* HW design limits control stack size to 0x7000. @@ -507,7 +508,7 @@ void kfd_queue_ctx_save_restore_size(struct kfd_topology_device *dev) props->ctl_stack_size = ctl_stack_size; props->debug_memory_size = ALIGN(wave_num * DEBUGGER_BYTES_PER_WAVE, DEBUGGER_BYTES_ALIGN); - props->cwsr_size = ctl_stack_size + wg_data_size; + props->cwsr_size = ALIGN(ctl_stack_size + wg_data_size, PAGE_SIZE); if (gfxv == 80002) /* GFX_VERSION_TONGA */ props->eop_buffer_size = 0x8000; From 59e1be1278f064d7172b00473b7e0c453cb1ec52 Mon Sep 17 00:00:00 2001 From: Emanuele Ghidoli Date: Fri, 13 Mar 2026 14:52:31 +0100 Subject: [PATCH 500/712] spi: cadence-qspi: Fix exec_mem_op error handling cqspi_exec_mem_op() increments the runtime PM usage counter before all refcount checks are performed. If one of these checks fails, the function returns without dropping the PM reference. Move the pm_runtime_resume_and_get() call after the refcount checks so that runtime PM is only acquired when the operation can proceed and drop the inflight_ops refcount if the PM resume fails. Cc: stable@vger.kernel.org Fixes: 7446284023e8 ("spi: cadence-quadspi: Implement refcount to handle unbind during busy") Signed-off-by: Emanuele Ghidoli Link: https://patch.msgid.link/20260313135236.46642-1-ghidoliemanuele@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 5fb0cb07c110..2ead419e896e 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1483,14 +1483,6 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) if (refcount_read(&cqspi->inflight_ops) == 0) return -ENODEV; - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(&mem->spi->dev, "resume failed with %d\n", ret); - return ret; - } - } - if (!refcount_read(&cqspi->refcount)) return -EBUSY; @@ -1502,6 +1494,14 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) return -EBUSY; } + if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(&mem->spi->dev, "resume failed with %d\n", ret); + goto dec_inflight_refcount; + } + } + ret = cqspi_mem_process(mem, op); if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) @@ -1510,6 +1510,7 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) if (ret) dev_err(&mem->spi->dev, "operation failed with %d\n", ret); +dec_inflight_refcount: if (refcount_read(&cqspi->inflight_ops) > 1) refcount_dec(&cqspi->inflight_ops); From 894f0d34d66cb47fe718fe2ae5c18729d22c5218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:02:58 +0200 Subject: [PATCH 501/712] drm/amd/pm/ci: Use highest MCLK on CI when MCLK DPM is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When MCLK DPM is disabled for any reason, populate the MCLK table with the highest MCLK DPM level, so that the ASIC can use the highest possible memory clock to get good performance even when MCLK DPM is disabled. Fixes: 9f4b35411cfe ("drm/amd/powerplay: add CI asics support to smumgr (v3)") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c index 62ebec1c6fe3..a09ca13df399 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c @@ -1322,6 +1322,14 @@ static int ci_populate_all_memory_levels(struct pp_hwmgr *hwmgr) return result; } + if (data->mclk_dpm_key_disabled && dpm_table->mclk_table.count) { + /* Populate the table with the highest MCLK level when MCLK DPM is disabled */ + for (i = 0; i < dpm_table->mclk_table.count - 1; i++) { + levels[i] = levels[dpm_table->mclk_table.count - 1]; + levels[i].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH; + } + } + smu_data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1; dev_id = adev->pdev->device; From 9851f29cb06c09f7dad3867d8b0feec3fc71b6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:02:59 +0200 Subject: [PATCH 502/712] drm/amd/pm/ci: Disable MCLK DPM on problematic CI ASICs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two known cases where MCLK DPM can causes issues: Radeon R9 M380 found in iMac computers from 2015. The SMU in this GPU just hangs as soon as we send it the PPSMC_MSG_MCLKDPM_Enable command, even when MCLK switching is disabled, and even when we only populate one MCLK DPM level. Apply workaround to all devices with the same subsystem ID. Radeon R7 260X due to old memory controller microcode. We only flash the MC ucode when it isn't set up by the VBIOS, therefore there is no way to make sure that it has the correct ucode version. I verified that this patch fixes the SMU hang on the R9 M380 which would previously fail to boot. This also fixes the UVD initialization error on that GPU which happened because the SMU couldn't ungate the UVD after it hung. Fixes: 86457c3b21cb ("drm/amd/powerplay: Add support for CI asics to hwmgr") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c index 2b5ac21fee39..1d6e30269d56 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c @@ -104,6 +104,21 @@ int hwmgr_early_init(struct pp_hwmgr *hwmgr) PP_GFXOFF_MASK); hwmgr->pp_table_version = PP_TABLE_V0; hwmgr->od_enabled = false; + switch (hwmgr->chip_id) { + case CHIP_BONAIRE: + /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM + * R7 260X cards with old MC ucode: MCLK DPM is unstable + */ + if (adev->pdev->subsystem_vendor == 0x106B || + adev->pdev->device == 0x6658) { + dev_info(adev->dev, "disabling MCLK DPM on quirky ASIC"); + adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK; + hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK; + } + break; + default: + break; + } smu7_init_function_pointers(hwmgr); break; case AMDGPU_FAMILY_CZ: From 0138610c14130425be53423b35336561829965e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:03:00 +0200 Subject: [PATCH 503/712] drm/amd/pm/smu7: Fix SMU7 voltage dependency on display clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DCE (display controller engine) requires a minimum voltage in order to function correctly, depending on which clock level it currently uses. Add a new table that contains display clock frequency levels and the corresponding required voltages. The clock frequency levels are taken from DC (and the old radeon driver's voltage dependency table for CI in cases where its values were lower). The voltage levels are taken from the following function: phm_initializa_dynamic_state_adjustment_rule_settings(). Furthermore, in case of CI, call smu7_patch_vddc() on the new table to account for leakage voltage (like in radeon). Use the display clock value from amd_pp_display_configuration to look up the voltage level needed by the DCE. Send the voltage to the SMU via the PPSMC_MSG_VddC_Request command. The previous implementation of this feature was non-functional because it relied on a "dal_power_level" field which was never assigned; and it was not at all implemented for CI ASICs. I verified this on a Radeon R9 M380 which previously booted to a black screen with DC enabled (default since Linux 6.19), but now works correctly. Fixes: 599a7e9fe1b6 ("drm/amd/powerplay: implement smu7 hwmgr to manager asics with smu ip version 7.") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- .../drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 88 ++++++++++++++++++- drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h | 1 + 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index e38222877f7e..563482f5d35f 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -2802,6 +2802,10 @@ static int smu7_patch_dependency_tables_with_leakage(struct pp_hwmgr *hwmgr) if (tmp) return -EINVAL; + tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dependency_on_display_clock); + if (tmp) + return -EINVAL; + tmp = smu7_patch_vce_vddc(hwmgr, hwmgr->dyn_state.vce_clock_voltage_dependency_table); if (tmp) return -EINVAL; @@ -2885,6 +2889,8 @@ static int smu7_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) { kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl); hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; + kfree(hwmgr->dyn_state.vddc_dependency_on_display_clock); + hwmgr->dyn_state.vddc_dependency_on_display_clock = NULL; kfree(hwmgr->backend); hwmgr->backend = NULL; @@ -2955,6 +2961,51 @@ static int smu7_update_edc_leakage_table(struct pp_hwmgr *hwmgr) return ret; } +static int smu7_init_voltage_dependency_on_display_clock_table(struct pp_hwmgr *hwmgr) +{ + struct phm_clock_voltage_dependency_table *table; + + if (!amdgpu_device_ip_get_ip_block(hwmgr->adev, AMD_IP_BLOCK_TYPE_DCE)) + return 0; + + table = kzalloc(struct_size(table, entries, 4), GFP_KERNEL); + if (!table) + return -ENOMEM; + + if (hwmgr->chip_id >= CHIP_POLARIS10) { + table->entries[0].clk = 38918; + table->entries[1].clk = 45900; + table->entries[2].clk = 66700; + table->entries[3].clk = 113200; + + table->entries[0].v = 700; + table->entries[1].v = 740; + table->entries[2].v = 800; + table->entries[3].v = 900; + } else { + if (hwmgr->chip_family == AMDGPU_FAMILY_CZ) { + table->entries[0].clk = 35200; + table->entries[1].clk = 35200; + table->entries[2].clk = 46700; + table->entries[3].clk = 64300; + } else { + table->entries[0].clk = 0; + table->entries[1].clk = 35200; + table->entries[2].clk = 54000; + table->entries[3].clk = 62500; + } + + table->entries[0].v = 0; + table->entries[1].v = 720; + table->entries[2].v = 810; + table->entries[3].v = 900; + } + + table->count = 4; + hwmgr->dyn_state.vddc_dependency_on_display_clock = table; + return 0; +} + static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) { struct amdgpu_device *adev = hwmgr->adev; @@ -2983,6 +3034,10 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) smu7_get_elb_voltages(hwmgr); } + result = smu7_init_voltage_dependency_on_display_clock_table(hwmgr); + if (result) + goto fail; + if (hwmgr->pp_table_version == PP_TABLE_V1) { smu7_complete_dependency_tables(hwmgr); smu7_set_private_data_based_on_pptable_v1(hwmgr); @@ -3079,13 +3134,40 @@ static int smu7_force_dpm_highest(struct pp_hwmgr *hwmgr) return 0; } +static uint32_t smu7_lookup_vddc_from_dispclk(struct pp_hwmgr *hwmgr) +{ + const struct amd_pp_display_configuration *cfg = hwmgr->display_config; + const struct phm_clock_voltage_dependency_table *vddc_dep_on_dispclk = + hwmgr->dyn_state.vddc_dependency_on_display_clock; + uint32_t i; + + if (!vddc_dep_on_dispclk || !vddc_dep_on_dispclk->count || + !cfg || !cfg->num_display || !cfg->display_clk) + return 0; + + /* Start from 1 because ClocksStateUltraLow should not be used according to DC. */ + for (i = 1; i < vddc_dep_on_dispclk->count; ++i) + if (vddc_dep_on_dispclk->entries[i].clk >= cfg->display_clk) + return vddc_dep_on_dispclk->entries[i].v; + + return vddc_dep_on_dispclk->entries[vddc_dep_on_dispclk->count - 1].v; +} + +static void smu7_apply_minimum_dce_voltage_request(struct pp_hwmgr *hwmgr) +{ + uint32_t req_vddc = smu7_lookup_vddc_from_dispclk(hwmgr); + + smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_VddC_Request, + req_vddc * VOLTAGE_SCALE, + NULL); +} + static int smu7_upload_dpm_level_enable_mask(struct pp_hwmgr *hwmgr) { struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); - if (hwmgr->pp_table_version == PP_TABLE_V1) - phm_apply_dal_min_voltage_request(hwmgr); -/* TO DO for v0 iceland and Ci*/ + smu7_apply_minimum_dce_voltage_request(hwmgr); if (!data->sclk_dpm_key_disabled) { if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index c661185753b4..2f49c95342a1 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -631,6 +631,7 @@ struct phm_dynamic_state_info { struct phm_clock_voltage_dependency_table *vddci_dependency_on_mclk; struct phm_clock_voltage_dependency_table *vddc_dependency_on_mclk; struct phm_clock_voltage_dependency_table *mvdd_dependency_on_mclk; + struct phm_clock_voltage_dependency_table *vddc_dependency_on_display_clock; struct phm_clock_voltage_dependency_table *vddc_dep_on_dal_pwrl; struct phm_clock_array *valid_sclk_values; struct phm_clock_array *valid_mclk_values; From 9f49e3d4cb86859a4e5fde3f2060ed4f09bddaed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:03:01 +0200 Subject: [PATCH 504/712] drm/amd/pm/smu7: Remove non-functional SMU7 voltage dependency on DAL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It looks like this was written for an old version of DC (DAL) and was never adapted afterwards. This was non-functional because it relied on the "dal_power_level" field which was never assigned anywhere in the code base. Also, it was not implemented for CI ASICs. Now superseded by the newer voltage dependency on display clock table added by the previous commit, let's remove. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- .../drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 9 -- .../drm/amd/pm/powerplay/hwmgr/smu_helper.c | 83 ------------------- .../drm/amd/pm/powerplay/hwmgr/smu_helper.h | 2 - drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h | 1 - 4 files changed, 95 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 563482f5d35f..2f3090182267 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -2794,10 +2794,6 @@ static int smu7_patch_dependency_tables_with_leakage(struct pp_hwmgr *hwmgr) if (tmp) return -EINVAL; - tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dep_on_dal_pwrl); - if (tmp) - return -EINVAL; - tmp = smu7_patch_vddci(hwmgr, hwmgr->dyn_state.vddci_dependency_on_mclk); if (tmp) return -EINVAL; @@ -2887,8 +2883,6 @@ static int smu7_set_private_data_based_on_pptable_v0(struct pp_hwmgr *hwmgr) static int smu7_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) { - kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl); - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; kfree(hwmgr->dyn_state.vddc_dependency_on_display_clock); hwmgr->dyn_state.vddc_dependency_on_display_clock = NULL; kfree(hwmgr->backend); @@ -3046,9 +3040,6 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) smu7_set_private_data_based_on_pptable_v0(hwmgr); } - /* Initalize Dynamic State Adjustment Rule Settings */ - result = phm_initializa_dynamic_state_adjustment_rule_settings(hwmgr); - if (result) goto fail; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c index 40ecaac6c604..30d83e18db40 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c @@ -484,52 +484,6 @@ int phm_get_sclk_for_voltage_evv(struct pp_hwmgr *hwmgr, return 0; } -/** - * phm_initializa_dynamic_state_adjustment_rule_settings - Initialize Dynamic State Adjustment Rule Settings - * - * @hwmgr: the address of the powerplay hardware manager. - */ -int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr) -{ - struct phm_clock_voltage_dependency_table *table_clk_vlt; - struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable); - - /* initialize vddc_dep_on_dal_pwrl table */ - table_clk_vlt = kzalloc_flex(*table_clk_vlt, entries, 4); - - if (NULL == table_clk_vlt) { - pr_err("Can not allocate space for vddc_dep_on_dal_pwrl! \n"); - return -ENOMEM; - } else { - table_clk_vlt->count = 4; - table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_ULTRALOW; - if (hwmgr->chip_id >= CHIP_POLARIS10 && - hwmgr->chip_id <= CHIP_VEGAM) - table_clk_vlt->entries[0].v = 700; - else - table_clk_vlt->entries[0].v = 0; - table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_LOW; - if (hwmgr->chip_id >= CHIP_POLARIS10 && - hwmgr->chip_id <= CHIP_VEGAM) - table_clk_vlt->entries[1].v = 740; - else - table_clk_vlt->entries[1].v = 720; - table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_NOMINAL; - if (hwmgr->chip_id >= CHIP_POLARIS10 && - hwmgr->chip_id <= CHIP_VEGAM) - table_clk_vlt->entries[2].v = 800; - else - table_clk_vlt->entries[2].v = 810; - table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_PERFORMANCE; - table_clk_vlt->entries[3].v = 900; - if (pptable_info != NULL) - pptable_info->vddc_dep_on_dal_pwrl = table_clk_vlt; - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt; - } - - return 0; -} - uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask) { uint32_t level = 0; @@ -540,43 +494,6 @@ uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask) return level; } -void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr) -{ - struct phm_ppt_v1_information *table_info = - (struct phm_ppt_v1_information *)hwmgr->pptable; - struct phm_clock_voltage_dependency_table *table = - table_info->vddc_dep_on_dal_pwrl; - struct phm_ppt_v1_clock_voltage_dependency_table *vddc_table; - enum PP_DAL_POWERLEVEL dal_power_level = hwmgr->dal_power_level; - uint32_t req_vddc = 0, req_volt, i; - - if (!table || table->count <= 0 - || dal_power_level < PP_DAL_POWERLEVEL_ULTRALOW - || dal_power_level > PP_DAL_POWERLEVEL_PERFORMANCE) - return; - - for (i = 0; i < table->count; i++) { - if (dal_power_level == table->entries[i].clk) { - req_vddc = table->entries[i].v; - break; - } - } - - vddc_table = table_info->vdd_dep_on_sclk; - for (i = 0; i < vddc_table->count; i++) { - if (req_vddc <= vddc_table->entries[i].vddc) { - req_volt = (((uint32_t)vddc_table->entries[i].vddc) * VOLTAGE_SCALE); - smum_send_msg_to_smc_with_parameter(hwmgr, - PPSMC_MSG_VddC_Request, - req_volt, - NULL); - return; - } - } - pr_err("DAL requested level can not" - " found a available voltage in VDDC DPM Table \n"); -} - int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint32_t sclk, uint16_t id, uint16_t *voltage) { diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h index 83b3c9315143..d370bfd0764d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h @@ -87,9 +87,7 @@ extern uint16_t phm_find_closest_vddci(struct pp_atomctrl_voltage_table *vddci_t extern int phm_find_boot_level(void *table, uint32_t value, uint32_t *boot_level); extern int phm_get_sclk_for_voltage_evv(struct pp_hwmgr *hwmgr, phm_ppt_v1_voltage_lookup_table *lookup_table, uint16_t virtual_voltage_id, int32_t *sclk); -extern int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr); extern uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask); -extern void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr); extern int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint32_t sclk, uint16_t id, uint16_t *voltage); diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index 2f49c95342a1..3ae45eac0c5c 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -773,7 +773,6 @@ struct pp_hwmgr { const struct pp_smumgr_func *smumgr_funcs; bool is_kicker; - enum PP_DAL_POWERLEVEL dal_power_level; struct phm_dynamic_state_info dyn_state; const struct pp_hwmgr_func *hwmgr_func; const struct pp_table_func *pptable_func; From d784759c07924280f3c313f205fc48eb62d7cb71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:03:02 +0200 Subject: [PATCH 505/712] drm/amd/pm/ci: Fix powertune defaults for Hawaii 0x67B0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no AMD GPU with the ID 0x66B0, this looks like a typo. It should be 0x67B0 which is actually part of the PCI ID list, and should use the Hawaii XT powertune defaults according to the old radeon driver. Fixes: 9f4b35411cfe ("drm/amd/powerplay: add CI asics support to smumgr (v3)") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c index a09ca13df399..b474561357e0 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c @@ -245,7 +245,7 @@ static void ci_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr) smu_data->power_tune_defaults = &defaults_hawaii_pro; break; case 0x67B8: - case 0x66B0: + case 0x67B0: smu_data->power_tune_defaults = &defaults_hawaii_xt; break; case 0x6640: From 5facfd4c4c67e8500116ffec0d9da35d92b9c787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:03:03 +0200 Subject: [PATCH 506/712] drm/amd/pm/ci: Clear EnabledForActivity field for memory levels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow what radeon did and what amdgpu does for other GPUs with SMU7. Fixes: 9f4b35411cfe ("drm/amd/powerplay: add CI asics support to smumgr (v3)") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c index b474561357e0..fa21a04fd7f0 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c @@ -1217,7 +1217,7 @@ static int ci_populate_single_memory_level( } memory_level->EnabledForThrottle = 1; - memory_level->EnabledForActivity = 1; + memory_level->EnabledForActivity = 0; memory_level->UpH = data->current_profile_setting.mclk_up_hyst; memory_level->DownH = data->current_profile_setting.mclk_down_hyst; memory_level->VoltageDownH = 0; From baf28ec5795c077406d6f52b8ad39e614153bce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:03:04 +0200 Subject: [PATCH 507/712] drm/amd/pm/ci: Fill DW8 fields from SMC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ci_populate_dw8() we currently just read a value from the SMU and then throw it away. Instead of throwing away the value, we should use it to fill other fields in DW8 (like radeon). Otherwise the value of the other fiels is just cleared when we copy this data to the SMU later. Fixes: 9f4b35411cfe ("drm/amd/powerplay: add CI asics support to smumgr (v3)") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c index fa21a04fd7f0..731355bdb9bc 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c @@ -543,12 +543,11 @@ static int ci_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset) { struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend); const struct ci_pt_defaults *defaults = smu_data->power_tune_defaults; - uint32_t temp; if (ci_read_smc_sram_dword(hwmgr, fuse_table_offset + offsetof(SMU7_Discrete_PmFuses, TdcWaterfallCtl), - (uint32_t *)&temp, SMC_RAM_END)) + (uint32_t *)&smu_data->power_tune_table.TdcWaterfallCtl, SMC_RAM_END)) PP_ASSERT_WITH_CODE(false, "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!", return -EINVAL); From 4724bc5b8d78c34b993594f9406135408ccb312a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:03:05 +0200 Subject: [PATCH 508/712] drm/amd/pm/smu7: Add SCLK cap for quirky Hawaii board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a specific Radeon R9 390X board, the GPU can "randomly" hang while gaming. Initially I thought this was a RADV bug and tried to work around this in Mesa: commit 8ea08747b86b ("radv: Mitigate GPU hang on Hawaii in Dota 2 and RotTR") However, I got some feedback from other users who are reporting that the above mitigation causes a significant performance regression for them, and they didn't experience the hang on their GPU in the first place. After some further investigation, it turns out that the problem is that the highest SCLK DPM level on this board isn't stable. Lowering SCLK to 1040 MHz (from 1070 MHz) works around the issue, and has a negligible impact on performance compared to the Mesa patch. (Note that increasing the voltage can also work around it, but we felt that lowering the SCLK is the safer option.) To solve the above issue, add an "sclk_cap" field to smu7_hwmgr and set this field for the affected board. The capped SCLK value correctly appears on the sysfs interface and shows up in GUI tools such as LACT. Fixes: 9f4b35411cfe ("drm/amd/powerplay: add CI asics support to smumgr (v3)") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- .../drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 30 ++++++++++++++++--- .../drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h | 1 + 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 2f3090182267..8c37aa452569 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -787,7 +787,7 @@ static int smu7_setup_dpm_tables_v0(struct pp_hwmgr *hwmgr) hwmgr->dyn_state.vddc_dependency_on_mclk; struct phm_cac_leakage_table *std_voltage_table = hwmgr->dyn_state.cac_leakage_table; - uint32_t i; + uint32_t i, clk; PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table != NULL, "SCLK dependency table is missing. This table is mandatory", return -EINVAL); @@ -804,10 +804,12 @@ static int smu7_setup_dpm_tables_v0(struct pp_hwmgr *hwmgr) data->dpm_table.sclk_table.count = 0; for (i = 0; i < allowed_vdd_sclk_table->count; i++) { + clk = min(allowed_vdd_sclk_table->entries[i].clk, data->sclk_cap); + if (i == 0 || data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count-1].value != - allowed_vdd_sclk_table->entries[i].clk) { + clk) { data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].value = - allowed_vdd_sclk_table->entries[i].clk; + clk; data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].enabled = (i == 0) ? 1 : 0; data->dpm_table.sclk_table.count++; } @@ -3000,6 +3002,25 @@ static int smu7_init_voltage_dependency_on_display_clock_table(struct pp_hwmgr * return 0; } +static void smu7_set_sclk_cap(struct pp_hwmgr *hwmgr) +{ + struct amdgpu_device *adev = hwmgr->adev; + struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); + + data->sclk_cap = 0xffffffff; + + if (hwmgr->od_enabled) + return; + + /* R9 390X board: last sclk dpm level is unstable, use lower sclk */ + if (adev->pdev->device == 0x67B0 && + adev->pdev->subsystem_vendor == 0x1043) + data->sclk_cap = 104000; /* 1040 MHz */ + + if (data->sclk_cap != 0xffffffff) + dev_info(adev->dev, "sclk cap: %u kHz on quirky ASIC\n", data->sclk_cap * 10); +} + static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) { struct amdgpu_device *adev = hwmgr->adev; @@ -3011,6 +3032,7 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) return -ENOMEM; hwmgr->backend = data; + smu7_set_sclk_cap(hwmgr); smu7_patch_voltage_workaround(hwmgr); smu7_init_dpm_defaults(hwmgr); @@ -3894,7 +3916,7 @@ static int smu7_get_pp_table_entry_callback_func_v0(struct pp_hwmgr *hwmgr, /* Performance levels are arranged from low to high. */ performance_level->memory_clock = memory_clock; - performance_level->engine_clock = engine_clock; + performance_level->engine_clock = min(engine_clock, data->sclk_cap); pcie_gen_from_bios = visland_clk_info->ucPCIEGen; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h index d9e8b386bd4d..66adabeab6a3 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h @@ -234,6 +234,7 @@ struct smu7_hwmgr { uint32_t pcie_gen_cap; uint32_t pcie_lane_cap; uint32_t pcie_spc_cap; + uint32_t sclk_cap; struct smu7_leakage_voltage vddc_leakage; struct smu7_leakage_voltage vddci_leakage; struct smu7_leakage_voltage vddcgfx_leakage; From 8b3e8fa6d7bdab292447a43f70532db437d5d4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Sun, 29 Mar 2026 18:03:06 +0200 Subject: [PATCH 509/712] drm/amdgpu/uvd4.2: Don't initialize UVD 4.2 when DPM is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UVD 4.2 doesn't work at all when DPM is disabled because the SMU is responsible for ungating it. So, Linux fails to boot with CIK GPUs when using the amdgpu.dpm=0 parameter. Fix this by returning -ENOENT from uvd_v4_2_early_init() when amdgpu_dpm isn't enabled. Note: amdgpu.dpm=0 is often suggested as a workaround for issues and is useful for debugging. Fixes: a2e73f56fa62 ("drm/amdgpu: Add support for CIK parts") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c index 73ce3d211ed6..8a9ba2276275 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c @@ -93,6 +93,11 @@ static void uvd_v4_2_ring_set_wptr(struct amdgpu_ring *ring) static int uvd_v4_2_early_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; + + /* UVD doesn't work without DPM, it needs DPM to ungate it. */ + if (!amdgpu_dpm) + return -ENOENT; + adev->uvd.num_uvd_inst = 1; uvd_v4_2_set_ring_funcs(adev); From fd63f185979b047fb22a0dfc6bd94d0cab6a6a70 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 27 Mar 2026 10:52:57 +0100 Subject: [PATCH 510/712] ipv6: prevent possible UaF in addrconf_permanent_addr() The mentioned helper try to warn the user about an exceptional condition, but the message is delivered too late, accessing the ipv6 after its possible deletion. Reorder the statement to avoid the possible UaF; while at it, place the warning outside the idev->lock as it needs no protection. Reported-by: Jakub Kicinski Closes: https://sashiko.dev/#/patchset/8c8bfe2e1a324e501f0e15fef404a77443fd8caf.1774365668.git.pabeni%40redhat.com Fixes: f1705ec197e7 ("net: ipv6: Make address flushing on ifdown optional") Signed-off-by: Paolo Abeni Link: https://patch.msgid.link/ef973c3a8cb4f8f1787ed469f3e5391b9fe95aa0.1774601542.git.pabeni@redhat.com Signed-off-by: Jakub Kicinski --- net/ipv6/addrconf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f4e23b543585..dd0b4d80e0f8 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3625,12 +3625,12 @@ static void addrconf_permanent_addr(struct net *net, struct net_device *dev) if ((ifp->flags & IFA_F_PERMANENT) && fixup_permanent_addr(net, idev, ifp) < 0) { write_unlock_bh(&idev->lock); - in6_ifa_hold(ifp); - ipv6_del_addr(ifp); - write_lock_bh(&idev->lock); net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n", idev->dev->name, &ifp->addr); + in6_ifa_hold(ifp); + ipv6_del_addr(ifp); + write_lock_bh(&idev->lock); } } From 514aac3599879a7ed48b7dc19e31145beb6958ac Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 27 Mar 2026 10:48:21 +0100 Subject: [PATCH 511/712] net: airoha: Add missing cleanup bits in airoha_qdma_cleanup_rx_queue() In order to properly cleanup hw rx QDMA queues and bring the device to the initial state, reset rx DMA queue head/tail index. Moreover, reset queued DMA descriptor fields. Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") Tested-by: Madhur Agrawal Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260327-airoha_qdma_cleanup_rx_queue-fix-v1-1-369d6ab1511a@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 56cf9a926a83..c2a54dbcbb0d 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -794,18 +794,34 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q, static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) { - struct airoha_eth *eth = q->qdma->eth; + struct airoha_qdma *qdma = q->qdma; + struct airoha_eth *eth = qdma->eth; + int qid = q - &qdma->q_rx[0]; while (q->queued) { struct airoha_queue_entry *e = &q->entry[q->tail]; + struct airoha_qdma_desc *desc = &q->desc[q->tail]; struct page *page = virt_to_head_page(e->buf); dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, page_pool_get_dma_dir(q->page_pool)); page_pool_put_full_page(q->page_pool, page, false); + /* Reset DMA descriptor */ + WRITE_ONCE(desc->ctrl, 0); + WRITE_ONCE(desc->addr, 0); + WRITE_ONCE(desc->data, 0); + WRITE_ONCE(desc->msg0, 0); + WRITE_ONCE(desc->msg1, 0); + WRITE_ONCE(desc->msg2, 0); + WRITE_ONCE(desc->msg3, 0); + q->tail = (q->tail + 1) % q->ndesc; q->queued--; } + + q->head = q->tail; + airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, + FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail)); } static int airoha_qdma_init_rx(struct airoha_qdma *qdma) From ddc748a391dd8642ba6b2e4fe22e7f2ddf84b7f0 Mon Sep 17 00:00:00 2001 From: Guoyu Su Date: Fri, 27 Mar 2026 23:35:07 +0800 Subject: [PATCH 512/712] net: use skb_header_pointer() for TCPv4 GSO frag_off check Syzbot reported a KMSAN uninit-value warning in gso_features_check() called from netif_skb_features() [1]. gso_features_check() reads iph->frag_off to decide whether to clear mangleid_features. Accessing the IPv4 header via ip_hdr()/inner_ip_hdr() can rely on skb header offsets that are not always safe for direct dereference on packets injected from PF_PACKET paths. Use skb_header_pointer() for the TCPv4 frag_off check so the header read is robust whether data is already linear or needs copying. [1] https://syzkaller.appspot.com/bug?extid=1543a7d954d9c6d00407 Link: https://lore.kernel.org/netdev/willemdebruijn.kernel.1a9f35039caab@gmail.com/ Fixes: cbc53e08a793 ("GSO: Add GSO type for fixed IPv4 ID") Reported-by: syzbot+1543a7d954d9c6d00407@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=1543a7d954d9c6d00407 Tested-by: syzbot+1543a7d954d9c6d00407@syzkaller.appspotmail.com Signed-off-by: Guoyu Su Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260327153507.39742-1-yss2813483011xxl@gmail.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index fc5557062414..831129f2a69b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3821,10 +3821,15 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb, * segmentation-offloads.rst). */ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { - struct iphdr *iph = skb->encapsulation ? - inner_ip_hdr(skb) : ip_hdr(skb); + const struct iphdr *iph; + struct iphdr _iph; + int nhoff = skb->encapsulation ? + skb_inner_network_offset(skb) : + skb_network_offset(skb); - if (!(iph->frag_off & htons(IP_DF))) + iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); + + if (!iph || !(iph->frag_off & htons(IP_DF))) features &= ~dev->mangleid_features; } From e6e3eb5ee89ac4c163d46429391c889a1bb5e404 Mon Sep 17 00:00:00 2001 From: Yochai Eisenrich Date: Sun, 29 Mar 2026 00:14:36 +0300 Subject: [PATCH 513/712] net: sched: cls_api: fix tc_chain_fill_node to initialize tcm_info to zero to prevent an info-leak When building netlink messages, tc_chain_fill_node() never initializes the tcm_info field of struct tcmsg. Since the allocation is not zeroed, kernel heap memory is leaked to userspace through this 4-byte field. The fix simply zeroes tcm_info alongside the other fields that are already initialized. Fixes: 32a4f5ecd738 ("net: sched: introduce chain object to uapi") Signed-off-by: Yochai Eisenrich Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260328211436.1010152-1-echelonh@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/cls_api.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 4829c27446e3..20f7f9ee0b35 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -2969,6 +2969,7 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, tcm->tcm__pad1 = 0; tcm->tcm__pad2 = 0; tcm->tcm_handle = 0; + tcm->tcm_info = 0; if (block->q) { tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex; tcm->tcm_parent = block->q->handle; From cedc1bf327de62ec30af9743bd1f601c2de30553 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 29 Mar 2026 12:32:27 +0200 Subject: [PATCH 514/712] net: airoha: Delay offloading until all net_devices are fully registered Netfilter flowtable can theoretically try to offload flower rules as soon as a net_device is registered while all the other ones are not registered or initialized, triggering a possible NULL pointer dereferencing of qdma pointer in airoha_ppe_set_cpu_port routine. Moreover, if register_netdev() fails for a particular net_device, there is a small race if Netfilter tries to offload flowtable rules before all the net_devices are properly unregistered in airoha_probe() error patch, triggering a NULL pointer dereferencing in airoha_ppe_set_cpu_port routine. In order to avoid any possible race, delay offloading until all net_devices are registered in the networking subsystem. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260329-airoha-regiser-race-fix-v2-1-f4ebb139277b@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 2 ++ drivers/net/ethernet/airoha/airoha_eth.h | 1 + drivers/net/ethernet/airoha/airoha_ppe.c | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index c2a54dbcbb0d..95ba99b89428 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2962,6 +2962,8 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth) return err; } + set_bit(DEV_STATE_REGISTERED, ð->state); + return 0; } diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 20e602d61e61..a97903569335 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -88,6 +88,7 @@ enum { enum { DEV_STATE_INITIALIZED, + DEV_STATE_REGISTERED, }; enum { diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 5724f8f2defd..c2c32b6833df 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1368,6 +1368,13 @@ int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data) struct airoha_eth *eth = ppe->eth; int err = 0; + /* Netfilter flowtable can try to offload flower rules while not all + * the net_devices are registered or initialized. Delay offloading + * until all net_devices are registered in the system. + */ + if (!test_bit(DEV_STATE_REGISTERED, ð->state)) + return -EBUSY; + mutex_lock(&flow_offload_mutex); if (!eth->npu) From 4ee937107d52f9e5c350e4b5e629760e328b3d9f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 29 Mar 2026 07:43:56 +0800 Subject: [PATCH 515/712] bnxt_en: set backing store type from query type bnxt_hwrm_func_backing_store_qcaps_v2() stores resp->type from the firmware response in ctxm->type and later uses that value to index fixed backing-store metadata arrays such as ctx_arr[] and bnxt_bstore_to_trace[]. ctxm->type is fixed by the current backing-store query type and matches the array index of ctx->ctx_arr. Set ctxm->type from the current loop variable instead of depending on resp->type. Also update the loop to advance type from next_valid_type in the for statement, which keeps the control flow simpler for non-valid and unchanged entries. Fixes: 6a4d0774f02d ("bnxt_en: Add support for new backing store query firmware API") Signed-off-by: Pengpeng Hou Reviewed-by: Michael Chan Tested-by: Michael Chan Link: https://patch.msgid.link/20260328234357.43669-1-pengpeng@iscas.ac.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 0751c0e4581a..7ed805713fbb 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8671,7 +8671,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) struct hwrm_func_backing_store_qcaps_v2_output *resp; struct hwrm_func_backing_store_qcaps_v2_input *req; struct bnxt_ctx_mem_info *ctx = bp->ctx; - u16 type; + u16 type, next_type = 0; int rc; rc = hwrm_req_init(bp, req, HWRM_FUNC_BACKING_STORE_QCAPS_V2); @@ -8687,7 +8687,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) resp = hwrm_req_hold(bp, req); - for (type = 0; type < BNXT_CTX_V2_MAX; ) { + for (type = 0; type < BNXT_CTX_V2_MAX; type = next_type) { struct bnxt_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; u8 init_val, init_off, i; u32 max_entries; @@ -8700,7 +8700,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) if (rc) goto ctx_done; flags = le32_to_cpu(resp->flags); - type = le16_to_cpu(resp->next_valid_type); + next_type = le16_to_cpu(resp->next_valid_type); if (!(flags & BNXT_CTX_MEM_TYPE_VALID)) { bnxt_free_one_ctx_mem(bp, ctxm, true); continue; @@ -8715,7 +8715,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) else continue; } - ctxm->type = le16_to_cpu(resp->type); + ctxm->type = type; ctxm->entry_size = entry_size; ctxm->flags = flags; ctxm->instance_bmap = le32_to_cpu(resp->instance_bit_map); From 316fb1b3169efb081d2db910cbbfef445afa03b9 Mon Sep 17 00:00:00 2001 From: robbieko Date: Wed, 25 Mar 2026 18:18:15 +0800 Subject: [PATCH 516/712] btrfs: fix incorrect return value after changing leaf in lookup_extent_data_ref() After commit 1618aa3c2e01 ("btrfs: simplify return variables in lookup_extent_data_ref()"), the err and ret variables were merged into a single ret variable. However, when btrfs_next_leaf() returns 0 (success), ret is overwritten from -ENOENT to 0. If the first key in the next leaf does not match (different objectid or type), the function returns 0 instead of -ENOENT, making the caller believe the lookup succeeded when it did not. This can lead to operations on the wrong extent tree item, potentially causing extent tree corruption. Fix this by returning -ENOENT directly when the key does not match, instead of relying on the ret variable. Fixes: 1618aa3c2e01 ("btrfs: simplify return variables in lookup_extent_data_ref()") CC: stable@vger.kernel.org # 6.12+ Reviewed-by: Filipe Manana Signed-off-by: robbieko Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 85ee5c79759d..098e64106d02 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -495,7 +495,7 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans, btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.objectid != bytenr || key.type != BTRFS_EXTENT_DATA_REF_KEY) - return ret; + return -ENOENT; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_data_ref); From e08e0754e690e4909cab83ac43fd2c93c6200514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 25 Mar 2026 15:58:44 +0200 Subject: [PATCH 517/712] drm/i915/cdclk: Do the full CDCLK dance for min_voltage_level changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently I forgot about the pipe min_voltage_level when I decoupled the CDCLK calculations from modesets. Even if the CDCLK frequency doesn't need changing we may still need to bump the voltage level to accommodate an increase in the port clock frequency. Currently, even if there is a full modeset, we won't notice the need to go through the full CDCLK calculations/programming, unless the set of enabled/active pipes changes, or the pipe/dbuf min CDCLK changes. Duplicate the same logic we use the pipe's min CDCLK frequency to also deal with its min voltage level. Note that the 'allow_voltage_level_decrease' stuff isn't really useful here since the min voltage level can only change during a full modeset. But I think sticking to the same approach in the three similar parts (pipe min cdclk, pipe min voltage level, dbuf min cdclk) is a good idea. Cc: stable@vger.kernel.org Tested-by: Mikhail Rudenko Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15826 Fixes: ba91b9eecb47 ("drm/i915/cdclk: Decouple cdclk from state->modeset") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260325135849.12603-2-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak (cherry picked from commit 0f21a14987ebae3c05ad1184ea872e7b7a7b8695) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_cdclk.c | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index f5946e677c93..3d7b4b0795cd 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2971,6 +2971,53 @@ static int intel_cdclk_update_crtc_min_cdclk(struct intel_atomic_state *state, return 0; } +static int intel_cdclk_update_crtc_min_voltage_level(struct intel_atomic_state *state, + struct intel_crtc *crtc, + u8 old_min_voltage_level, + u8 new_min_voltage_level, + bool *need_cdclk_calc) +{ + struct intel_display *display = to_intel_display(state); + struct intel_cdclk_state *cdclk_state; + bool allow_voltage_level_decrease = intel_any_crtc_needs_modeset(state); + int ret; + + if (new_min_voltage_level == old_min_voltage_level) + return 0; + + if (!allow_voltage_level_decrease && + new_min_voltage_level < old_min_voltage_level) + return 0; + + cdclk_state = intel_atomic_get_cdclk_state(state); + if (IS_ERR(cdclk_state)) + return PTR_ERR(cdclk_state); + + old_min_voltage_level = cdclk_state->min_voltage_level[crtc->pipe]; + + if (new_min_voltage_level == old_min_voltage_level) + return 0; + + if (!allow_voltage_level_decrease && + new_min_voltage_level < old_min_voltage_level) + return 0; + + cdclk_state->min_voltage_level[crtc->pipe] = new_min_voltage_level; + + ret = intel_atomic_lock_global_state(&cdclk_state->base); + if (ret) + return ret; + + *need_cdclk_calc = true; + + drm_dbg_kms(display->drm, + "[CRTC:%d:%s] min voltage level: %d -> %d\n", + crtc->base.base.id, crtc->base.name, + old_min_voltage_level, new_min_voltage_level); + + return 0; +} + int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state, int old_min_cdclk, int new_min_cdclk, bool *need_cdclk_calc) @@ -3386,6 +3433,13 @@ static int intel_crtcs_calc_min_cdclk(struct intel_atomic_state *state, need_cdclk_calc); if (ret) return ret; + + ret = intel_cdclk_update_crtc_min_voltage_level(state, crtc, + old_crtc_state->min_voltage_level, + new_crtc_state->min_voltage_level, + need_cdclk_calc); + if (ret) + return ret; } return 0; From 9c9a57e4e337f94e23ddf69263fd0685c91155fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 25 Mar 2026 15:58:45 +0200 Subject: [PATCH 518/712] drm/i915/dp: Use crtc_state->enhanced_framing properly on ivb/hsw CPU eDP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks like I missed the drm_dp_enhanced_frame_cap() in the ivb/hsw CPU eDP code when I introduced crtc_state->enhanced_framing. Fix it up so that the state we program to the hardware is guaranteed to match what we computed earlier. Cc: stable@vger.kernel.org Fixes: 3072a24c778a ("drm/i915: Introduce crtc_state->enhanced_framing") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260325135849.12603-3-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak (cherry picked from commit 799fe8dc2af52f35c78c4ac97f8e34994dfd8760) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/g4x_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index 4cb753177fd8..c7fa014e0d50 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -137,7 +137,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder, intel_dp->DP |= DP_SYNC_VS_HIGH; intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; - if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) + if (pipe_config->enhanced_framing) intel_dp->DP |= DP_ENHANCED_FRAMING; intel_dp->DP |= DP_PIPE_SEL_IVB(crtc->pipe); From 720460722310c7ab35421aa81a3153ff96b6c82b Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 31 Mar 2026 09:35:36 +0800 Subject: [PATCH 519/712] ALSA: hda/realtek: add quirk for HP Laptop 15-fc0xxx For the HP Laptop 15-fc0xxx with ALC236, the built-in mic 0x12 was not set up, making it unusable; after adding it, it now works properly. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221233 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260331013536.13778-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 766d7bb2d4f5..57ba642252b0 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4123,6 +4123,7 @@ enum { ALC245_FIXUP_BASS_HP_DAC, ALC245_FIXUP_ACER_MICMUTE_LED, ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED, + ALC236_FIXUP_HP_DMIC, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6658,6 +6659,13 @@ static const struct hda_fixup alc269_fixups[] = { .v.func = alc245_fixup_hp_mute_led_coefbit, .chained = true, .chain_id = ALC287_FIXUP_CS35L41_I2C_2, + }, + [ALC236_FIXUP_HP_DMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x90a60160 }, /* use as internal mic */ + { } + }, } }; @@ -7153,6 +7161,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8da1, "HP 16 Clipper OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8da7, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8dc9, "HP Laptop 15-fc0xxx", ALC236_FIXUP_HP_DMIC), SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), SND_PCI_QUIRK(0x103c, 0x8dd7, "HP Laptop 15-fd0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), From e6c888202297eca21860b669edb74fc600e679d9 Mon Sep 17 00:00:00 2001 From: songxiebing Date: Tue, 31 Mar 2026 11:36:50 +0800 Subject: [PATCH 520/712] ALSA: hda/realtek: Add quirk for Lenovo Yoga Slim 7 14AKP10 The Pin Complex 0x17 (bass/woofer speakers) is incorrectly reported as unconnected in the BIOS (pin default 0x411111f0 = N/A). This causes the kernel to configure speaker_outs=0, meaning only the tweeters (pin 0x14) are used. The result is very low, tinny audio with no bass. The existing quirk ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN (already present in patch_realtek.c for SSID 0x17aa3801) fixes the issue completely. Reported-by: Garcicasti Link: https://bugzilla.kernel.org/show_bug.cgi?id=221298 Signed-off-by: songxiebing Link: https://patch.msgid.link/20260331033650.285601-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 57ba642252b0..fa5f08cd2c65 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7676,6 +7676,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), + SND_PCI_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), From b981e9e94c687b7b19ae8820963f005b842cb2f2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 29 Mar 2026 19:27:48 -0700 Subject: [PATCH 521/712] x86/platform/geode: Fix on-stack property data use-after-return bug The PROPERTY_ENTRY_GPIO macro (and by extension PROPERTY_ENTRY_REF) creates a temporary software_node_ref_args structure on the stack when used in a runtime assignment. This results in the property pointing to data that is invalid once the function returns. Fix this by ensuring the GPIO reference data is not stored on stack and using PROPERTY_ENTRY_REF_ARRAY_LEN() to point directly to the persistent reference data. Fixes: 298c9babadb8 ("x86/platform/geode: switch GPIO buttons and LEDs to software properties") Signed-off-by: Dmitry Torokhov Signed-off-by: Ingo Molnar Cc: Rafael J. Wysocki Cc: Andy Shevchenko Cc: Daniel Scally Cc: Danilo Krummrich Cc: Hans de Goede Cc: Heikki Krogerus Cc: Sakari Ailus Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260329-property-gpio-fix-v2-1-3cca5ba136d8@gmail.com --- arch/x86/platform/geode/geode-common.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/arch/x86/platform/geode/geode-common.c b/arch/x86/platform/geode/geode-common.c index 05189c5f7d2a..1843ae385e2d 100644 --- a/arch/x86/platform/geode/geode-common.c +++ b/arch/x86/platform/geode/geode-common.c @@ -28,8 +28,10 @@ static const struct software_node geode_gpio_keys_node = { .properties = geode_gpio_keys_props, }; -static struct property_entry geode_restart_key_props[] = { - { /* Placeholder for GPIO property */ }, +static struct software_node_ref_args geode_restart_gpio_ref; + +static const struct property_entry geode_restart_key_props[] = { + PROPERTY_ENTRY_REF_ARRAY_LEN("gpios", &geode_restart_gpio_ref, 1), PROPERTY_ENTRY_U32("linux,code", KEY_RESTART), PROPERTY_ENTRY_STRING("label", "Reset button"), PROPERTY_ENTRY_U32("debounce-interval", 100), @@ -64,8 +66,7 @@ int __init geode_create_restart_key(unsigned int pin) struct platform_device *pd; int err; - geode_restart_key_props[0] = PROPERTY_ENTRY_GPIO("gpios", - &geode_gpiochip_node, + geode_restart_gpio_ref = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, pin, GPIO_ACTIVE_LOW); err = software_node_register_node_group(geode_gpio_keys_swnodes); @@ -99,6 +100,7 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, const struct software_node *group[MAX_LEDS + 2] = { 0 }; struct software_node *swnodes; struct property_entry *props; + struct software_node_ref_args *gpio_refs; struct platform_device_info led_info = { .name = "leds-gpio", .id = PLATFORM_DEVID_NONE, @@ -127,6 +129,12 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, goto err_free_swnodes; } + gpio_refs = kzalloc_objs(*gpio_refs, n_leds); + if (!gpio_refs) { + err = -ENOMEM; + goto err_free_props; + } + group[0] = &geode_gpio_leds_node; for (i = 0; i < n_leds; i++) { node_name = kasprintf(GFP_KERNEL, "%s:%d", label, i); @@ -135,9 +143,11 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, goto err_free_names; } + gpio_refs[i] = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, + leds[i].pin, + GPIO_ACTIVE_LOW); props[i * 3 + 0] = - PROPERTY_ENTRY_GPIO("gpios", &geode_gpiochip_node, - leds[i].pin, GPIO_ACTIVE_LOW); + PROPERTY_ENTRY_REF_ARRAY_LEN("gpios", &gpio_refs[i], 1); props[i * 3 + 1] = PROPERTY_ENTRY_STRING("linux,default-trigger", leds[i].default_on ? @@ -171,6 +181,8 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, err_free_names: while (--i >= 0) kfree(swnodes[i].name); + kfree(gpio_refs); +err_free_props: kfree(props); err_free_swnodes: kfree(swnodes); From af416cd9b3fb9d17ac7f4cfa12d1ea83dfd0e4be Mon Sep 17 00:00:00 2001 From: Jessica Liu Date: Tue, 31 Mar 2026 09:30:29 +0800 Subject: [PATCH 522/712] irqchip/riscv-aplic: Restrict genpd notifier to device tree only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On ACPI systems, the aplic's pm_domain is set to acpi_general_pm_domain, which provides its own power management callbacks (e.g., runtime_suspend via acpi_subsys_runtime_suspend). aplic_pm_add() unconditionally calls dev_pm_genpd_add_notifier() when dev->pm_domain is non‑NULL, leading to a comparison between runtime_suspend and genpd_runtime_suspend. This results in the following errors when ACPI is enabled: riscv-aplic RSCV0002:00: failed to create APLIC context riscv-aplic RSCV0002:00: error -ENODEV: failed to setup APLIC in MSI mode Fix this by checking for dev->of_node before adding or removing the genpd notifier, ensuring it is only used for device tree based systems. Fixes: 95a8ddde3660 ("irqchip/riscv-aplic: Preserve APLIC states across suspend/resume") Signed-off-by: Jessica Liu Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260331093029749vRpdH-0qoEqjS0Wnn9M4x@zte.com.cn --- drivers/irqchip/irq-riscv-aplic-main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c index 9f53979b6962..d9afb6ae98cf 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -150,7 +150,7 @@ static void aplic_pm_remove(void *data) struct device *dev = priv->dev; list_del(&priv->head); - if (dev->pm_domain) + if (dev->pm_domain && dev->of_node) dev_pm_genpd_remove_notifier(dev); } @@ -165,7 +165,7 @@ static int aplic_pm_add(struct device *dev, struct aplic_priv *priv) priv->saved_hw_regs.srcs = srcs; list_add(&priv->head, &aplics); - if (dev->pm_domain) { + if (dev->pm_domain && dev->of_node) { priv->genpd_nb.notifier_call = aplic_pm_notifier; ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); if (ret) From a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 26 Mar 2026 15:30:20 +0900 Subject: [PATCH 523/712] crypto: algif_aead - Revert to operating out-of-place This mostly reverts commit 72548b093ee3 except for the copying of the associated data. There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings. Get rid of all the complexity added for in-place operation and just copy the AD directly. Fixes: 72548b093ee3 ("crypto: algif_aead - copy AAD from src to dst") Reported-by: Taeyang Lee <0wn@theori.io> Signed-off-by: Herbert Xu --- crypto/af_alg.c | 49 ++++---------------- crypto/algif_aead.c | 100 ++++++++-------------------------------- crypto/algif_skcipher.c | 6 +-- include/crypto/if_alg.h | 5 +- 4 files changed, 34 insertions(+), 126 deletions(-) diff --git a/crypto/af_alg.c b/crypto/af_alg.c index c2fd9cd86c5e..8e0199394984 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -637,15 +637,13 @@ static int af_alg_alloc_tsgl(struct sock *sk) /** * af_alg_count_tsgl - Count number of TX SG entries * - * The counting starts from the beginning of the SGL to @bytes. If - * an @offset is provided, the counting of the SG entries starts at the @offset. + * The counting starts from the beginning of the SGL to @bytes. * * @sk: socket of connection to user space * @bytes: Count the number of SG entries holding given number of bytes. - * @offset: Start the counting of SG entries from the given offset. * Return: Number of TX SG entries found given the constraints */ -unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset) +unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes) { const struct alg_sock *ask = alg_sk(sk); const struct af_alg_ctx *ctx = ask->private; @@ -660,25 +658,11 @@ unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset) const struct scatterlist *sg = sgl->sg; for (i = 0; i < sgl->cur; i++) { - size_t bytes_count; - - /* Skip offset */ - if (offset >= sg[i].length) { - offset -= sg[i].length; - bytes -= sg[i].length; - continue; - } - - bytes_count = sg[i].length - offset; - - offset = 0; sgl_count++; - - /* If we have seen requested number of bytes, stop */ - if (bytes_count >= bytes) + if (sg[i].length >= bytes) return sgl_count; - bytes -= bytes_count; + bytes -= sg[i].length; } } @@ -690,19 +674,14 @@ EXPORT_SYMBOL_GPL(af_alg_count_tsgl); * af_alg_pull_tsgl - Release the specified buffers from TX SGL * * If @dst is non-null, reassign the pages to @dst. The caller must release - * the pages. If @dst_offset is given only reassign the pages to @dst starting - * at the @dst_offset (byte). The caller must ensure that @dst is large - * enough (e.g. by using af_alg_count_tsgl with the same offset). + * the pages. * * @sk: socket of connection to user space * @used: Number of bytes to pull from TX SGL * @dst: If non-NULL, buffer is reassigned to dst SGL instead of releasing. The * caller must release the buffers in dst. - * @dst_offset: Reassign the TX SGL from given offset. All buffers before - * reaching the offset is released. */ -void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst, - size_t dst_offset) +void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst) { struct alg_sock *ask = alg_sk(sk); struct af_alg_ctx *ctx = ask->private; @@ -727,18 +706,10 @@ void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst, * SG entries in dst. */ if (dst) { - if (dst_offset >= plen) { - /* discard page before offset */ - dst_offset -= plen; - } else { - /* reassign page to dst after offset */ - get_page(page); - sg_set_page(dst + j, page, - plen - dst_offset, - sg[i].offset + dst_offset); - dst_offset = 0; - j++; - } + /* reassign page to dst after offset */ + get_page(page); + sg_set_page(dst + j, page, plen, sg[i].offset); + j++; } sg[i].length -= plen; diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 79b016a899a1..dda15bb05e89 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -72,9 +71,8 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, struct alg_sock *pask = alg_sk(psk); struct af_alg_ctx *ctx = ask->private; struct crypto_aead *tfm = pask->private; - unsigned int i, as = crypto_aead_authsize(tfm); + unsigned int as = crypto_aead_authsize(tfm); struct af_alg_async_req *areq; - struct af_alg_tsgl *tsgl, *tmp; struct scatterlist *rsgl_src, *tsgl_src = NULL; int err = 0; size_t used = 0; /* [in] TX bufs to be en/decrypted */ @@ -154,23 +152,24 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, outlen -= less; } + /* + * Create a per request TX SGL for this request which tracks the + * SG entries from the global TX SGL. + */ processed = used + ctx->aead_assoclen; - list_for_each_entry_safe(tsgl, tmp, &ctx->tsgl_list, list) { - for (i = 0; i < tsgl->cur; i++) { - struct scatterlist *process_sg = tsgl->sg + i; - - if (!(process_sg->length) || !sg_page(process_sg)) - continue; - tsgl_src = process_sg; - break; - } - if (tsgl_src) - break; - } - if (processed && !tsgl_src) { - err = -EFAULT; + areq->tsgl_entries = af_alg_count_tsgl(sk, processed); + if (!areq->tsgl_entries) + areq->tsgl_entries = 1; + areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl), + areq->tsgl_entries), + GFP_KERNEL); + if (!areq->tsgl) { + err = -ENOMEM; goto free; } + sg_init_table(areq->tsgl, areq->tsgl_entries); + af_alg_pull_tsgl(sk, processed, areq->tsgl); + tsgl_src = areq->tsgl; /* * Copy of AAD from source to destination @@ -179,76 +178,15 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, * when user space uses an in-place cipher operation, the kernel * will copy the data as it does not see whether such in-place operation * is initiated. - * - * To ensure efficiency, the following implementation ensure that the - * ciphers are invoked to perform a crypto operation in-place. This - * is achieved by memory management specified as follows. */ /* Use the RX SGL as source (and destination) for crypto op. */ rsgl_src = areq->first_rsgl.sgl.sgt.sgl; - if (ctx->enc) { - /* - * Encryption operation - The in-place cipher operation is - * achieved by the following operation: - * - * TX SGL: AAD || PT - * | | - * | copy | - * v v - * RX SGL: AAD || PT || Tag - */ - memcpy_sglist(areq->first_rsgl.sgl.sgt.sgl, tsgl_src, - processed); - af_alg_pull_tsgl(sk, processed, NULL, 0); - } else { - /* - * Decryption operation - To achieve an in-place cipher - * operation, the following SGL structure is used: - * - * TX SGL: AAD || CT || Tag - * | | ^ - * | copy | | Create SGL link. - * v v | - * RX SGL: AAD || CT ----+ - */ - - /* Copy AAD || CT to RX SGL buffer for in-place operation. */ - memcpy_sglist(areq->first_rsgl.sgl.sgt.sgl, tsgl_src, outlen); - - /* Create TX SGL for tag and chain it to RX SGL. */ - areq->tsgl_entries = af_alg_count_tsgl(sk, processed, - processed - as); - if (!areq->tsgl_entries) - areq->tsgl_entries = 1; - areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl), - areq->tsgl_entries), - GFP_KERNEL); - if (!areq->tsgl) { - err = -ENOMEM; - goto free; - } - sg_init_table(areq->tsgl, areq->tsgl_entries); - - /* Release TX SGL, except for tag data and reassign tag data. */ - af_alg_pull_tsgl(sk, processed, areq->tsgl, processed - as); - - /* chain the areq TX SGL holding the tag with RX SGL */ - if (usedpages) { - /* RX SGL present */ - struct af_alg_sgl *sgl_prev = &areq->last_rsgl->sgl; - struct scatterlist *sg = sgl_prev->sgt.sgl; - - sg_unmark_end(sg + sgl_prev->sgt.nents - 1); - sg_chain(sg, sgl_prev->sgt.nents + 1, areq->tsgl); - } else - /* no RX SGL present (e.g. authentication only) */ - rsgl_src = areq->tsgl; - } + memcpy_sglist(rsgl_src, tsgl_src, ctx->aead_assoclen); /* Initialize the crypto operation */ - aead_request_set_crypt(&areq->cra_u.aead_req, rsgl_src, + aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src, areq->first_rsgl.sgl.sgt.sgl, used, ctx->iv); aead_request_set_ad(&areq->cra_u.aead_req, ctx->aead_assoclen); aead_request_set_tfm(&areq->cra_u.aead_req, tfm); @@ -450,7 +388,7 @@ static void aead_sock_destruct(struct sock *sk) struct crypto_aead *tfm = pask->private; unsigned int ivlen = crypto_aead_ivsize(tfm); - af_alg_pull_tsgl(sk, ctx->used, NULL, 0); + af_alg_pull_tsgl(sk, ctx->used, NULL); sock_kzfree_s(sk, ctx->iv, ivlen); sock_kfree_s(sk, ctx, ctx->len); af_alg_release_parent(sk); diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 125d395c5e00..82735e51be10 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -138,7 +138,7 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, * Create a per request TX SGL for this request which tracks the * SG entries from the global TX SGL. */ - areq->tsgl_entries = af_alg_count_tsgl(sk, len, 0); + areq->tsgl_entries = af_alg_count_tsgl(sk, len); if (!areq->tsgl_entries) areq->tsgl_entries = 1; areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl), @@ -149,7 +149,7 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, goto free; } sg_init_table(areq->tsgl, areq->tsgl_entries); - af_alg_pull_tsgl(sk, len, areq->tsgl, 0); + af_alg_pull_tsgl(sk, len, areq->tsgl); /* Initialize the crypto operation */ skcipher_request_set_tfm(&areq->cra_u.skcipher_req, tfm); @@ -363,7 +363,7 @@ static void skcipher_sock_destruct(struct sock *sk) struct alg_sock *pask = alg_sk(psk); struct crypto_skcipher *tfm = pask->private; - af_alg_pull_tsgl(sk, ctx->used, NULL, 0); + af_alg_pull_tsgl(sk, ctx->used, NULL); sock_kzfree_s(sk, ctx->iv, crypto_skcipher_ivsize(tfm)); if (ctx->state) sock_kzfree_s(sk, ctx->state, crypto_skcipher_statesize(tfm)); diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 107b797c33ec..0cc8fa749f68 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -230,9 +230,8 @@ static inline bool af_alg_readable(struct sock *sk) return PAGE_SIZE <= af_alg_rcvbuf(sk); } -unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset); -void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst, - size_t dst_offset); +unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes); +void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst); void af_alg_wmem_wakeup(struct sock *sk); int af_alg_wait_for_data(struct sock *sk, unsigned flags, unsigned min); int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, From e02494114ebf7c8b42777c6cd6982f113bfdbec7 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 27 Mar 2026 15:04:17 +0900 Subject: [PATCH 524/712] crypto: authencesn - Do not place hiseq at end of dst for out-of-place decryption When decrypting data that is not in-place (src != dst), there is no need to save the high-order sequence bits in dst as it could simply be re-copied from the source. However, the data to be hashed need to be rearranged accordingly. Reported-by: Taeyang Lee <0wn@theori.io> Fixes: 104880a6b470 ("crypto: authencesn - Convert to new AEAD interface") Signed-off-by: Herbert Xu Thanks, Signed-off-by: Herbert Xu --- crypto/authencesn.c | 50 +++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/crypto/authencesn.c b/crypto/authencesn.c index 542a978663b9..c0a01d738d9b 100644 --- a/crypto/authencesn.c +++ b/crypto/authencesn.c @@ -207,6 +207,7 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req, u8 *ohash = areq_ctx->tail; unsigned int cryptlen = req->cryptlen - authsize; unsigned int assoclen = req->assoclen; + struct scatterlist *src = req->src; struct scatterlist *dst = req->dst; u8 *ihash = ohash + crypto_ahash_digestsize(auth); u32 tmp[2]; @@ -214,23 +215,27 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req, if (!authsize) goto decrypt; - /* Move high-order bits of sequence number back. */ - scatterwalk_map_and_copy(tmp, dst, 4, 4, 0); - scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0); - scatterwalk_map_and_copy(tmp, dst, 0, 8, 1); + if (src == dst) { + /* Move high-order bits of sequence number back. */ + scatterwalk_map_and_copy(tmp, dst, 4, 4, 0); + scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0); + scatterwalk_map_and_copy(tmp, dst, 0, 8, 1); + } else + memcpy_sglist(dst, src, assoclen); if (crypto_memneq(ihash, ohash, authsize)) return -EBADMSG; decrypt: - sg_init_table(areq_ctx->dst, 2); + if (src != dst) + src = scatterwalk_ffwd(areq_ctx->src, src, assoclen); dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen); skcipher_request_set_tfm(skreq, ctx->enc); skcipher_request_set_callback(skreq, flags, req->base.complete, req->base.data); - skcipher_request_set_crypt(skreq, dst, dst, cryptlen, req->iv); + skcipher_request_set_crypt(skreq, src, dst, cryptlen, req->iv); return crypto_skcipher_decrypt(skreq); } @@ -255,6 +260,7 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req) unsigned int assoclen = req->assoclen; unsigned int cryptlen = req->cryptlen; u8 *ihash = ohash + crypto_ahash_digestsize(auth); + struct scatterlist *src = req->src; struct scatterlist *dst = req->dst; u32 tmp[2]; int err; @@ -262,24 +268,28 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req) if (assoclen < 8) return -EINVAL; - cryptlen -= authsize; - - if (req->src != dst) - memcpy_sglist(dst, req->src, assoclen + cryptlen); - - scatterwalk_map_and_copy(ihash, req->src, assoclen + cryptlen, - authsize, 0); - if (!authsize) goto tail; - /* Move high-order bits of sequence number to the end. */ - scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); - scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); - scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); + cryptlen -= authsize; + scatterwalk_map_and_copy(ihash, req->src, assoclen + cryptlen, + authsize, 0); - sg_init_table(areq_ctx->dst, 2); - dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4); + /* Move high-order bits of sequence number to the end. */ + scatterwalk_map_and_copy(tmp, src, 0, 8, 0); + if (src == dst) { + scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); + scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); + dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4); + } else { + scatterwalk_map_and_copy(tmp, dst, 0, 4, 1); + scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen - 4, 4, 1); + + src = scatterwalk_ffwd(areq_ctx->src, src, 8); + dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4); + memcpy_sglist(dst, src, assoclen + cryptlen - 8); + dst = req->dst; + } ahash_request_set_tfm(ahreq, auth); ahash_request_set_crypt(ahreq, dst, ohash, assoclen + cryptlen); From 75dc1980cf48826287e43dc7a49e310c6691f97e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 31 Mar 2026 10:12:17 +0200 Subject: [PATCH 525/712] ALSA: ctxfi: Don't enumerate SPDIF1 at DAIO initialization The recent refactoring of xfi driver changed the assignment of atc->daios[] at atc_get_resources(); now it loops over all enum DAIOTYP entries while it looped formerly only a part of them. The problem is that the last entry, SPDIF1, is a special type that is used only for hw20k1 CTSB073X model (as a replacement of SPDIFIO), and there is no corresponding definition for hw20k2. Due to the lack of the info, it caused a kernel crash on hw20k2, which was already worked around by the commit b045ab3dff97 ("ALSA: ctxfi: Fix missing SPDIFI1 index handling"). This patch addresses the root cause of the regression above properly, simply by skipping the incorrect SPDIF1 type in the parser loop. For making the change clearer, the code is slightly arranged, too. Fixes: a2dbaeb5c61e ("ALSA: ctxfi: Refactor resource alloc for sparse mappings") Cc: Link: https://bugzilla.suse.com/show_bug.cgi?id=1259925 Link: https://patch.msgid.link/20260331081227.216134-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index f122e396bc55..da2667cb2489 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1427,10 +1427,14 @@ static int atc_get_resources(struct ct_atc *atc) daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; da_desc.msr = atc->msr; for (i = 0; i < NUM_DAIOTYP; i++) { - if (((i == MIC) && !cap.dedicated_mic) || ((i == RCA) && !cap.dedicated_rca)) + if (((i == MIC) && !cap.dedicated_mic) || + ((i == RCA) && !cap.dedicated_rca) || + i == SPDIFI1) continue; - da_desc.type = (atc->model != CTSB073X) ? i : - ((i == SPDIFIO) ? SPDIFI1 : i); + if (atc->model == CTSB073X && i == SPDIFIO) + da_desc.type = SPDIFI1; + else + da_desc.type = i; da_desc.output = (i < LINEIM) || (i == RCA); err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); From 2884bf72fb8f03409e423397319205de48adca16 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 26 Mar 2026 00:55:53 -0700 Subject: [PATCH 526/712] net: bonding: fix use-after-free in bond_xmit_broadcast() bond_xmit_broadcast() reuses the original skb for the last slave (determined by bond_is_last_slave()) and clones it for others. Concurrent slave enslave/release can mutate the slave list during RCU-protected iteration, changing which slave is "last" mid-loop. This causes the original skb to be double-consumed (double-freed). Replace the racy bond_is_last_slave() check with a simple index comparison (i + 1 == slaves_count) against the pre-snapshot slave count taken via READ_ONCE() before the loop. This preserves the zero-copy optimization for the last slave while making the "last" determination stable against concurrent list mutations. The UAF can trigger the following crash: ================================================================== BUG: KASAN: slab-use-after-free in skb_clone Read of size 8 at addr ffff888100ef8d40 by task exploit/147 CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY Call Trace: dump_stack_lvl (lib/dump_stack.c:123) print_report (mm/kasan/report.c:379 mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:597) skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108) bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334) bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593) dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887) __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838) ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136) ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219) ip6_output (net/ipv6/ip6_output.c:250) ip6_send_skb (net/ipv6/ip6_output.c:1985) udp_v6_send_skb (net/ipv6/udp.c:1442) udpv6_sendmsg (net/ipv6/udp.c:1733) __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206) __x64_sys_sendto (net/socket.c:2209) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) Allocated by task 147: Freed by task 147: The buggy address belongs to the object at ffff888100ef8c80 which belongs to the cache skbuff_head_cache of size 224 The buggy address is located 192 bytes inside of freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60) Memory state around the buggy address: ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc ^ ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Fixes: 4e5bd03ae346 ("net: bonding: fix bond_xmit_broadcast return value error bug") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260326075553.3960562-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- drivers/net/bonding/bond_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 33f414d03ab9..a5484d11553d 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5326,7 +5326,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb, if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP)) continue; - if (bond_is_last_slave(bond, slave)) { + if (i + 1 == slaves_count) { skb2 = skb; skb_used = true; } else { From 30fe3f5f6494f827d812ff179f295a8e532709d6 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 26 Mar 2026 22:20:33 +0800 Subject: [PATCH 527/712] NFC: pn533: bound the UART receive buffer pn532_receive_buf() appends every incoming byte to dev->recv_skb and only resets the buffer after pn532_uart_rx_is_frame() recognizes a complete frame. A continuous stream of bytes without a valid PN532 frame header therefore keeps growing the skb until skb_put_u8() hits the tail limit. Drop the accumulated partial frame once the fixed receive buffer is full so malformed UART traffic cannot grow the skb past PN532_UART_SKB_BUFF_LEN. Fixes: c656aa4c27b1 ("nfc: pn533: add UART phy driver") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260326142033.82297-1-pengpeng@iscas.ac.cn Signed-off-by: Paolo Abeni --- drivers/nfc/pn533/uart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nfc/pn533/uart.c b/drivers/nfc/pn533/uart.c index 6d2f520a5bc8..1b82b7b2a5fa 100644 --- a/drivers/nfc/pn533/uart.c +++ b/drivers/nfc/pn533/uart.c @@ -211,6 +211,9 @@ static size_t pn532_receive_buf(struct serdev_device *serdev, timer_delete(&dev->cmd_timeout); for (i = 0; i < count; i++) { + if (unlikely(!skb_tailroom(dev->recv_skb))) + skb_trim(dev->recv_skb, 0); + skb_put_u8(dev->recv_skb, *data++); if (!pn532_uart_rx_is_frame(dev->recv_skb)) continue; From 393e0b4f178ec7fce1141dacc3304e3607a92ee9 Mon Sep 17 00:00:00 2001 From: Suraj Gupta Date: Fri, 27 Mar 2026 13:02:37 +0530 Subject: [PATCH 528/712] net: xilinx: axienet: Correct BD length masks to match AXIDMA IP spec The XAXIDMA_BD_CTRL_LENGTH_MASK and XAXIDMA_BD_STS_ACTUAL_LEN_MASK macros were defined as 0x007FFFFF (23 bits), but the AXI DMA IP product guide (PG021) specifies the buffer length field as bits 25:0 (26 bits). Update both masks to match the IP documentation. In practice this had no functional impact, since Ethernet frames are far smaller than 2^23 bytes and the extra bits were always zero, but the masks should still reflect the hardware specification. Fixes: 8a3b7a252dca ("drivers/net/ethernet/xilinx: added Xilinx AXI Ethernet driver") Signed-off-by: Suraj Gupta Reviewed-by: Sean Anderson Link: https://patch.msgid.link/20260327073238.134948-2-suraj.gupta2@amd.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/xilinx/xilinx_axienet.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 5ff742103beb..fcd3aaef27fc 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -105,7 +105,7 @@ #define XAXIDMA_BD_HAS_DRE_MASK 0xF00 /* Whether has DRE mask */ #define XAXIDMA_BD_WORDLEN_MASK 0xFF /* Whether has DRE mask */ -#define XAXIDMA_BD_CTRL_LENGTH_MASK 0x007FFFFF /* Requested len */ +#define XAXIDMA_BD_CTRL_LENGTH_MASK GENMASK(25, 0) /* Requested len */ #define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */ #define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */ #define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */ @@ -130,7 +130,7 @@ #define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */ #define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */ -#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK 0x007FFFFF /* Actual len */ +#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK GENMASK(25, 0) /* Actual len */ #define XAXIDMA_BD_STS_COMPLETE_MASK 0x80000000 /* Completed */ #define XAXIDMA_BD_STS_DEC_ERR_MASK 0x40000000 /* Decode error */ #define XAXIDMA_BD_STS_SLV_ERR_MASK 0x20000000 /* Slave error */ From d1978d03e86785872871bff9c2623174b10740de Mon Sep 17 00:00:00 2001 From: Suraj Gupta Date: Fri, 27 Mar 2026 13:02:38 +0530 Subject: [PATCH 529/712] net: xilinx: axienet: Fix BQL accounting for multi-BD TX packets When a TX packet spans multiple buffer descriptors (scatter-gather), axienet_free_tx_chain sums the per-BD actual length from descriptor status into a caller-provided accumulator. That sum is reset on each NAPI poll. If the BDs for a single packet complete across different polls, the earlier bytes are lost and never credited to BQL. This causes BQL to think bytes are permanently in-flight, eventually stalling the TX queue. The SKB pointer is stored only on the last BD of a packet. When that BD completes, use skb->len for the byte count instead of summing per-BD status lengths. This matches netdev_sent_queue(), which debits skb->len, and naturally survives across polls because no partial packet contributes to the accumulator. Fixes: c900e49d58eb ("net: xilinx: axienet: Implement BQL") Signed-off-by: Suraj Gupta Reviewed-by: Sean Anderson Link: https://patch.msgid.link/20260327073238.134948-3-suraj.gupta2@amd.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index b06e4c37ff61..263c4b67fd5a 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -770,8 +770,8 @@ static int axienet_device_reset(struct net_device *ndev) * @first_bd: Index of first descriptor to clean up * @nr_bds: Max number of descriptors to clean up * @force: Whether to clean descriptors even if not complete - * @sizep: Pointer to a u32 filled with the total sum of all bytes - * in all cleaned-up descriptors. Ignored if NULL. + * @sizep: Pointer to a u32 accumulating the total byte count of + * completed packets (using skb->len). Ignored if NULL. * @budget: NAPI budget (use 0 when not called from NAPI poll) * * Would either be called after a successful transmit operation, or after @@ -805,6 +805,8 @@ static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd, DMA_TO_DEVICE); if (cur_p->skb && (status & XAXIDMA_BD_STS_COMPLETE_MASK)) { + if (sizep) + *sizep += cur_p->skb->len; napi_consume_skb(cur_p->skb, budget); packets++; } @@ -818,9 +820,6 @@ static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd, wmb(); cur_p->cntrl = 0; cur_p->status = 0; - - if (sizep) - *sizep += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK; } if (!force) { From 5e77923a3eb39cce91bf08ed7670f816bf86d4af Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Wed, 11 Mar 2026 14:46:52 +0800 Subject: [PATCH 530/712] drm/sysfb: Fix efidrm error handling and memory type mismatch Fix incorrect error checking and memory type confusion in efidrm_device_create(). devm_memremap() returns error pointers, not NULL, and returns system memory while devm_ioremap() returns I/O memory. The code incorrectly passes system memory to iosys_map_set_vaddr_iomem(). Restructure to handle each memory type separately. Use devm_ioremap*() with ERR_PTR(-ENXIO) for WC/UC, and devm_memremap() with ERR_CAST() for WT/WB. Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Signed-off-by: Chen Ni Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260311064652.2903449-1-nichen@iscas.ac.cn --- drivers/gpu/drm/sysfb/efidrm.c | 46 +++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c index 50e0aeef709c..9d84caf69627 100644 --- a/drivers/gpu/drm/sysfb/efidrm.c +++ b/drivers/gpu/drm/sysfb/efidrm.c @@ -151,7 +151,6 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, struct drm_sysfb_device *sysfb; struct drm_device *dev; struct resource *mem = NULL; - void __iomem *screen_base = NULL; struct drm_plane *primary_plane; struct drm_crtc *crtc; struct drm_encoder *encoder; @@ -238,21 +237,38 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, mem_flags = efidrm_get_mem_flags(dev, res->start, vsize); - if (mem_flags & EFI_MEMORY_WC) - screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); - else if (mem_flags & EFI_MEMORY_UC) - screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - else if (mem_flags & EFI_MEMORY_WT) - screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem), - MEMREMAP_WT); - else if (mem_flags & EFI_MEMORY_WB) - screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem), - MEMREMAP_WB); - else + if (mem_flags & EFI_MEMORY_WC) { + void __iomem *screen_base = devm_ioremap_wc(&pdev->dev, mem->start, + resource_size(mem)); + + if (!screen_base) + return ERR_PTR(-ENXIO); + iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); + } else if (mem_flags & EFI_MEMORY_UC) { + void __iomem *screen_base = devm_ioremap(&pdev->dev, mem->start, + resource_size(mem)); + + if (!screen_base) + return ERR_PTR(-ENXIO); + iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); + } else if (mem_flags & EFI_MEMORY_WT) { + void *screen_base = devm_memremap(&pdev->dev, mem->start, + resource_size(mem), MEMREMAP_WT); + + if (IS_ERR(screen_base)) + return ERR_CAST(screen_base); + iosys_map_set_vaddr(&sysfb->fb_addr, screen_base); + } else if (mem_flags & EFI_MEMORY_WB) { + void *screen_base = devm_memremap(&pdev->dev, mem->start, + resource_size(mem), MEMREMAP_WB); + + if (IS_ERR(screen_base)) + return ERR_CAST(screen_base); + iosys_map_set_vaddr(&sysfb->fb_addr, screen_base); + } else { drm_err(dev, "invalid mem_flags: 0x%llx\n", mem_flags); - if (!screen_base) - return ERR_PTR(-ENOMEM); - iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); + return ERR_PTR(-EINVAL); + } /* * Modesetting From 51e3eb3d074a8c7c306d447612011cf8568c8d6f Mon Sep 17 00:00:00 2001 From: Sachin Mokashi Date: Fri, 27 Mar 2026 09:14:39 -0400 Subject: [PATCH 531/712] ASoC: Intel: ehl_rt5660: Use the correct rtd->dev device in hw_params In rt5660_hw_params(), the error path for snd_soc_dai_set_sysclk() correctly uses rtd->dev as the logging device, but the error path for snd_soc_dai_set_pll() uses codec_dai->dev instead. These two devices are distinct: - rtd->dev is the platform device of the PCM runtime (the Intel HDA/SSP controller, e.g. 0000:00:1f.3), which owns the machine driver callback. - codec_dai->dev is the I2C device of the rt5660 codec itself (i2c-10EC5660:00). Since hw_params is a machine driver operation and both calls are made within the same function from the machine driver's context, all error messages should be attributed to rtd->dev. Using codec_dai->dev for one of them would suggest the error originates inside the codec driver, which is misleading. Align the PLL error log with the sysclk one to use rtd->dev, matching the convention used by all other Intel board drivers in this directory. Signed-off-by: Sachin Mokashi Link: https://patch.msgid.link/20260327131439.1330373-1-sachin.mokashi@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/ehl_rt5660.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c index 5c7b218f22b7..c40cd9fb1a26 100644 --- a/sound/soc/intel/boards/ehl_rt5660.c +++ b/sound/soc/intel/boards/ehl_rt5660.c @@ -127,7 +127,7 @@ static int rt5660_hw_params(struct snd_pcm_substream *substream, params_rate(params) * 50, params_rate(params) * 512); if (ret < 0) - dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret); + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; } From 45b859b0728267a6199ee5002d62e6c6f3e8c89d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 27 Mar 2026 10:49:52 +0100 Subject: [PATCH 532/712] thermal: core: Address thermal zone removal races with resume Since thermal_zone_pm_complete() and thermal_zone_device_resume() re-initialize the poll_queue delayed work for the given thermal zone, the cancel_delayed_work_sync() in thermal_zone_device_unregister() may miss some already running work items and the thermal zone may be freed prematurely [1]. There are two failing scenarios that both start with running thermal_pm_notify_complete() right before invoking thermal_zone_device_unregister() for one of the thermal zones. In the first scenario, there is a work item already running for the given thermal zone when thermal_pm_notify_complete() calls thermal_zone_pm_complete() for that thermal zone and it continues to run when thermal_zone_device_unregister() starts. Since the poll_queue delayed work has been re-initialized by thermal_pm_notify_complete(), the running work item will be missed by the cancel_delayed_work_sync() in thermal_zone_device_unregister() and if it continues to run past the freeing of the thermal zone object, a use-after-free will occur. In the second scenario, thermal_zone_device_resume() queued up by thermal_pm_notify_complete() runs right after the thermal_zone_exit() called by thermal_zone_device_unregister() has returned. The poll_queue delayed work is re-initialized by it before cancel_delayed_work_sync() is called by thermal_zone_device_unregister(), so it may continue to run after the freeing of the thermal zone object, which also leads to a use-after-free. Address the first failing scenario by ensuring that no thermal work items will be running when thermal_pm_notify_complete() is called. For this purpose, first move the cancel_delayed_work() call from thermal_zone_pm_complete() to thermal_zone_pm_prepare() to prevent new work from entering the workqueue going forward. Next, switch over to using a dedicated workqueue for thermal events and update the code in thermal_pm_notify() to flush that workqueue after thermal_pm_notify_prepare() has returned which will take care of all leftover thermal work already on the workqueue (that leftover work would do nothing useful anyway because all of the thermal zones have been flagged as suspended). The second failing scenario is addressed by adding a tz->state check to thermal_zone_device_resume() to prevent it from re-initializing the poll_queue delayed work if the thermal zone is going away. Note that the above changes will also facilitate relocating the suspend and resume of thermal zones closer to the suspend and resume of devices, respectively. Fixes: 5a5efdaffda5 ("thermal: core: Resume thermal zones asynchronously") Reported-by: syzbot+3b3852c6031d0f30dfaf@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=3b3852c6031d0f30dfaf Reported-by: Mauricio Faria de Oliveira Closes: https://lore.kernel.org/linux-pm/20260324-thermal-core-uaf-init_delayed_work-v1-1-6611ae76a8a1@igalia.com/ [1] Signed-off-by: Rafael J. Wysocki Reviewed-by: Mauricio Faria de Oliveira Tested-by: Mauricio Faria de Oliveira Reviewed-by: Lukasz Luba Cc: All applicable Link: https://patch.msgid.link/6267615.lOV4Wx5bFT@rafael.j.wysocki --- drivers/thermal/thermal_core.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b7d706ed7ed9..5337612e484d 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -41,6 +41,8 @@ static struct thermal_governor *def_governor; static bool thermal_pm_suspended; +static struct workqueue_struct *thermal_wq __ro_after_init; + /* * Governor section: set of functions to handle thermal governors * @@ -313,7 +315,7 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, if (delay > HZ) delay = round_jiffies_relative(delay); - mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay); + mod_delayed_work(thermal_wq, &tz->poll_queue, delay); } static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) @@ -1785,6 +1787,10 @@ static void thermal_zone_device_resume(struct work_struct *work) guard(thermal_zone)(tz); + /* If the thermal zone is going away, there's nothing to do. */ + if (tz->state & TZ_STATE_FLAG_EXIT) + return; + tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING); thermal_debug_tz_resume(tz); @@ -1811,6 +1817,9 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) } tz->state |= TZ_STATE_FLAG_SUSPENDED; + + /* Prevent new work from getting to the workqueue subsequently. */ + cancel_delayed_work(&tz->poll_queue); } static void thermal_pm_notify_prepare(void) @@ -1829,8 +1838,6 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz) { guard(thermal_zone)(tz); - cancel_delayed_work(&tz->poll_queue); - reinit_completion(&tz->resume); tz->state |= TZ_STATE_FLAG_RESUMING; @@ -1840,7 +1847,7 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz) */ INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume); /* Queue up the work without a delay. */ - mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0); + mod_delayed_work(thermal_wq, &tz->poll_queue, 0); } static void thermal_pm_notify_complete(void) @@ -1863,6 +1870,11 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_RESTORE_PREPARE: case PM_SUSPEND_PREPARE: thermal_pm_notify_prepare(); + /* + * Allow any leftover thermal work items already on the + * worqueue to complete so they don't get in the way later. + */ + flush_workqueue(thermal_wq); break; case PM_POST_HIBERNATION: case PM_POST_RESTORE: @@ -1895,9 +1907,16 @@ static int __init thermal_init(void) if (result) goto error; + thermal_wq = alloc_workqueue("thermal_events", + WQ_FREEZABLE | WQ_POWER_EFFICIENT | WQ_PERCPU, 0); + if (!thermal_wq) { + result = -ENOMEM; + goto unregister_netlink; + } + result = thermal_register_governors(); if (result) - goto unregister_netlink; + goto destroy_workqueue; thermal_class = kzalloc_obj(*thermal_class); if (!thermal_class) { @@ -1924,6 +1943,8 @@ static int __init thermal_init(void) unregister_governors: thermal_unregister_governors(); +destroy_workqueue: + destroy_workqueue(thermal_wq); unregister_netlink: thermal_netlink_exit(); error: From ec327abae5edd1d5b60ea9f920212970133171d2 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 14 Mar 2026 11:19:51 +0000 Subject: [PATCH 533/712] rust_binder: use AssertSync for BINDER_VM_OPS When declaring an immutable global variable in Rust, the compiler checks that it looks thread safe, because it is generally safe to access said global variable. When using C bindings types for these globals, we don't really want this check, because it is conservative and assumes pointers are not thread safe. In the case of BINDER_VM_OPS, this is a challenge when combined with the patch 'userfaultfd: introduce vm_uffd_ops' [1], which introduces a pointer field to vm_operations_struct. It previously only held function pointers, which are considered thread safe. Rust Binder should not be assuming that vm_operations_struct contains no pointer fields, so to fix this, use AssertSync (which Rust Binder has already declared for another similar global of type struct file_operations with the same problem). This ensures that even if another commit adds a pointer field to vm_operations_struct, this does not cause problems. Fixes: 8ef2c15aeae0 ("rust_binder: check ownership before using vma") Cc: stable Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603121235.tpnRxFKO-lkp@intel.com/ Link: https://lore.kernel.org/r/20260306171815.3160826-8-rppt@kernel.org [1] Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260314111951.4139029-1-aliceryhl@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/page_range.rs | 8 +++++--- drivers/android/binder/rust_binder_main.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index 9dfc154e5dd4..b57e0c7ba3f1 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -13,6 +13,8 @@ // // The shrinker will use trylock methods because it locks them in a different order. +use crate::AssertSync; + use core::{ marker::PhantomPinned, mem::{size_of, size_of_val, MaybeUninit}, @@ -143,14 +145,14 @@ pub(crate) struct ShrinkablePageRange { } // We do not define any ops. For now, used only to check identity of vmas. -static BINDER_VM_OPS: bindings::vm_operations_struct = pin_init::zeroed(); +static BINDER_VM_OPS: AssertSync = AssertSync(pin_init::zeroed()); // To ensure that we do not accidentally install pages into or zap pages from the wrong vma, we // check its vm_ops and private data before using it. fn check_vma(vma: &virt::VmaRef, owner: *const ShrinkablePageRange) -> Option<&virt::VmaMixedMap> { // SAFETY: Just reading the vm_ops pointer of any active vma is safe. let vm_ops = unsafe { (*vma.as_ptr()).vm_ops }; - if !ptr::eq(vm_ops, &BINDER_VM_OPS) { + if !ptr::eq(vm_ops, &BINDER_VM_OPS.0) { return None; } @@ -342,7 +344,7 @@ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result { // SAFETY: We own the vma, and we don't use any methods on VmaNew that rely on // `vm_ops`. - unsafe { (*vma.as_ptr()).vm_ops = &BINDER_VM_OPS }; + unsafe { (*vma.as_ptr()).vm_ops = &BINDER_VM_OPS.0 }; Ok(num_pages) } diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index aa5f2a75adb4..014010662df8 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -306,7 +306,7 @@ fn init(_module: &'static kernel::ThisModule) -> Result { /// Makes the inner type Sync. #[repr(transparent)] pub struct AssertSync(T); -// SAFETY: Used only to insert `file_operations` into a global, which is safe. +// SAFETY: Used only to insert C bindings types into globals, which is safe. unsafe impl Sync for AssertSync {} /// File operations that rust_binderfs.c can use. From 8c27b1bce059a11a8d3c8682984e13866f0714af Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 30 Mar 2026 11:38:30 +0300 Subject: [PATCH 534/712] mei: me: reduce the scope on unexpected reset After commit 2cedb296988c ("mei: me: trigger link reset if hw ready is unexpected") some devices started to show long resume times (5-7 seconds). This happens as mei falsely detects unready hardware, starts parallel link reset flow and triggers link reset timeouts in the resume callback. Address it by performing detection of unready hardware only when driver is in the MEI_DEV_ENABLED state instead of blacklisting states as done in the original patch. This eliminates active waitqueue check as in MEI_DEV_ENABLED state there will be no active waitqueue. Reviewed-by: Rafael J. Wysocki (Intel) Reported-by: Todd Brandt Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221023 Tested-by: Todd Brandt Fixes: 2cedb296988c ("mei: me: trigger link reset if hw ready is unexpected") Cc: stable Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260330083830.536056-1-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index d4612c659784..1e4a41ac428f 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1337,19 +1337,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if we need to start the dev */ if (!mei_host_is_ready(dev)) { if (mei_hw_is_ready(dev)) { - /* synchronized by dev mutex */ - if (waitqueue_active(&dev->wait_hw_ready)) { - dev_dbg(&dev->dev, "we need to start the dev.\n"); - dev->recvd_hw_ready = true; - wake_up(&dev->wait_hw_ready); - } else if (dev->dev_state != MEI_DEV_UNINITIALIZED && - dev->dev_state != MEI_DEV_POWERING_DOWN && - dev->dev_state != MEI_DEV_POWER_DOWN) { + if (dev->dev_state == MEI_DEV_ENABLED) { dev_dbg(&dev->dev, "Force link reset.\n"); schedule_work(&dev->reset_work); } else { - dev_dbg(&dev->dev, "Ignore this interrupt in state = %d\n", - dev->dev_state); + dev_dbg(&dev->dev, "we need to start the dev.\n"); + dev->recvd_hw_ready = true; + wake_up(&dev->wait_hw_ready); } } else { dev_dbg(&dev->dev, "Spurious Interrupt\n"); From 69335152910b775e7835939d5c863c580c605275 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 27 Jan 2026 20:11:05 +0100 Subject: [PATCH 535/712] misc/mei: INTEL_MEI should depend on X86 or DRM_XE The Intel Management Engine Interface is only present on x86 platforms and Intel Xe graphics cards. Hence add a dependency on X86 or DRM_XE, to prevent asking the user about this driver when configuring a kernel for a non-x86 architecture and without Xe graphics support. Fixes: 25f9b0d35155 ("misc/mei: Allow building Intel ME interface on non-x86") Cc: stable Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/8e2646fb71b148b3d38beb13f19b14e3634a1e1a.1769541024.git.geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index 5902dd1ee44b..094fb1dde0fe 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -3,6 +3,7 @@ config INTEL_MEI tristate "Intel Management Engine Interface" depends on PCI + depends on X86 || DRM_XE!=n || COMPILE_TEST default X86_64 || MATOM help The Intel Management Engine (Intel ME) provides Manageability, From e920c36f2073d533bdf19ba6ab690432c8173b63 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Wed, 25 Mar 2026 00:15:21 +0000 Subject: [PATCH 536/712] ASoC: Intel: boards: fix unmet dependency on PINCTRL This reverts commit c073f0757663 ("ASoC: Intel: sof_sdw: select PINCTRL_CS42L43 and SPI_CS42L43") Currently, SND_SOC_INTEL_SOUNDWIRE_SOF_MACH selects PINCTRL_CS42L43 without also selecting or depending on PINCTRL, despite PINCTRL_CS42L43 depending on PINCTRL. See the following Kbuild warning: WARNING: unmet direct dependencies detected for PINCTRL_CS42L43 Depends on [n]: PINCTRL [=n] && MFD_CS42L43 [=m] Selected by [m]: - SND_SOC_INTEL_SOUNDWIRE_SOF_MACH [=m] && SOUND [=y] && SND [=m] && SND_SOC [=m] && SND_SOC_INTEL_MACH [=y] && (SND_SOC_SOF_INTEL_COMMON [=m] || !SND_SOC_SOF_INTEL_COMMON [=m]) && SND_SOC_SOF_INTEL_SOUNDWIRE [=m] && I2C [=y] && SPI_MASTER [=y] && ACPI [=y] && (MFD_INTEL_LPSS [=n] || COMPILE_TEST [=y]) && (SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES [=n] || COMPILE_TEST [=y]) && SOUNDWIRE [=m] In response to v1 of this patch [1], Arnd pointed out that there is no compile-time dependency sof_sdw and the PINCTRL_CS42L43 driver. After testing, I can confirm that the kernel compiled with SND_SOC_INTEL_SOUNDWIRE_SOF_MACH enabled and PINCTRL_CS42L43 disabled. This unmet dependency was detected by kconfirm, a static analysis tool for Kconfig. Link: https://lore.kernel.org/all/b8aecc71-1fed-4f52-9f6c-263fbe56d493@app.fastmail.com/ [1] Fixes: c073f0757663 ("ASoC: Intel: sof_sdw: select PINCTRL_CS42L43 and SPI_CS42L43") Signed-off-by: Julian Braha Acked-by: Arnd Bergmann Link: https://patch.msgid.link/20260325001522.1727678-1-julianbraha@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index c5942b5655d3..d53af8f7e55b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -530,8 +530,6 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_CS42L43_SDW select MFD_CS42L43 select MFD_CS42L43_SDW - select PINCTRL_CS42L43 - select SPI_CS42L43 select SND_SOC_CS35L56_SPI select SND_SOC_CS35L56_SDW select SND_SOC_DMIC From fa6e24963342de4370e3a3c9af41e38277b74cf3 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Fri, 27 Mar 2026 23:30:00 -0700 Subject: [PATCH 537/712] bridge: mrp: reject zero test interval to avoid OOM panic br_mrp_start_test() and br_mrp_start_in_test() accept the user-supplied interval value from netlink without validation. When interval is 0, usecs_to_jiffies(0) yields 0, causing the delayed work (br_mrp_test_work_expired / br_mrp_in_test_work_expired) to reschedule itself with zero delay. This creates a tight loop on system_percpu_wq that allocates and transmits MRP test frames at maximum rate, exhausting all system memory and causing a kernel panic via OOM deadlock. The same zero-interval issue applies to br_mrp_start_in_test_parse() for interconnect test frames. Use NLA_POLICY_MIN(NLA_U32, 1) in the nla_policy tables for both IFLA_BRIDGE_MRP_START_TEST_INTERVAL and IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL, so zero is rejected at the netlink attribute parsing layer before the value ever reaches the workqueue scheduling code. This is consistent with how other bridge subsystems (br_fdb, br_mst) enforce range constraints on netlink attributes. Fixes: 20f6a05ef635 ("bridge: mrp: Rework the MRP netlink interface") Fixes: 7ab1748e4ce6 ("bridge: mrp: Extend MRP netlink interface for configuring MRP interconnect") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260328063000.1845376-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/bridge/br_mrp_netlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c index ce6f63c77cc0..86f0e75d6e34 100644 --- a/net/bridge/br_mrp_netlink.c +++ b/net/bridge/br_mrp_netlink.c @@ -196,7 +196,7 @@ static const struct nla_policy br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = { [IFLA_BRIDGE_MRP_START_TEST_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_MRP_START_TEST_RING_ID] = { .type = NLA_U32 }, - [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1), [IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NLA_U32 }, @@ -316,7 +316,7 @@ static const struct nla_policy br_mrp_start_in_test_policy[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1] = { [IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] = { .type = NLA_U32 }, - [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1), [IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD] = { .type = NLA_U32 }, }; From 217d5bc9f96272316ac5a3215c7cc32a5127bbf3 Mon Sep 17 00:00:00 2001 From: Alexander Savenko Date: Tue, 31 Mar 2026 11:29:28 +0300 Subject: [PATCH 538/712] ALSA: hda/realtek: Add quirk for Lenovo Yoga Pro 7 14IMH9 The Lenovo Yoga Pro 7 14IMH9 (DMI: 83E2) shares PCI SSID 17aa:3847 with the Legion 7 16ACHG6, but has a different codec subsystem ID (17aa:38cf). The existing SND_PCI_QUIRK for 17aa:3847 applies ALC287_FIXUP_LEGION_16ACHG6, which attempts to initialize an external I2C amplifier (CLSA0100) that is not present on the Yoga Pro 7 14IMH9. As a result, pin 0x17 (bass speakers) is connected to DAC 0x06 which has no volume control, making hardware volume adjustment completely non-functional. Audio is either silent or at maximum volume regardless of the slider position. Add a HDA_CODEC_QUIRK entry using the codec subsystem ID (17aa:38cf) to correctly identify the Yoga Pro 7 14IMH9 and apply ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN, which redirects pin 0x17 to DAC 0x02 and restores proper volume control. The existing Legion entry is preserved unchanged. This follows the same pattern used for 17aa:386e, where Legion Y9000X and Yoga Pro 7 14ARP8 share a PCI SSID but are distinguished via HDA_CODEC_QUIRK. Link: https://github.com/nomad4tech/lenovo-yoga-pro-7-linux Tested-by: Alexander Savenko Signed-off-by: Alexander Savenko Link: https://patch.msgid.link/20260331082929.44890-1-alex.sav4387@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index fa5f08cd2c65..c6f878678986 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7614,6 +7614,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I), + /* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6; + * use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x38cf, "Lenovo Yoga Pro 7 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), From dd9b99b822684f421f9b7e1e5a69d791ffc1d48f Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 31 Mar 2026 17:46:14 +0800 Subject: [PATCH 539/712] ALSA: hda/realtek: add quirk for Acer Swift SFG14-73 fix mute/micmute LEDs and headset microphone for Acer Swift SFG14-73. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220279 Cc: stable@vger.kernel.org Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260331094614.186063-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c6f878678986..6db23ce8a5fe 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6720,6 +6720,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), From bbe5ab8191a33572c11be8628c55b79246307125 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Tue, 31 Mar 2026 11:11:07 -0400 Subject: [PATCH 540/712] cgroup/cpuset: Simplify setsched decision check in task iteration loop of cpuset_can_attach() Centralize the check required to run security_task_setscheduler() in the task iteration loop of cpuset_can_attach() outside of the loop as it has no dependency on the characteristics of the tasks themselves. There is no functional change. Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/cpuset.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index d21868455341..58c5b7b72cca 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2988,7 +2988,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) struct cgroup_subsys_state *css; struct cpuset *cs, *oldcs; struct task_struct *task; - bool cpus_updated, mems_updated; + bool setsched_check; int ret; /* used later by cpuset_attach() */ @@ -3003,20 +3003,21 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) if (ret) goto out_unlock; - cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus); - mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems); + /* + * Skip rights over task setsched check in v2 when nothing changes, + * migration permission derives from hierarchy ownership in + * cgroup_procs_write_permission()). + */ + setsched_check = !cpuset_v2() || + !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus) || + !nodes_equal(cs->effective_mems, oldcs->effective_mems); cgroup_taskset_for_each(task, css, tset) { ret = task_can_attach(task); if (ret) goto out_unlock; - /* - * Skip rights over task check in v2 when nothing changes, - * migration permission derives from hierarchy ownership in - * cgroup_procs_write_permission()). - */ - if (!cpuset_v2() || (cpus_updated || mems_updated)) { + if (setsched_check) { ret = security_task_setscheduler(task); if (ret) goto out_unlock; From 089f3fcd690c71cb3d8ca09f34027764e28920a0 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Tue, 31 Mar 2026 11:11:08 -0400 Subject: [PATCH 541/712] cgroup/cpuset: Skip security check for hotplug induced v1 task migration When a CPU hot removal causes a v1 cpuset to lose all its CPUs, the cpuset hotplug handler will schedule a work function to migrate tasks in that cpuset with no CPU to its ancestor to enable those tasks to continue running. If a strict security policy is in place, however, the task migration may fail when security_task_setscheduler() call in cpuset_can_attach() returns a -EACCES error. That will mean that those tasks will have no CPU to run on. The system administrators will have to explicitly intervene to either add CPUs to that cpuset or move the tasks elsewhere if they are aware of it. This problem was found by a reported test failure in the LTP's cpuset_hotplug_test.sh. Fix this problem by treating this special case as an exception to skip the setsched security check in cpuset_can_attach() when a v1 cpuset with tasks have no CPU left. With that patch applied, the cpuset_hotplug_test.sh test can be run successfully without failure. Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/cpuset.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 58c5b7b72cca..1335e437098e 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -3012,6 +3012,16 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus) || !nodes_equal(cs->effective_mems, oldcs->effective_mems); + /* + * A v1 cpuset with tasks will have no CPU left only when CPU hotplug + * brings the last online CPU offline as users are not allowed to empty + * cpuset.cpus when there are active tasks inside. When that happens, + * we should allow tasks to migrate out without security check to make + * sure they will be able to run after migration. + */ + if (!is_in_v2_mode() && cpumask_empty(oldcs->effective_cpus)) + setsched_check = false; + cgroup_taskset_for_each(task, css, tset) { ret = task_can_attach(task); if (ret) From 4625fe5bbdaccd45be274c30ff0a42e30d4e38cf Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 27 Jan 2026 16:33:34 +0900 Subject: [PATCH 542/712] drm: gpu: msm: forbid mem reclaim from reset We sometimes get into a situtation where GPU hangcheck fails to recover GPU: [..] msm_dpu ae01000.display-controller: [drm:hangcheck_handler] *ERROR* (IPv4: 1): hangcheck detected gpu lockup rb 0! msm_dpu ae01000.display-controller: [drm:hangcheck_handler] *ERROR* (IPv4: 1): completed fence: 7840161 msm_dpu ae01000.display-controller: [drm:hangcheck_handler] *ERROR* (IPv4: 1): submitted fence: 7840162 msm_dpu ae01000.display-controller: [drm:hangcheck_handler] *ERROR* (IPv4: 1): hangcheck detected gpu lockup rb 0! msm_dpu ae01000.display-controller: [drm:hangcheck_handler] *ERROR* (IPv4: 1): completed fence: 7840162 msm_dpu ae01000.display-controller: [drm:hangcheck_handler] *ERROR* (IPv4: 1): submitted fence: 7840163 [..] The problem is that msm_job worker is blocked on gpu->lock INFO: task ring0:155 blocked for more than 122 seconds. Not tainted 6.6.99-08727-gaac38b365d2c #1 task:ring0 state:D stack:0 pid:155 ppid:2 flags:0x00000008 Call trace: __switch_to+0x108/0x208 schedule+0x544/0x11f0 schedule_preempt_disabled+0x30/0x50 __mutex_lock_common+0x410/0x850 __mutex_lock_slowpath+0x28/0x40 mutex_lock+0x5c/0x90 msm_job_run+0x9c/0x140 drm_sched_main+0x514/0x938 kthread+0x114/0x138 ret_from_fork+0x10/0x20 which is owned by recover worker, which is waiting for DMA fences from a memory reclaim path, under the very same gpu->lock INFO: task ring0:155 is blocked on a mutex likely owned by task gpu-worker:154. task:gpu-worker state:D stack:0 pid:154 ppid:2 flags:0x00000008 Call trace: __switch_to+0x108/0x208 schedule+0x544/0x11f0 schedule_timeout+0x1f8/0x770 dma_fence_default_wait+0x108/0x218 dma_fence_wait_timeout+0x6c/0x1c0 dma_resv_wait_timeout+0xe4/0x118 active_purge+0x34/0x98 drm_gem_lru_scan+0x1d0/0x388 msm_gem_shrinker_scan+0x1cc/0x2e8 shrink_slab+0x228/0x478 shrink_node+0x380/0x730 try_to_free_pages+0x204/0x510 __alloc_pages_direct_reclaim+0x90/0x158 __alloc_pages_slowpath+0x1d4/0x4a0 __alloc_pages+0x9f0/0xc88 vm_area_alloc_pages+0x17c/0x260 __vmalloc_node_range+0x1c0/0x420 kvmalloc_node+0xe8/0x108 msm_gpu_crashstate_capture+0x1e4/0x280 recover_worker+0x1c0/0x638 kthread_worker_fn+0x150/0x2d8 kthread+0x114/0x138 So no one can make any further progress. Forbid recover/fault worker to enter memory reclaim (under gpu->lock) to address this deadlock scenario. Cc: Tomasz Figa Signed-off-by: Sergey Senozhatsky Reviewed-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/700978/ Message-ID: <20260127073341.2862078-1-senozhatsky@chromium.org> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gpu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 995549d0bbbc..ddcd9e1c217a 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -17,6 +17,7 @@ #include #include #include +#include /* * Power Management: @@ -469,6 +470,7 @@ static void recover_worker(struct kthread_work *work) struct msm_gem_submit *submit; struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu); char *comm = NULL, *cmd = NULL; + unsigned int noreclaim_flag; struct task_struct *task; int i; @@ -506,6 +508,8 @@ static void recover_worker(struct kthread_work *work) msm_gem_vm_unusable(submit->vm); } + noreclaim_flag = memalloc_noreclaim_save(); + get_comm_cmdline(submit, &comm, &cmd); if (comm && cmd) { @@ -524,6 +528,8 @@ static void recover_worker(struct kthread_work *work) pm_runtime_get_sync(&gpu->pdev->dev); msm_gpu_crashstate_capture(gpu, submit, NULL, comm, cmd); + memalloc_noreclaim_restore(noreclaim_flag); + kfree(cmd); kfree(comm); @@ -588,6 +594,7 @@ void msm_gpu_fault_crashstate_capture(struct msm_gpu *gpu, struct msm_gpu_fault_ struct msm_gem_submit *submit; struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu); char *comm = NULL, *cmd = NULL; + unsigned int noreclaim_flag; mutex_lock(&gpu->lock); @@ -595,6 +602,8 @@ void msm_gpu_fault_crashstate_capture(struct msm_gpu *gpu, struct msm_gpu_fault_ if (submit && submit->fault_dumped) goto resume_smmu; + noreclaim_flag = memalloc_noreclaim_save(); + if (submit) { get_comm_cmdline(submit, &comm, &cmd); @@ -610,6 +619,8 @@ void msm_gpu_fault_crashstate_capture(struct msm_gpu *gpu, struct msm_gpu_fault_ msm_gpu_crashstate_capture(gpu, submit, fault_info, comm, cmd); pm_runtime_put_sync(&gpu->pdev->dev); + memalloc_noreclaim_restore(noreclaim_flag); + kfree(cmd); kfree(comm); From 6a0b843a16751e3b1ebac794984d4e48384f9078 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Mon, 9 Feb 2026 20:46:03 -0500 Subject: [PATCH 543/712] dt-bindings: display/msm/gmu: Add SDM670 compatible The Snapdragon 670 has a GMU. Add its compatible. Signed-off-by: Richard Acayan Acked-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/703803/ Message-ID: <20260210014603.1372-2-mailingradian@gmail.com> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/display/msm/gmu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/gmu.yaml b/Documentation/devicetree/bindings/display/msm/gmu.yaml index e32056ae0f5d..93e5e6e19754 100644 --- a/Documentation/devicetree/bindings/display/msm/gmu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gmu.yaml @@ -91,6 +91,7 @@ allOf: compatible: contains: enum: + - qcom,adreno-gmu-615.0 - qcom,adreno-gmu-618.0 - qcom,adreno-gmu-630.2 then: From 01a0d6cd7032e9993feea19fadb03ef9d5b488f2 Mon Sep 17 00:00:00 2001 From: Anna Maniscalco Date: Tue, 10 Feb 2026 17:29:42 +0100 Subject: [PATCH 544/712] drm/msm: always recover the gpu Previously, in case there was no more work to do, recover worker wouldn't trigger recovery and would instead rely on the gpu going to sleep and then resuming when more work is submitted. Recover_worker will first increment the fence of the hung ring so, if there's only one job submitted to a ring and that causes an hang, it will early out. There's no guarantee that the gpu will suspend and resume before more work is submitted and if the gpu is in a hung state it will stay in that state and probably trigger a timeout again. Just stop checking and always recover the gpu. Signed-off-by: Anna Maniscalco Cc: stable@vger.kernel.org Patchwork: https://patchwork.freedesktop.org/patch/704066/ Message-ID: <20260210-recovery_suspend_fix-v1-1-00ed9013da04@gmail.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gpu.c | 42 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index ddcd9e1c217a..d901304fff6d 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -553,32 +553,30 @@ static void recover_worker(struct kthread_work *work) msm_update_fence(ring->fctx, fence); } - if (msm_gpu_active(gpu)) { - /* retire completed submits, plus the one that hung: */ - retire_submits(gpu); + /* retire completed submits, plus the one that hung: */ + retire_submits(gpu); - gpu->funcs->recover(gpu); + gpu->funcs->recover(gpu); - /* - * Replay all remaining submits starting with highest priority - * ring - */ - for (i = 0; i < gpu->nr_rings; i++) { - struct msm_ringbuffer *ring = gpu->rb[i]; - unsigned long flags; + /* + * Replay all remaining submits starting with highest priority + * ring + */ + for (i = 0; i < gpu->nr_rings; i++) { + struct msm_ringbuffer *ring = gpu->rb[i]; + unsigned long flags; - spin_lock_irqsave(&ring->submit_lock, flags); - list_for_each_entry(submit, &ring->submits, node) { - /* - * If the submit uses an unusable vm make sure - * we don't actually run it - */ - if (to_msm_vm(submit->vm)->unusable) - submit->nr_cmds = 0; - gpu->funcs->submit(gpu, submit); - } - spin_unlock_irqrestore(&ring->submit_lock, flags); + spin_lock_irqsave(&ring->submit_lock, flags); + list_for_each_entry(submit, &ring->submits, node) { + /* + * If the submit uses an unusable vm make sure + * we don't actually run it + */ + if (to_msm_vm(submit->vm)->unusable) + submit->nr_cmds = 0; + gpu->funcs->submit(gpu, submit); } + spin_unlock_irqrestore(&ring->submit_lock, flags); } pm_runtime_put(&gpu->pdev->dev); From cb9af17e11ec82ca729660f7e9f536bd6d6928a6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 1 Mar 2026 15:20:34 +0100 Subject: [PATCH 545/712] dt-bindings: display/msm/gpu: Drop redundant reg-names in one if:then: Top-level reg-names defines already proper order for "reg-names" with minItems: 1, so no need to repeat it again in one of "if:then:" cases. Signed-off-by: Krzysztof Kozlowski Reviewed-by: David Heidelberg Reviewed-by: Dmitry Baryshkov Acked-by: Rob Herring (Arm) Patchwork: https://patchwork.freedesktop.org/patch/707987/ Message-ID: <20260301142033.88851-2-krzysztof.kozlowski@oss.qualcomm.com> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/display/msm/gpu.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml index ec84b64d4c00..04b2328903ca 100644 --- a/Documentation/devicetree/bindings/display/msm/gpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml @@ -440,13 +440,6 @@ allOf: clocks: false clock-names: false - reg-names: - minItems: 1 - items: - - const: kgsl_3d0_reg_memory - - const: cx_mem - - const: cx_dbgc - examples: - | From cc53487e01fc209079b703730e0c513b06975545 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Mar 2026 11:34:33 -0700 Subject: [PATCH 546/712] drm/msm/adreno: Change chip_id format The "ipv4-style" %u.%u.%u.%u used to make sense when the chip_id was simply encoding gen.major.minor.patch. But this hasn't been true for at least a couple years. Switch to %08x, which is still easy enough to read for older devices, and much easier to read with the new scheme. Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/712222/ Message-ID: <20260316183436.671482-2-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 1d0145f8b3ec..6cdfafcb0c57 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -67,12 +67,8 @@ enum adreno_family { /* Helper for formating the chip_id in the way that userspace tools like * crashdec expect. */ -#define ADRENO_CHIPID_FMT "u.%u.%u.%u" -#define ADRENO_CHIPID_ARGS(_c) \ - (((_c) >> 24) & 0xff), \ - (((_c) >> 16) & 0xff), \ - (((_c) >> 8) & 0xff), \ - ((_c) & 0xff) +#define ADRENO_CHIPID_FMT "08x" +#define ADRENO_CHIPID_ARGS(_c) (_c) struct adreno_gpu; From 9d24ec327690d8b27190331386319a5c98ec61e7 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Mar 2026 11:34:34 -0700 Subject: [PATCH 547/712] drm/msm/a8xx: Update GPU name with slice_mask Once we've updated the chip_id after reading the slice_mask, also update the GPU name so it matches. Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/712225/ Message-ID: <20260316183436.671482-3-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index b1887e0cf698..dd5e06868706 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -87,6 +87,7 @@ void a8xx_gpu_get_slice_info(struct msm_gpu *gpu) struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); const struct a6xx_info *info = adreno_gpu->info->a6xx; + struct device *dev = &gpu->pdev->dev; u32 slice_mask; if (adreno_gpu->info->family < ADRENO_8XX_GEN1) @@ -110,6 +111,15 @@ void a8xx_gpu_get_slice_info(struct msm_gpu *gpu) /* Chip ID depends on the number of slices available. So update it */ adreno_gpu->chip_id |= FIELD_PREP(GENMASK(7, 4), hweight32(slice_mask)); + + /* Update the gpu-name to reflect the slice config: */ + const char *name = devm_kasprintf(dev, GFP_KERNEL, + "%"ADRENO_CHIPID_FMT, + ADRENO_CHIPID_ARGS(adreno_gpu->chip_id)); + if (name) { + devm_kfree(dev, adreno_gpu->base.name); + adreno_gpu->base.name = name; + } } static u32 a8xx_get_first_slice(struct a6xx_gpu *a6xx_gpu) From 8a7023b035355ef5bfa096bd323256fa8abbbc6a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Mar 2026 11:44:42 -0700 Subject: [PATCH 548/712] drm/msm/vma: Avoid lock in VM_BIND fence signaling path Use msm_gem_unpin_active(), similar to what is used in the GEM_SUBMIT path. This avoids needing to hold the obj lock, and the end result is the same. (As with GEM_SUBMIT, we know the fence isn't signaled yet.) Reported-by: Akhil P Oommen Fixes: 2e6a8a1fe2b2 ("drm/msm: Add VM_BIND ioctl") Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/712230/ Message-ID: <20260316184442.673558-1-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem.c | 3 +++ drivers/gpu/drm/msm/msm_gem_vma.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index da74f1413f94..74636646d80e 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -507,8 +507,11 @@ void msm_gem_unpin_locked(struct drm_gem_object *obj) */ void msm_gem_unpin_active(struct drm_gem_object *obj) { + struct msm_drm_private *priv = obj->dev->dev_private; struct msm_gem_object *msm_obj = to_msm_bo(obj); + GEM_WARN_ON(!mutex_is_locked(&priv->lru.lock)); + msm_obj->pin_count--; GEM_WARN_ON(msm_obj->pin_count < 0); update_lru_active(obj); diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 3a6c435d5105..1c6b0920c0d8 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -696,6 +696,7 @@ static struct dma_fence * msm_vma_job_run(struct drm_sched_job *_job) { struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job); + struct msm_drm_private *priv = job->vm->drm->dev_private; struct msm_gem_vm *vm = to_msm_vm(job->vm); struct drm_gem_object *obj; int ret = vm->unusable ? -EINVAL : 0; @@ -738,12 +739,14 @@ msm_vma_job_run(struct drm_sched_job *_job) if (ret) msm_gem_vm_unusable(job->vm); + mutex_lock(&priv->lru.lock); + job_foreach_bo (obj, job) { - msm_gem_lock(obj); - msm_gem_unpin_locked(obj); - msm_gem_unlock(obj); + msm_gem_unpin_active(obj); } + mutex_unlock(&priv->lru.lock); + /* VM_BIND ops are synchronous, so no fence to wait on: */ return NULL; } From d4ef6d77bb1ef92bdbfb70c7a5d08072848357d8 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 23 Mar 2026 09:16:02 -0700 Subject: [PATCH 549/712] drm/msm/a6xx: Add missing aperture_lock init Looks like this was somehow missed when introducing gen8 support. Fixes: 288a93200892 ("drm/msm/adreno: Introduce A8x GPU Support") Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/713545/ Message-ID: <20260323161603.1165108-1-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 2129d230a92b..a00215b7bd1e 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -2656,6 +2656,7 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) gpu = &adreno_gpu->base; mutex_init(&a6xx_gpu->gmu.lock); + spin_lock_init(&a6xx_gpu->aperture_lock); adreno_gpu->registers = NULL; From cf50ccdb765b3a6f1cd8e75642b0439fea0263a5 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 25 Mar 2026 11:59:26 -0700 Subject: [PATCH 550/712] drm/msm: Reject fb creation from _NO_SHARE objs It would be an error to map these into kms->vm. So reject this as early as possible, when creating an fb. Fixes: b58e12a66e47 ("drm/msm: Add _NO_SHARE flag") Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/714264/ Message-ID: <20260325185926.1265661-1-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_fb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 1eff615ff9bf..ce1725990a48 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -219,7 +219,12 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, + mode_cmd->offsets[i]; if (bos[i]->size < min_size) { - ret = -EINVAL; + ret = UERR(EINVAL, dev, "plane %d too small", i); + goto fail; + } + + if (to_msm_bo(bos[i])->flags & MSM_BO_NO_SHARE) { + ret = UERR(EINVAL, dev, "Cannot map _NO_SHARE to kms vm"); goto fail; } From c07612365087c8873f3faa2a47642ffa73b12c54 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 24 Mar 2026 15:05:17 -0700 Subject: [PATCH 551/712] drm/msm: Disallow foreign mapping of _NO_SHARE This restriction applies to mapping of _NO_SHARE objs in the kms vm as well as importing/exporting BOs. Since the DPU has it's own VM, scanout counts as "exporting" a BO from outside of it's host VM. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/713897/ Message-ID: <20260324220519.1221471-1-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem_vma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 1c6b0920c0d8..43d61e0919bd 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -373,6 +373,12 @@ msm_gem_vma_new(struct drm_gpuvm *gpuvm, struct drm_gem_object *obj, struct msm_gem_vma *vma; int ret; + /* _NO_SHARE objs cannot be mapped outside of their "host" vm: */ + if (obj && (to_msm_bo(obj)->flags & MSM_BO_NO_SHARE) && + GEM_WARN_ON(obj->resv != drm_gpuvm_resv(gpuvm))) { + return ERR_PTR(-EINVAL); + } + drm_gpuvm_resv_assert_held(&vm->base); vma = kzalloc(sizeof(*vma), GFP_KERNEL); From 85042c2cd970a6b0e686329387096fe19989ae62 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 24 Mar 2026 15:05:18 -0700 Subject: [PATCH 552/712] drm/msm: Fix VM_BIND UNMAP locking Wrong argument meant that the objs involved in UNMAP ops were not always getting locked. Since _NO_SHARE objs share a common resv with the VM (which is always locked) this would only show up with non-_NO_SHARE BOs. Reported-by: Victoria Brekenfeld Fixes: 2e6a8a1fe2b2 ("drm/msm: Add VM_BIND ioctl") Closes: https://gitlab.freedesktop.org/drm/msm/-/issues/94 Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/713898/ Message-ID: <20260324220519.1221471-2-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem_vma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 43d61e0919bd..953a2403f598 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -1251,7 +1251,7 @@ vm_bind_job_lock_objects(struct msm_vm_bind_job *job, struct drm_exec *exec) case MSM_VM_BIND_OP_UNMAP: ret = drm_gpuvm_sm_unmap_exec_lock(job->vm, exec, op->iova, - op->obj_offset); + op->range); break; case MSM_VM_BIND_OP_MAP: case MSM_VM_BIND_OP_MAP_NULL: { From c289a6db9ba6cb974f0317da142e4f665d589566 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 25 Mar 2026 11:40:42 -0700 Subject: [PATCH 553/712] drm/msm/a6xx: Fix HLSQ register dumping Fix the bitfield offset of HLSQ_READ_SEL state-type bitfield. Otherwise we are always reading TP state when we wanted SP or HLSQ state. Reported-by: Connor Abbott Suggested-by: Connor Abbott Fixes: 1707add81551 ("drm/msm/a6xx: Add a6xx gpu state") Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/714236/ Message-ID: <20260325184043.1259312-1-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index d2d6b2fd3cba..f7598d0c3975 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1013,7 +1013,7 @@ static void a6xx_get_crashdumper_hlsq_registers(struct msm_gpu *gpu, u64 out = dumper->iova + A6XX_CD_DATA_OFFSET; int i, regcount = 0; - in += CRASHDUMP_WRITE(in, REG_A6XX_HLSQ_DBG_READ_SEL, regs->val1); + in += CRASHDUMP_WRITE(in, REG_A6XX_HLSQ_DBG_READ_SEL, (regs->val1 & 0xff) << 8); for (i = 0; i < regs->count; i += 2) { u32 count = RANGE(regs->registers, i); From df0f439e3926817cf577ca6272aad68468ff7624 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 25 Mar 2026 11:41:05 -0700 Subject: [PATCH 554/712] drm/msm/shrinker: Fix can_block() logic The intention here was to allow blocking if DIRECT_RECLAIM or if called from kswapd and KSWAPD_RECLAIM is set. Reported by Claude code review: https://lore.gitlab.freedesktop.org/drm-ai-reviews/review-patch9-20260309151119.290217-10-boris.brezillon@collabora.com/ on a panthor patch which had copied similar logic. Reported-by: Boris Brezillon Fixes: 7860d720a84c ("drm/msm: Fix build break with recent mm tree") Signed-off-by: Rob Clark Reviewed-by: Boris Brezillon Patchwork: https://patchwork.freedesktop.org/patch/714238/ Message-ID: <20260325184106.1259528-1-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem_shrinker.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c index 1039e3c0a47b..31fa51a44f86 100644 --- a/drivers/gpu/drm/msm/msm_gem_shrinker.c +++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c @@ -26,9 +26,8 @@ static bool can_swap(void) static bool can_block(struct shrink_control *sc) { - if (!(sc->gfp_mask & __GFP_DIRECT_RECLAIM)) - return false; - return current_is_kswapd() || (sc->gfp_mask & __GFP_RECLAIM); + return (sc->gfp_mask & __GFP_DIRECT_RECLAIM) || + (current_is_kswapd() && (sc->gfp_mask & __GFP_KSWAPD_RECLAIM)); } static unsigned long From cc83f71c9be0715fe93b963ffa9767d5d84354ed Mon Sep 17 00:00:00 2001 From: Connor Abbott Date: Wed, 25 Mar 2026 16:58:37 -0400 Subject: [PATCH 555/712] drm/msm/a6xx: Fix dumping A650+ debugbus blocks These should be appended after the existing debugbus blocks, instead of replacing them. Fixes: 1e05bba5e2b8 ("drm/msm/a6xx: Update a6xx gpu coredump") Signed-off-by: Connor Abbott Patchwork: https://patchwork.freedesktop.org/patch/714270/ Message-ID: <20260325-drm-msm-a650-debugbus-v1-1-dfbf358890a7@gmail.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index f7598d0c3975..6e4950d513b3 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -361,7 +361,7 @@ static void a6xx_get_debugbus_blocks(struct msm_gpu *gpu, sizeof(*a6xx_state->debugbus)); if (a6xx_state->debugbus) { - int i; + int i, j; for (i = 0; i < ARRAY_SIZE(a6xx_debugbus_blocks); i++) a6xx_get_debugbus_block(gpu, @@ -369,8 +369,6 @@ static void a6xx_get_debugbus_blocks(struct msm_gpu *gpu, &a6xx_debugbus_blocks[i], &a6xx_state->debugbus[i]); - a6xx_state->nr_debugbus = ARRAY_SIZE(a6xx_debugbus_blocks); - /* * GBIF has same debugbus as of other GPU blocks, fall back to * default path if GPU uses GBIF, also GBIF uses exactly same @@ -381,17 +379,19 @@ static void a6xx_get_debugbus_blocks(struct msm_gpu *gpu, &a6xx_gbif_debugbus_block, &a6xx_state->debugbus[i]); - a6xx_state->nr_debugbus += 1; + i++; } if (adreno_is_a650_family(to_adreno_gpu(gpu))) { - for (i = 0; i < ARRAY_SIZE(a650_debugbus_blocks); i++) + for (j = 0; j < ARRAY_SIZE(a650_debugbus_blocks); i++, j++) a6xx_get_debugbus_block(gpu, a6xx_state, - &a650_debugbus_blocks[i], + &a650_debugbus_blocks[j], &a6xx_state->debugbus[i]); } + + a6xx_state->nr_debugbus = i; } } From 47cbfe2608314b833ad61a65827d8fb363bc2d2d Mon Sep 17 00:00:00 2001 From: Yasuaki Torimaru Date: Wed, 25 Mar 2026 20:46:34 +0900 Subject: [PATCH 556/712] drm/msm/gem: fix error handling in msm_ioctl_gem_info_get_metadata() msm_ioctl_gem_info_get_metadata() always returns 0 regardless of errors. When copy_to_user() fails or the user buffer is too small, the error code stored in ret is ignored because the function unconditionally returns 0. This causes userspace to believe the ioctl succeeded when it did not. Additionally, kmemdup() can return NULL on allocation failure, but the return value is not checked. This leads to a NULL pointer dereference in the subsequent copy_to_user() call. Add the missing NULL check for kmemdup() and return ret instead of 0. Note that the SET counterpart (msm_ioctl_gem_info_set_metadata) correctly returns ret. Fixes: 9902cb999e4e ("drm/msm/gem: Add metadata") Cc: stable@vger.kernel.org Signed-off-by: Yasuaki Torimaru Patchwork: https://patchwork.freedesktop.org/patch/714478/ Message-ID: <20260325114635.383241-1-yasuakitorimaru@gmail.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_drv.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index ed2a61c66ac9..c7f6d07a5043 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -536,6 +536,11 @@ static int msm_ioctl_gem_info_get_metadata(struct drm_gem_object *obj, len = msm_obj->metadata_size; buf = kmemdup(msm_obj->metadata, len, GFP_KERNEL); + if (!buf) { + msm_gem_unlock(obj); + return -ENOMEM; + } + msm_gem_unlock(obj); if (*metadata_size < len) { @@ -548,7 +553,7 @@ static int msm_ioctl_gem_info_get_metadata(struct drm_gem_object *obj, kfree(buf); - return 0; + return ret; } static int msm_ioctl_gem_info(struct drm_device *dev, void *data, From dc78b35d5ec09d1b0b8a937e6e640d2c5a030915 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:50 +0530 Subject: [PATCH 557/712] drm/msm/a6xx: Use barriers while updating HFI Q headers To avoid harmful compiler optimizations and IO reordering in the HW, use barriers and READ/WRITE_ONCE helpers as necessary while accessing the HFI queue index variables. Fixes: 4b565ca5a2cb ("drm/msm: Add A6XX device support") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714653/ Message-ID: <20260327-a8xx-gpu-batch2-v2-1-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c index 53cfdf4e6c34..4f5dbf46132b 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c @@ -34,7 +34,7 @@ static int a6xx_hfi_queue_read(struct a6xx_gmu *gmu, struct a6xx_hfi_queue_header *header = queue->header; u32 i, hdr, index = header->read_index; - if (header->read_index == header->write_index) { + if (header->read_index == READ_ONCE(header->write_index)) { header->rx_request = 1; return 0; } @@ -62,7 +62,10 @@ static int a6xx_hfi_queue_read(struct a6xx_gmu *gmu, if (!gmu->legacy) index = ALIGN(index, 4) % header->size; - header->read_index = index; + /* Ensure all memory operations are complete before updating the read index */ + dma_mb(); + + WRITE_ONCE(header->read_index, index); return HFI_HEADER_SIZE(hdr); } @@ -74,7 +77,7 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu *gmu, spin_lock(&queue->lock); - space = CIRC_SPACE(header->write_index, header->read_index, + space = CIRC_SPACE(header->write_index, READ_ONCE(header->read_index), header->size); if (space < dwords) { header->dropped++; @@ -95,7 +98,10 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu *gmu, queue->data[index] = 0xfafafafa; } - header->write_index = index; + /* Ensure all memory operations are complete before updating the write index */ + dma_mb(); + + WRITE_ONCE(header->write_index, index); spin_unlock(&queue->lock); gmu_write(gmu, REG_A6XX_GMU_HOST2GMU_INTR_SET, 0x01); From cfc8b48649e159ff394fb4b7b08e5006c5c1c234 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:51 +0530 Subject: [PATCH 558/712] drm/msm/a8xx: Fix the ticks used in submit traces GMU_ALWAYS_ON_COUNTER_* registers got moved in A8x, but currently, A6x register offsets are used in the submit traces instead of A8x offsets. To fix this, refactor a bit and use adreno_gpu->funcs->get_timestamp() everywhere. While we are at it, update a8xx_gmu_get_timestamp() to use the GMU AO counter. Fixes: 288a93200892 ("drm/msm/adreno: Introduce A8x GPU Support") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714655/ Message-ID: <20260327-a8xx-gpu-batch2-v2-2-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a4xx_gpu.c | 6 ++--- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 6 ++--- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 23 ++++++------------- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 2 +- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 20 +++++++--------- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++--- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 2 +- .../gpu/drm/msm/registers/adreno/a6xx_gmu.xml | 6 +++-- 8 files changed, 27 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index db06c06067ae..0ed8bf2b5dd5 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -604,11 +604,9 @@ static int a4xx_pm_suspend(struct msm_gpu *gpu) { return 0; } -static int a4xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value) +static u64 a4xx_get_timestamp(struct msm_gpu *gpu) { - *value = gpu_read64(gpu, REG_A4XX_RBBM_PERFCTR_CP_0_LO); - - return 0; + return gpu_read64(gpu, REG_A4XX_RBBM_PERFCTR_CP_0_LO); } static u64 a4xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 56eaff2ee4e4..79a441e91fa1 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1435,11 +1435,9 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu) return 0; } -static int a5xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value) +static u64 a5xx_get_timestamp(struct msm_gpu *gpu) { - *value = gpu_read64(gpu, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO); - - return 0; + return gpu_read64(gpu, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO); } struct a5xx_crashdumper { diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index a00215b7bd1e..8013d6700c88 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -16,8 +16,10 @@ #define GPU_PAS_ID 13 -static u64 read_gmu_ao_counter(struct a6xx_gpu *a6xx_gpu) +static u64 a6xx_gmu_get_timestamp(struct msm_gpu *gpu) { + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); u64 count_hi, count_lo, temp; do { @@ -404,7 +406,7 @@ static void a6xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_RING(ring, upper_32_bits(rbmemptr(ring, fence))); OUT_RING(ring, submit->seqno); - trace_msm_gpu_submit_flush(submit, read_gmu_ao_counter(a6xx_gpu)); + trace_msm_gpu_submit_flush(submit, adreno_gpu->funcs->get_timestamp(gpu)); a6xx_flush(gpu, ring); } @@ -614,7 +616,7 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) } - trace_msm_gpu_submit_flush(submit, read_gmu_ao_counter(a6xx_gpu)); + trace_msm_gpu_submit_flush(submit, adreno_gpu->funcs->get_timestamp(gpu)); a6xx_flush(gpu, ring); @@ -2414,20 +2416,9 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu) return 0; } -static int a6xx_gmu_get_timestamp(struct msm_gpu *gpu, uint64_t *value) +static u64 a6xx_get_timestamp(struct msm_gpu *gpu) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); - - *value = read_gmu_ao_counter(a6xx_gpu); - - return 0; -} - -static int a6xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value) -{ - *value = gpu_read64(gpu, REG_A6XX_CP_ALWAYS_ON_COUNTER); - return 0; + return gpu_read64(gpu, REG_A6XX_CP_ALWAYS_ON_COUNTER); } static struct msm_ringbuffer *a6xx_active_ring(struct msm_gpu *gpu) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index 4eaa04711246..a4434a6a56dd 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -320,7 +320,7 @@ int a6xx_zap_shader_init(struct msm_gpu *gpu); void a8xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_off); int a8xx_fault_handler(void *arg, unsigned long iova, int flags, void *data); void a8xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring); -int a8xx_gmu_get_timestamp(struct msm_gpu *gpu, uint64_t *value); +u64 a8xx_gmu_get_timestamp(struct msm_gpu *gpu); u64 a8xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate); int a8xx_gpu_feature_probe(struct msm_gpu *gpu); void a8xx_gpu_get_slice_info(struct msm_gpu *gpu); diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index dd5e06868706..07bc4747caad 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -1184,23 +1184,19 @@ void a8xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_ gpu_write(gpu, REG_A6XX_GBIF_HALT, 0x0); } -int a8xx_gmu_get_timestamp(struct msm_gpu *gpu, uint64_t *value) +u64 a8xx_gmu_get_timestamp(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + u64 count_hi, count_lo, temp; - mutex_lock(&a6xx_gpu->gmu.lock); + do { + count_hi = gmu_read(&a6xx_gpu->gmu, REG_A8XX_GMU_ALWAYS_ON_COUNTER_H); + count_lo = gmu_read(&a6xx_gpu->gmu, REG_A8XX_GMU_ALWAYS_ON_COUNTER_L); + temp = gmu_read(&a6xx_gpu->gmu, REG_A8XX_GMU_ALWAYS_ON_COUNTER_H); + } while (unlikely(count_hi != temp)); - /* Force the GPU power on so we can read this register */ - a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET); - - *value = gpu_read64(gpu, REG_A8XX_CP_ALWAYS_ON_COUNTER); - - a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET); - - mutex_unlock(&a6xx_gpu->gmu.lock); - - return 0; + return (count_hi << 32) | count_lo; } u64 a8xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index d5fe6f6f0dec..785e99fb5bd5 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -391,13 +391,11 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, return 0; case MSM_PARAM_TIMESTAMP: if (adreno_gpu->funcs->get_timestamp) { - int ret; - pm_runtime_get_sync(&gpu->pdev->dev); - ret = adreno_gpu->funcs->get_timestamp(gpu, value); + *value = adreno_gpu->funcs->get_timestamp(gpu); pm_runtime_put_autosuspend(&gpu->pdev->dev); - return ret; + return 0; } return -EINVAL; case MSM_PARAM_PRIORITIES: diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 6cdfafcb0c57..0761cb527013 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -75,7 +75,7 @@ struct adreno_gpu; struct adreno_gpu_funcs { struct msm_gpu_funcs base; struct msm_gpu *(*init)(struct drm_device *dev); - int (*get_timestamp)(struct msm_gpu *gpu, uint64_t *value); + u64 (*get_timestamp)(struct msm_gpu *gpu); void (*bus_halt)(struct adreno_gpu *adreno_gpu, bool gx_off); int (*mmu_fault_handler)(void *arg, unsigned long iova, int flags, void *data); }; diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml index c4e00b1263cd..33404eb18fd0 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml @@ -141,8 +141,10 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - - + + + + From 0c59f258ffd4c9c2a6bd37d71a0ade1db8bc03b7 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:52 +0530 Subject: [PATCH 559/712] drm/msm/a6xx: Switch to preemption safe AO counter CP_ALWAYS_ON_COUNTER is not save-restored during preemption, so it won't provide accurate data about the 'submit' when preemption is enabled. Switch to CP_ALWAYS_ON_CONTEXT which is preemption safe. Fixes: e7ae83da4a28 ("drm/msm/a6xx: Implement preemption for a7xx targets") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714657/ Message-ID: <20260327-a8xx-gpu-batch2-v2-3-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 8013d6700c88..29cbebbb46cb 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -347,7 +347,7 @@ static void a6xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) * GPU registers so we need to add 0x1a800 to the register value on A630 * to get the right value from PM4. */ - get_stats_counter(ring, REG_A6XX_CP_ALWAYS_ON_COUNTER, + get_stats_counter(ring, REG_A6XX_CP_ALWAYS_ON_CONTEXT, rbmemptr_stats(ring, index, alwayson_start)); /* Invalidate CCU depth and color */ @@ -388,7 +388,7 @@ static void a6xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) get_stats_counter(ring, REG_A6XX_RBBM_PERFCTR_CP(0), rbmemptr_stats(ring, index, cpcycles_end)); - get_stats_counter(ring, REG_A6XX_CP_ALWAYS_ON_COUNTER, + get_stats_counter(ring, REG_A6XX_CP_ALWAYS_ON_CONTEXT, rbmemptr_stats(ring, index, alwayson_end)); /* Write the fence to the scratch register */ @@ -457,7 +457,7 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); struct msm_ringbuffer *ring = submit->ring; - u32 rbbm_perfctr_cp0, cp_always_on_counter; + u32 rbbm_perfctr_cp0, cp_always_on_context; unsigned int i, ibs = 0; adreno_check_and_reenable_stall(adreno_gpu); @@ -480,14 +480,14 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) if (adreno_is_a8xx(adreno_gpu)) { rbbm_perfctr_cp0 = REG_A8XX_RBBM_PERFCTR_CP(0); - cp_always_on_counter = REG_A8XX_CP_ALWAYS_ON_COUNTER; + cp_always_on_context = REG_A8XX_CP_ALWAYS_ON_CONTEXT; } else { rbbm_perfctr_cp0 = REG_A7XX_RBBM_PERFCTR_CP(0); - cp_always_on_counter = REG_A6XX_CP_ALWAYS_ON_COUNTER; + cp_always_on_context = REG_A6XX_CP_ALWAYS_ON_CONTEXT; } get_stats_counter(ring, rbbm_perfctr_cp0, rbmemptr_stats(ring, index, cpcycles_start)); - get_stats_counter(ring, cp_always_on_counter, rbmemptr_stats(ring, index, alwayson_start)); + get_stats_counter(ring, cp_always_on_context, rbmemptr_stats(ring, index, alwayson_start)); OUT_PKT7(ring, CP_THREAD_CONTROL, 1); OUT_RING(ring, CP_SET_THREAD_BOTH); @@ -535,7 +535,7 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) } get_stats_counter(ring, rbbm_perfctr_cp0, rbmemptr_stats(ring, index, cpcycles_end)); - get_stats_counter(ring, cp_always_on_counter, rbmemptr_stats(ring, index, alwayson_end)); + get_stats_counter(ring, cp_always_on_context, rbmemptr_stats(ring, index, alwayson_end)); /* Write the fence to the scratch register */ if (adreno_is_a8xx(adreno_gpu)) { From d34b6919798c1a8c93e1d7cca297d0e068146bd5 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:53 +0530 Subject: [PATCH 560/712] drm/msm/a6xx: Correct OOB usage During the GMU resume sequence, using another OOB other than OOB_GPU may confuse the internal state of GMU firmware. To align more strictly with the downstream sequence, move the sysprof related OOB setup after the OOB_GPU is cleared. Fixes: 62cd0fa6990b ("drm/msm/adreno: Disable IFPC when sysprof is active") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714659/ Message-ID: <20260327-a8xx-gpu-batch2-v2-4-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 5 ----- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 6 ++++++ drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 6 ++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 9662201cd2e9..690d3e53e273 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -1236,11 +1236,6 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) /* Set the GPU to the current freq */ a6xx_gmu_set_initial_freq(gpu, gmu); - if (refcount_read(&gpu->sysprof_active) > 1) { - ret = a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET); - if (!ret) - set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status); - } out: /* On failure, shut down the GMU to leave it in a good state */ if (ret) { diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 29cbebbb46cb..ae1d418ed0ab 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -1605,6 +1605,12 @@ static int hw_init(struct msm_gpu *gpu) a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_BOOT_SLUMBER); } + if (!ret && (refcount_read(&gpu->sysprof_active) > 1)) { + ret = a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET); + if (!ret) + set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status); + } + return ret; } diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 07bc4747caad..d5c547d347e2 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -721,6 +721,12 @@ static int hw_init(struct msm_gpu *gpu) */ a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET); + if (!ret && (refcount_read(&gpu->sysprof_active) > 1)) { + ret = a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET); + if (!ret) + set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status); + } + return ret; } From ae25e6e9cdcac4cfef102b9d6de8bff13ca4d13b Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:54 +0530 Subject: [PATCH 561/712] drm/msm/adreno: Implement gx_is_on() for A8x A8x has a diverged enough for a separate implementation of gx_is_on() check. Add that and move them to the adreno func table. Fixes: 288a93200892 ("drm/msm/adreno: Introduce A8x GPU Support") Reviewed-by: Konrad Dybcio Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714661/ Message-ID: <20260327-a8xx-gpu-batch2-v2-5-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 42 +++++++++++++++++++-- drivers/gpu/drm/msm/adreno/a6xx_gmu.h | 5 ++- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 6 ++- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 4 +- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 690d3e53e273..b41dbca1ebc6 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -91,10 +91,10 @@ bool a6xx_gmu_sptprac_is_on(struct a6xx_gmu *gmu) } /* Check to see if the GX rail is still powered */ -bool a6xx_gmu_gx_is_on(struct a6xx_gmu *gmu) +bool a6xx_gmu_gx_is_on(struct adreno_gpu *adreno_gpu) { - struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); - struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + struct a6xx_gmu *gmu = &a6xx_gpu->gmu; u32 val; /* This can be called from gpu state code so make sure GMU is valid */ @@ -117,6 +117,40 @@ bool a6xx_gmu_gx_is_on(struct a6xx_gmu *gmu) A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_CLK_OFF)); } +bool a7xx_gmu_gx_is_on(struct adreno_gpu *adreno_gpu) +{ + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + struct a6xx_gmu *gmu = &a6xx_gpu->gmu; + u32 val; + + /* This can be called from gpu state code so make sure GMU is valid */ + if (!gmu->initialized) + return false; + + val = gmu_read(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS); + + return !(val & + (A7XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_GDSC_POWER_OFF | + A7XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_CLK_OFF)); +} + +bool a8xx_gmu_gx_is_on(struct adreno_gpu *adreno_gpu) +{ + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + struct a6xx_gmu *gmu = &a6xx_gpu->gmu; + u32 val; + + /* This can be called from gpu state code so make sure GMU is valid */ + if (!gmu->initialized) + return false; + + val = gmu_read(gmu, REG_A8XX_GMU_PWR_CLK_STATUS); + + return !(val & + (A8XX_GMU_PWR_CLK_STATUS_GX_HM_GDSC_POWER_OFF | + A8XX_GMU_PWR_CLK_STATUS_GX_HM_CLK_OFF)); +} + void a6xx_gmu_set_freq(struct msm_gpu *gpu, struct dev_pm_opp *opp, bool suspended) { @@ -240,7 +274,7 @@ static bool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu) if (val == local) { if (gmu->idle_level != GMU_IDLE_STATE_IFPC || - !a6xx_gmu_gx_is_on(gmu)) + !adreno_gpu->funcs->gx_is_on(adreno_gpu)) return true; } diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h index 2af074c8e8cf..9f09daf45ab2 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h @@ -10,6 +10,7 @@ #include #include #include "msm_drv.h" +#include "adreno_gpu.h" #include "a6xx_hfi.h" struct a6xx_gmu_bo { @@ -231,7 +232,9 @@ void a6xx_hfi_stop(struct a6xx_gmu *gmu); int a6xx_hfi_send_prep_slumber(struct a6xx_gmu *gmu); int a6xx_hfi_set_freq(struct a6xx_gmu *gmu, u32 perf_index, u32 bw_index); -bool a6xx_gmu_gx_is_on(struct a6xx_gmu *gmu); +bool a6xx_gmu_gx_is_on(struct adreno_gpu *adreno_gpu); +bool a7xx_gmu_gx_is_on(struct adreno_gpu *adreno_gpu); +bool a8xx_gmu_gx_is_on(struct adreno_gpu *adreno_gpu); bool a6xx_gmu_sptprac_is_on(struct a6xx_gmu *gmu); void a6xx_sptprac_disable(struct a6xx_gmu *gmu); int a6xx_sptprac_enable(struct a6xx_gmu *gmu); diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index ae1d418ed0ab..d480c621f911 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -1643,7 +1643,7 @@ static void a6xx_recover(struct msm_gpu *gpu) adreno_dump_info(gpu); - if (a6xx_gmu_gx_is_on(&a6xx_gpu->gmu)) { + if (adreno_gpu->funcs->gx_is_on(adreno_gpu)) { /* Sometimes crashstate capture is skipped, so SQE should be halted here again */ gpu_write(gpu, REG_A6XX_CP_SQE_CNTL, 3); @@ -2763,6 +2763,7 @@ const struct adreno_gpu_funcs a6xx_gpu_funcs = { .get_timestamp = a6xx_gmu_get_timestamp, .bus_halt = a6xx_bus_clear_pending_transactions, .mmu_fault_handler = a6xx_fault_handler, + .gx_is_on = a6xx_gmu_gx_is_on, }; const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs = { @@ -2795,6 +2796,7 @@ const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs = { .get_timestamp = a6xx_get_timestamp, .bus_halt = a6xx_bus_clear_pending_transactions, .mmu_fault_handler = a6xx_fault_handler, + .gx_is_on = a6xx_gmu_gx_is_on, }; const struct adreno_gpu_funcs a7xx_gpu_funcs = { @@ -2829,6 +2831,7 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = { .get_timestamp = a6xx_gmu_get_timestamp, .bus_halt = a6xx_bus_clear_pending_transactions, .mmu_fault_handler = a6xx_fault_handler, + .gx_is_on = a7xx_gmu_gx_is_on, }; const struct adreno_gpu_funcs a8xx_gpu_funcs = { @@ -2856,4 +2859,5 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = { .get_timestamp = a8xx_gmu_get_timestamp, .bus_halt = a8xx_bus_clear_pending_transactions, .mmu_fault_handler = a8xx_fault_handler, + .gx_is_on = a8xx_gmu_gx_is_on, }; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 6e4950d513b3..621f0b71ed11 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1251,7 +1251,7 @@ static void a6xx_get_gmu_registers(struct msm_gpu *gpu, _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gpucc_reg, &a6xx_state->gmu_registers[2], false); - if (!a6xx_gmu_gx_is_on(&a6xx_gpu->gmu)) + if (!adreno_gpu->funcs->gx_is_on(adreno_gpu)) return; /* Set the fence to ALLOW mode so we can access the registers */ @@ -1608,7 +1608,7 @@ struct msm_gpu_state *a6xx_gpu_state_get(struct msm_gpu *gpu) } /* If GX isn't on the rest of the data isn't going to be accessible */ - if (!a6xx_gmu_gx_is_on(&a6xx_gpu->gmu)) + if (!adreno_gpu->funcs->gx_is_on(adreno_gpu)) return &a6xx_state->base; /* Halt SQE first */ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 0761cb527013..41b0e376ee31 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -78,6 +78,7 @@ struct adreno_gpu_funcs { u64 (*get_timestamp)(struct msm_gpu *gpu); void (*bus_halt)(struct adreno_gpu *adreno_gpu, bool gx_off); int (*mmu_fault_handler)(void *arg, unsigned long iova, int flags, void *data); + bool (*gx_is_on)(struct adreno_gpu *adreno_gpu); }; struct adreno_reglist { From bb9b1d6e945ea90459bda1aac7e2aa7179119887 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:55 +0530 Subject: [PATCH 562/712] drm/msm/a6xx: Fix gpu init from secure world A7XX_GEN2 and newer GPUs requires initialization of few configurations related to features/power from secure world. The SCM call to do this should be triggered after GDSC and clocks are enabled. So, keep this sequence to a6xx_gmu_resume instead of the probe. Also, simplify the error handling in a6xx_gmu_resume() using 'goto' labels. Fixes: 14b27d5df3ea ("drm/msm/a7xx: Initialize a750 "software fuse"") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714664/ Message-ID: <20260327-a8xx-gpu-batch2-v2-6-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 93 ++++++++++++++++++++++----- drivers/gpu/drm/msm/adreno/a6xx_gmu.h | 2 + drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 59 ----------------- 3 files changed, 80 insertions(+), 74 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index b41dbca1ebc6..1b44b9e21ad8 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -1191,6 +1192,65 @@ static void a6xx_gmu_set_initial_bw(struct msm_gpu *gpu, struct a6xx_gmu *gmu) dev_pm_opp_put(gpu_opp); } +static int a6xx_gmu_secure_init(struct a6xx_gpu *a6xx_gpu) +{ + struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; + struct msm_gpu *gpu = &adreno_gpu->base; + struct a6xx_gmu *gmu = &a6xx_gpu->gmu; + u32 fuse_val; + int ret; + + if (test_bit(GMU_STATUS_SECURE_INIT, &gmu->status)) + return 0; + + if (adreno_is_a750(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) { + /* + * Assume that if qcom scm isn't available, that whatever + * replacement allows writing the fuse register ourselves. + * Users of alternative firmware need to make sure this + * register is writeable or indicate that it's not somehow. + * Print a warning because if you mess this up you're about to + * crash horribly. + */ + if (!qcom_scm_is_available()) { + dev_warn_once(gpu->dev->dev, + "SCM is not available, poking fuse register\n"); + a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE, + A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING | + A7XX_CX_MISC_SW_FUSE_VALUE_FASTBLEND | + A7XX_CX_MISC_SW_FUSE_VALUE_LPAC); + adreno_gpu->has_ray_tracing = true; + goto done; + } + + ret = qcom_scm_gpu_init_regs(QCOM_SCM_GPU_ALWAYS_EN_REQ | + QCOM_SCM_GPU_TSENSE_EN_REQ); + if (ret) { + dev_warn_once(gpu->dev->dev, + "SCM call failed\n"); + return ret; + } + + /* + * On A7XX_GEN3 and newer, raytracing may be disabled by the + * firmware, find out whether that's the case. The scm call + * above sets the fuse register. + */ + fuse_val = a6xx_llc_read(a6xx_gpu, + REG_A7XX_CX_MISC_SW_FUSE_VALUE); + adreno_gpu->has_ray_tracing = + !!(fuse_val & A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING); + } else if (adreno_is_a740(adreno_gpu)) { + /* Raytracing is always enabled on a740 */ + adreno_gpu->has_ray_tracing = true; + } + +done: + set_bit(GMU_STATUS_SECURE_INIT, &gmu->status); + return 0; +} + + int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) { struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; @@ -1219,11 +1279,12 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) clk_set_rate(gmu->hub_clk, adreno_is_a740_family(adreno_gpu) ? 200000000 : 150000000); ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks); - if (ret) { - pm_runtime_put(gmu->gxpd); - pm_runtime_put(gmu->dev); - return ret; - } + if (ret) + goto rpm_put; + + ret = a6xx_gmu_secure_init(a6xx_gpu); + if (ret) + goto disable_clk; /* Read the slice info on A8x GPUs */ a8xx_gpu_get_slice_info(gpu); @@ -1253,11 +1314,11 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) ret = a6xx_gmu_fw_start(gmu, status); if (ret) - goto out; + goto disable_irq; ret = a6xx_hfi_start(gmu, status); if (ret) - goto out; + goto disable_irq; /* * Turn on the GMU firmware fault interrupt after we know the boot @@ -1270,14 +1331,16 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) /* Set the GPU to the current freq */ a6xx_gmu_set_initial_freq(gpu, gmu); -out: - /* On failure, shut down the GMU to leave it in a good state */ - if (ret) { - disable_irq(gmu->gmu_irq); - a6xx_rpmh_stop(gmu); - pm_runtime_put(gmu->gxpd); - pm_runtime_put(gmu->dev); - } + return 0; + +disable_irq: + disable_irq(gmu->gmu_irq); + a6xx_rpmh_stop(gmu); +disable_clk: + clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks); +rpm_put: + pm_runtime_put(gmu->gxpd); + pm_runtime_put(gmu->dev); return ret; } diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h index 9f09daf45ab2..0cd8ae1b4f5c 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h @@ -130,6 +130,8 @@ struct a6xx_gmu { #define GMU_STATUS_PDC_SLEEP 1 /* To track Perfcounter OOB set status */ #define GMU_STATUS_OOB_PERF_SET 2 +/* To track whether secure world init was done */ +#define GMU_STATUS_SECURE_INIT 3 unsigned long status; }; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index d480c621f911..252a040e02b0 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -2160,56 +2159,6 @@ static void a6xx_llc_slices_init(struct platform_device *pdev, a6xx_gpu->llc_mmio = ERR_PTR(-EINVAL); } -static int a7xx_cx_mem_init(struct a6xx_gpu *a6xx_gpu) -{ - struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; - struct msm_gpu *gpu = &adreno_gpu->base; - u32 fuse_val; - int ret; - - if (adreno_is_a750(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) { - /* - * Assume that if qcom scm isn't available, that whatever - * replacement allows writing the fuse register ourselves. - * Users of alternative firmware need to make sure this - * register is writeable or indicate that it's not somehow. - * Print a warning because if you mess this up you're about to - * crash horribly. - */ - if (!qcom_scm_is_available()) { - dev_warn_once(gpu->dev->dev, - "SCM is not available, poking fuse register\n"); - a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE, - A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING | - A7XX_CX_MISC_SW_FUSE_VALUE_FASTBLEND | - A7XX_CX_MISC_SW_FUSE_VALUE_LPAC); - adreno_gpu->has_ray_tracing = true; - return 0; - } - - ret = qcom_scm_gpu_init_regs(QCOM_SCM_GPU_ALWAYS_EN_REQ | - QCOM_SCM_GPU_TSENSE_EN_REQ); - if (ret) - return ret; - - /* - * On A7XX_GEN3 and newer, raytracing may be disabled by the - * firmware, find out whether that's the case. The scm call - * above sets the fuse register. - */ - fuse_val = a6xx_llc_read(a6xx_gpu, - REG_A7XX_CX_MISC_SW_FUSE_VALUE); - adreno_gpu->has_ray_tracing = - !!(fuse_val & A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING); - } else if (adreno_is_a740(adreno_gpu)) { - /* Raytracing is always enabled on a740 */ - adreno_gpu->has_ray_tracing = true; - } - - return 0; -} - - #define GBIF_CLIENT_HALT_MASK BIT(0) #define GBIF_ARB_HALT_MASK BIT(1) #define VBIF_XIN_HALT_CTRL0_MASK GENMASK(3, 0) @@ -2706,14 +2655,6 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) return ERR_PTR(ret); } - if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) { - ret = a7xx_cx_mem_init(a6xx_gpu); - if (ret) { - a6xx_destroy(&(a6xx_gpu->base.base)); - return ERR_PTR(ret); - } - } - adreno_gpu->uche_trap_base = 0x1fffffffff000ull; msm_mmu_set_fault_handler(to_msm_vm(gpu->vm)->mmu, gpu, From 61957ab99d8c47a74a44fa85740ec0d922359554 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:56 +0530 Subject: [PATCH 563/712] drm/msm/a6xx: Add support for Debug HFI Q Add the Debug HFI Queue which contains the F2H messages posted from the GMU firmware. Having this data in coredump is useful to debug firmware issues. Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/714666/ Message-ID: <20260327-a8xx-gpu-batch2-v2-7-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.h | 2 +- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 2 +- drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 16 +++++++++++++--- drivers/gpu/drm/msm/adreno/a6xx_hfi.h | 2 ++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h index 0cd8ae1b4f5c..3f96b10b5f61 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h @@ -111,7 +111,7 @@ struct a6xx_gmu { unsigned long freq; - struct a6xx_hfi_queue queues[2]; + struct a6xx_hfi_queue queues[HFI_MAX_QUEUES]; bool initialized; bool hung; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 621f0b71ed11..4b51d6701aa2 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -57,7 +57,7 @@ struct a6xx_gpu_state { struct msm_gpu_state_bo *gmu_hfi; struct msm_gpu_state_bo *gmu_debug; - s32 hfi_queue_history[2][HFI_HISTORY_SZ]; + s32 hfi_queue_history[HFI_MAX_QUEUES][HFI_HISTORY_SZ]; struct list_head objs; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c index 4f5dbf46132b..09b6bc464b47 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c @@ -1062,8 +1062,8 @@ void a6xx_hfi_init(struct a6xx_gmu *gmu) struct a6xx_gmu_bo *hfi = &gmu->hfi; struct a6xx_hfi_queue_table_header *table = hfi->virt; struct a6xx_hfi_queue_header *headers = hfi->virt + sizeof(*table); + int table_size, idx; u64 offset; - int table_size; /* * The table size is the size of the table header plus all of the queue @@ -1082,12 +1082,22 @@ void a6xx_hfi_init(struct a6xx_gmu *gmu) table->active_queues = ARRAY_SIZE(gmu->queues); /* Command queue */ + idx = 0; offset = SZ_4K; - a6xx_hfi_queue_init(&gmu->queues[0], &headers[0], hfi->virt + offset, + a6xx_hfi_queue_init(&gmu->queues[idx], &headers[idx], hfi->virt + offset, hfi->iova + offset, 0); /* GMU response queue */ + idx++; offset += SZ_4K; - a6xx_hfi_queue_init(&gmu->queues[1], &headers[1], hfi->virt + offset, + a6xx_hfi_queue_init(&gmu->queues[idx], &headers[idx], hfi->virt + offset, hfi->iova + offset, gmu->legacy ? 4 : 1); + + /* GMU Debug queue */ + idx++; + offset += SZ_4K; + a6xx_hfi_queue_init(&gmu->queues[idx], &headers[idx], hfi->virt + offset, + hfi->iova + offset, gmu->legacy ? 5 : 2); + + WARN_ON(idx >= HFI_MAX_QUEUES); } diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h index 6f9f74a0bc85..19f6eca2c8c9 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h @@ -4,6 +4,8 @@ #ifndef _A6XX_HFI_H_ #define _A6XX_HFI_H_ +#define HFI_MAX_QUEUES 3 + struct a6xx_hfi_queue_table_header { u32 version; u32 size; /* Size of the queue table in dwords */ From 29c1d7e5db01e870b11fb40126d5044f9da9e92b Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:58 +0530 Subject: [PATCH 564/712] drm/msm/a6xx: Use packed structs for HFI HFI related structs define the ABI between the KMD and the GMU firmware. So, use packed structures to avoid unintended compiler inserted padding. Reviewed-by: Konrad Dybcio Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714669/ Message-ID: <20260327-a8xx-gpu-batch2-v2-9-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_hfi.h | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h index 19f6eca2c8c9..217708b03f6f 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h @@ -13,7 +13,7 @@ struct a6xx_hfi_queue_table_header { u32 qhdr_size; /* Size of the queue headers */ u32 num_queues; /* Number of total queues */ u32 active_queues; /* Number of active queues */ -}; +} __packed; struct a6xx_hfi_queue_header { u32 status; @@ -28,7 +28,7 @@ struct a6xx_hfi_queue_header { u32 tx_request; u32 read_index; u32 write_index; -}; +} __packed; struct a6xx_hfi_queue { struct a6xx_hfi_queue_header *header; @@ -74,7 +74,7 @@ struct a6xx_hfi_msg_response { u32 ret_header; u32 error; u32 payload[HFI_RESPONSE_PAYLOAD_SIZE]; -}; +} __packed; #define HFI_F2H_MSG_ERROR 100 @@ -82,7 +82,7 @@ struct a6xx_hfi_msg_error { u32 header; u32 code; u32 payload[2]; -}; +} __packed; #define HFI_H2F_MSG_INIT 0 @@ -92,27 +92,27 @@ struct a6xx_hfi_msg_gmu_init_cmd { u32 dbg_buffer_addr; u32 dbg_buffer_size; u32 boot_state; -}; +} __packed; #define HFI_H2F_MSG_FW_VERSION 1 struct a6xx_hfi_msg_fw_version { u32 header; u32 supported_version; -}; +} __packed; #define HFI_H2F_MSG_PERF_TABLE 4 struct perf_level { u32 vote; u32 freq; -}; +} __packed; struct perf_gx_level { u32 vote; u32 acd; u32 freq; -}; +} __packed; struct a6xx_hfi_msg_perf_table_v1 { u32 header; @@ -121,7 +121,7 @@ struct a6xx_hfi_msg_perf_table_v1 { struct perf_level gx_votes[16]; struct perf_level cx_votes[4]; -}; +} __packed; struct a6xx_hfi_msg_perf_table { u32 header; @@ -130,7 +130,7 @@ struct a6xx_hfi_msg_perf_table { struct perf_gx_level gx_votes[16]; struct perf_level cx_votes[4]; -}; +} __packed; #define HFI_H2F_MSG_BW_TABLE 3 @@ -145,13 +145,13 @@ struct a6xx_hfi_msg_bw_table { u32 cnoc_cmds_data[2][6]; u32 ddr_cmds_addrs[8]; u32 ddr_cmds_data[16][8]; -}; +} __packed; #define HFI_H2F_MSG_TEST 5 struct a6xx_hfi_msg_test { u32 header; -}; +} __packed; #define HFI_H2F_MSG_ACD 7 #define MAX_ACD_STRIDE 2 @@ -163,13 +163,13 @@ struct a6xx_hfi_acd_table { u32 stride; u32 num_levels; u32 data[16 * MAX_ACD_STRIDE]; -}; +} __packed; #define HFI_H2F_MSG_START 10 struct a6xx_hfi_msg_start { u32 header; -}; +} __packed; #define HFI_H2F_FEATURE_CTRL 11 @@ -178,14 +178,14 @@ struct a6xx_hfi_msg_feature_ctrl { u32 feature; u32 enable; u32 data; -}; +} __packed; #define HFI_H2F_MSG_CORE_FW_START 14 struct a6xx_hfi_msg_core_fw_start { u32 header; u32 handle; -}; +} __packed; #define HFI_H2F_MSG_TABLE 15 @@ -193,7 +193,7 @@ struct a6xx_hfi_table_entry { u32 count; u32 stride; u32 data[]; -}; +} __packed; struct a6xx_hfi_table { u32 header; @@ -202,7 +202,7 @@ struct a6xx_hfi_table { #define HFI_TABLE_BW_VOTE 0 #define HFI_TABLE_GPU_PERF 1 struct a6xx_hfi_table_entry entry[]; -}; +} __packed; #define HFI_H2F_MSG_GX_BW_PERF_VOTE 30 @@ -211,7 +211,7 @@ struct a6xx_hfi_gx_bw_perf_vote_cmd { u32 ack_type; u32 freq; u32 bw; -}; +} __packed; #define AB_VOTE_MASK GENMASK(31, 16) #define MAX_AB_VOTE (FIELD_MAX(AB_VOTE_MASK) - 1) @@ -224,6 +224,6 @@ struct a6xx_hfi_prep_slumber_cmd { u32 header; u32 bw; u32 freq; -}; +} __packed; #endif From 742b4e88cddb9840234623db9f3dc06a4d7c9135 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:43:59 +0530 Subject: [PATCH 565/712] drm/msm/a6xx: Update HFI definitions Update the HFI definitions to support additional GMU based power features. Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/714671/ Message-ID: <20260327-a8xx-gpu-batch2-v2-10-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 3 - drivers/gpu/drm/msm/adreno/a6xx_hfi.h | 113 +++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c index 09b6bc464b47..487c2736f2b3 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c @@ -851,7 +851,6 @@ static int a6xx_hfi_feature_ctrl_msg(struct a6xx_gmu *gmu, u32 feature, u32 enab return a6xx_hfi_send_msg(gmu, HFI_H2F_FEATURE_CTRL, &msg, sizeof(msg), NULL, 0); } -#define HFI_FEATURE_IFPC 9 #define IFPC_LONG_HYST 0x1680 static int a6xx_hfi_enable_ifpc(struct a6xx_gmu *gmu) @@ -862,8 +861,6 @@ static int a6xx_hfi_enable_ifpc(struct a6xx_gmu *gmu) return a6xx_hfi_feature_ctrl_msg(gmu, HFI_FEATURE_IFPC, 1, IFPC_LONG_HYST); } -#define HFI_FEATURE_ACD 12 - static int a6xx_hfi_enable_acd(struct a6xx_gmu *gmu) { struct a6xx_hfi_acd_table *acd_table = &gmu->acd_table; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h index 217708b03f6f..e10d32ce93e0 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h @@ -165,6 +165,42 @@ struct a6xx_hfi_acd_table { u32 data[16 * MAX_ACD_STRIDE]; } __packed; +#define CLX_DATA(irated, num_phases, clx_path, extd_intf) \ + ((extd_intf << 29) | \ + (clx_path << 28) | \ + (num_phases << 22) | \ + (irated << 16)) + +struct a6xx_hfi_clx_domain_v2 { + /** + * @data: BITS[0:15] Migration time + * BITS[16:21] Current rating + * BITS[22:27] Phases for domain + * BITS[28:28] Path notification + * BITS[29:31] Extra features + */ + u32 data; + /** @clxt: CLX time in microseconds */ + u32 clxt; + /** @clxh: CLH time in microseconds */ + u32 clxh; + /** @urg_mode: Urgent HW throttle mode of operation */ + u32 urg_mode; + /** @lkg_en: Enable leakage current estimate */ + u32 lkg_en; + /** curr_budget: Current Budget */ + u32 curr_budget; +} __packed; + +#define HFI_H2F_MSG_CLX_TBL 8 + +#define MAX_CLX_DOMAINS 2 +struct a6xx_hfi_clx_table_v2_cmd { + u32 hdr; + u32 version; + struct a6xx_hfi_clx_domain_v2 domain[MAX_CLX_DOMAINS]; +} __packed; + #define HFI_H2F_MSG_START 10 struct a6xx_hfi_msg_start { @@ -176,6 +212,41 @@ struct a6xx_hfi_msg_start { struct a6xx_hfi_msg_feature_ctrl { u32 header; u32 feature; +#define HFI_FEATURE_DCVS 0 +#define HFI_FEATURE_HWSCHED 1 +#define HFI_FEATURE_PREEMPTION 2 +#define HFI_FEATURE_CLOCKS_ON 3 +#define HFI_FEATURE_BUS_ON 4 +#define HFI_FEATURE_RAIL_ON 5 +#define HFI_FEATURE_HWCG 6 +#define HFI_FEATURE_LM 7 +#define HFI_FEATURE_THROTTLE 8 +#define HFI_FEATURE_IFPC 9 +#define HFI_FEATURE_NAP 10 +#define HFI_FEATURE_BCL 11 +#define HFI_FEATURE_ACD 12 +#define HFI_FEATURE_DIDT 13 +#define HFI_FEATURE_DEPRECATED 14 +#define HFI_FEATURE_CB 15 +#define HFI_FEATURE_KPROF 16 +#define HFI_FEATURE_BAIL_OUT_TIMER 17 +#define HFI_FEATURE_GMU_STATS 18 +#define HFI_FEATURE_DBQ 19 +#define HFI_FEATURE_MINBW 20 +#define HFI_FEATURE_CLX 21 +#define HFI_FEATURE_LSR 23 +#define HFI_FEATURE_LPAC 24 +#define HFI_FEATURE_HW_FENCE 25 +#define HFI_FEATURE_PERF_NORETAIN 26 +#define HFI_FEATURE_DMS 27 +#define HFI_FEATURE_THERMAL 28 +#define HFI_FEATURE_AQE 29 +#define HFI_FEATURE_TDCVS 30 +#define HFI_FEATURE_DCE 31 +#define HFI_FEATURE_IFF_PCLX 32 +#define HFI_FEATURE_SOFT_RESET 0x10000001 +#define HFI_FEATURE_DCVS_PROFILE 0x10000002 +#define HFI_FEATURE_FAST_CTX_DESTROY 0x10000003 u32 enable; u32 data; } __packed; @@ -199,8 +270,17 @@ struct a6xx_hfi_table { u32 header; u32 version; u32 type; -#define HFI_TABLE_BW_VOTE 0 -#define HFI_TABLE_GPU_PERF 1 +#define HFI_TABLE_BW_VOTE 0 +#define HFI_TABLE_GPU_PERF 1 +#define HFI_TABLE_DIDT 2 +#define HFI_TABLE_ACD 3 +#define HFI_TABLE_CLX_V1 4 /* Unused */ +#define HFI_TABLE_CLX_V2 5 +#define HFI_TABLE_THERM 6 +#define HFI_TABLE_DCVS 7 +#define HFI_TABLE_SYS_TIME 8 +#define HFI_TABLE_GMU_DCVS 9 +#define HFI_TABLE_LIMITS_MIT 10 struct a6xx_hfi_table_entry entry[]; } __packed; @@ -226,4 +306,33 @@ struct a6xx_hfi_prep_slumber_cmd { u32 freq; } __packed; +struct a6xx_hfi_limits_cfg { + u32 enable; + u32 msg_path; + u32 lkg_en; + /* + * BIT[0]: 0 = (static) throttle to fixed sid level + * 1 = (dynamic) throttle to sid level calculated by HW + * BIT[1]: 0 = Mx + * 1 = Bx + */ + u32 mode; + u32 sid; + /* Mitigation time in microseconds */ + u32 mit_time; + /* Max current in mA during mitigation */ + u32 curr_limit; +} __packed; + +struct a6xx_hfi_limits_tbl { + u8 feature_id; +#define GMU_MIT_IFF 0 +#define GMU_MIT_PCLX 1 + u8 domain; +#define GMU_GX_DOMAIN 0 +#define GMU_MX_DOMAIN 1 + u16 feature_rev; + struct a6xx_hfi_limits_cfg cfg; +} __packed; + #endif From bb79a606321ae63cb086bd34d38de7bb1a1231f7 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:00 +0530 Subject: [PATCH 566/712] drm/msm/a8xx: Add SKU table for A840 Add the SKU table in the catalog for A840 GPU. This data helps to pick the correct bin from the OPP table based on the speed_bin fuse value. Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714673/ Message-ID: <20260327-a8xx-gpu-batch2-v2-11-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 38561f26837e..f6b9792531a6 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1954,6 +1954,12 @@ static const struct adreno_info a8xx_gpus[] = { }, }, .preempt_record_size = 19708 * SZ_1K, + .speedbins = ADRENO_SPEEDBINS( + { 0, 0 }, + { 273, 1 }, + { 252, 2 }, + { 221, 3 }, + ), } }; From 4ac686bfd1929ef659a99f893ebe8faf7f35c76c Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:01 +0530 Subject: [PATCH 567/712] drm/msm/a6xx: Add soft fuse detection support Recent chipsets like Glymur supports a new mechanism for SKU detection. A new CX_MISC register exposes the combined (or final) speedbin value from both HW fuse register and the Soft Fuse register. Implement this new SKU detection along with a new quirk to identify the GPUs that has soft fuse support. There is a side effect of this patch on A4x and older series. The speedbin field in the MSM_PARAM_CHIPID will be 0 instead of 0xffff. This should be okay as Mesa correctly handles it. Speedbin was not even a thing when those GPUs' support were added. Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714676/ Message-ID: <20260327-a8xx-gpu-batch2-v2-12-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 6 +++ drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 41 +++++++++++++++---- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 5 --- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + drivers/gpu/drm/msm/registers/adreno/a6xx.xml | 4 ++ 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 79a441e91fa1..d7ed3225f635 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1731,6 +1731,7 @@ static struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) struct adreno_gpu *adreno_gpu; struct msm_gpu *gpu; unsigned int nr_rings; + u32 speedbin; int ret; a5xx_gpu = kzalloc(sizeof(*a5xx_gpu), GFP_KERNEL); @@ -1757,6 +1758,11 @@ static struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) return ERR_PTR(ret); } + /* Set the speedbin value that is passed to userspace */ + if (adreno_read_speedbin(&pdev->dev, &speedbin) || !speedbin) + speedbin = 0xffff; + adreno_gpu->speedbin = (uint16_t) (0xffff & speedbin); + msm_mmu_set_fault_handler(to_msm_vm(gpu->vm)->mmu, gpu, a5xx_fault_handler); diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 252a040e02b0..fb52557e22d8 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -2546,13 +2546,33 @@ static u32 fuse_to_supp_hw(const struct adreno_info *info, u32 fuse) return UINT_MAX; } -static int a6xx_set_supported_hw(struct device *dev, const struct adreno_info *info) +static int a6xx_read_speedbin(struct device *dev, struct a6xx_gpu *a6xx_gpu, + const struct adreno_info *info, u32 *speedbin) +{ + int ret; + + /* Use speedbin fuse if present. Otherwise, fallback to softfuse */ + ret = adreno_read_speedbin(dev, speedbin); + if (ret != -ENOENT) + return ret; + + if (info->quirks & ADRENO_QUIRK_SOFTFUSE) { + *speedbin = a6xx_llc_read(a6xx_gpu, REG_A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS); + *speedbin = A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS_FINALFREQLIMIT(*speedbin); + return 0; + } + + return -ENOENT; +} + +static int a6xx_set_supported_hw(struct device *dev, struct a6xx_gpu *a6xx_gpu, + const struct adreno_info *info) { u32 supp_hw; u32 speedbin; int ret; - ret = adreno_read_speedbin(dev, &speedbin); + ret = a6xx_read_speedbin(dev, a6xx_gpu, info, &speedbin); /* * -ENOENT means that the platform doesn't support speedbin which is * fine @@ -2586,11 +2606,13 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev = priv->gpu_pdev; struct adreno_platform_config *config = pdev->dev.platform_data; + const struct adreno_info *info = config->info; struct device_node *node; struct a6xx_gpu *a6xx_gpu; struct adreno_gpu *adreno_gpu; struct msm_gpu *gpu; extern int enable_preemption; + u32 speedbin; bool is_a7xx; int ret, nr_rings = 1; @@ -2614,14 +2636,14 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) adreno_gpu->gmu_is_wrapper = of_device_is_compatible(node, "qcom,adreno-gmu-wrapper"); adreno_gpu->base.hw_apriv = - !!(config->info->quirks & ADRENO_QUIRK_HAS_HW_APRIV); + !!(info->quirks & ADRENO_QUIRK_HAS_HW_APRIV); /* gpu->info only gets assigned in adreno_gpu_init(). A8x is included intentionally */ - is_a7xx = config->info->family >= ADRENO_7XX_GEN1; + is_a7xx = info->family >= ADRENO_7XX_GEN1; a6xx_llc_slices_init(pdev, a6xx_gpu, is_a7xx); - ret = a6xx_set_supported_hw(&pdev->dev, config->info); + ret = a6xx_set_supported_hw(&pdev->dev, a6xx_gpu, info); if (ret) { a6xx_llc_slices_destroy(a6xx_gpu); kfree(a6xx_gpu); @@ -2629,15 +2651,20 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) } if ((enable_preemption == 1) || (enable_preemption == -1 && - (config->info->quirks & ADRENO_QUIRK_PREEMPTION))) + (info->quirks & ADRENO_QUIRK_PREEMPTION))) nr_rings = 4; - ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, nr_rings); + ret = adreno_gpu_init(dev, pdev, adreno_gpu, info->funcs, nr_rings); if (ret) { a6xx_destroy(&(a6xx_gpu->base.base)); return ERR_PTR(ret); } + /* Set the speedbin value that is passed to userspace */ + if (a6xx_read_speedbin(&pdev->dev, a6xx_gpu, info, &speedbin) || !speedbin) + speedbin = 0xffff; + adreno_gpu->speedbin = (uint16_t) (0xffff & speedbin); + /* * For now only clamp to idle freq for devices where this is known not * to cause power supply issues: diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 785e99fb5bd5..0dbeb332f8d1 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -1182,7 +1182,6 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct msm_gpu_config adreno_gpu_config = { 0 }; struct msm_gpu *gpu = &adreno_gpu->base; const char *gpu_name; - u32 speedbin; int ret; adreno_gpu->funcs = funcs; @@ -1211,10 +1210,6 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, devm_pm_opp_set_clkname(dev, "core"); } - if (adreno_read_speedbin(dev, &speedbin) || !speedbin) - speedbin = 0xffff; - adreno_gpu->speedbin = (uint16_t) (0xffff & speedbin); - gpu_name = devm_kasprintf(dev, GFP_KERNEL, "%"ADRENO_CHIPID_FMT, ADRENO_CHIPID_ARGS(config->chip_id)); if (!gpu_name) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 41b0e376ee31..834f6fd2a89e 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -63,6 +63,7 @@ enum adreno_family { #define ADRENO_QUIRK_PREEMPTION BIT(5) #define ADRENO_QUIRK_4GB_VA BIT(6) #define ADRENO_QUIRK_IFPC BIT(7) +#define ADRENO_QUIRK_SOFTFUSE BIT(8) /* Helper for formating the chip_id in the way that userspace tools like * crashdec expect. diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml index 3941e7510754..2309870f5031 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml @@ -5016,6 +5016,10 @@ by a particular renderpass/blit. + + + + From dd108bb9da731d1d68245a7460c6a54a584c913d Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:02 +0530 Subject: [PATCH 568/712] drm/msm/a6xx: Add SKU detection support for X2-85 Add the Speedbin table to the catalog to enable SKU detection support for X2-85 GPU found in Glymur chipset. As this chipset support the SOFT FUSE mechanism, enable the ADRENO_QUIRK_SOFTFUSE quirk too. Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714677/ Message-ID: <20260327-a8xx-gpu-batch2-v2-13-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index f6b9792531a6..758bc7bd31f6 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1902,7 +1902,8 @@ static const struct adreno_info a8xx_gpus[] = { .gmem = 21 * SZ_1M, .inactive_period = DRM_MSM_INACTIVE_PERIOD, .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT | - ADRENO_QUIRK_HAS_HW_APRIV, + ADRENO_QUIRK_HAS_HW_APRIV | + ADRENO_QUIRK_SOFTFUSE, .funcs = &a8xx_gpu_funcs, .a6xx = &(const struct a6xx_info) { .protect = &x285_protect, @@ -1922,6 +1923,12 @@ static const struct adreno_info a8xx_gpus[] = { { /* sentinel */ }, }, }, + .speedbins = ADRENO_SPEEDBINS( + { 0, 0 }, + { 388, 1 }, + { 357, 2 }, + { 284, 3 }, + ), }, { .chip_ids = ADRENO_CHIP_IDS(0x44050a01), .family = ADRENO_8XX_GEN2, From ee37487ffecff1de834fa05b0e4b1c1d920a8189 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:03 +0530 Subject: [PATCH 569/712] drm/msm/a8xx: Implement IFPC support for A840 Implement pwrup reglist support and add the necessary register configurations to enable IFPC support on A840 Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714679/ Message-ID: <20260327-a8xx-gpu-batch2-v2-14-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 185 +++++++++++++++++++++- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 99 +++++++++++- 2 files changed, 281 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 758bc7bd31f6..53548f6e891b 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1891,6 +1891,185 @@ static const struct adreno_reglist a840_gbif[] = { { }, }; +static const uint32_t a840_pwrup_reglist_regs[] = { + REG_A7XX_SP_HLSQ_TIMEOUT_THRESHOLD_DP, + REG_A7XX_SP_READ_SEL, + REG_A6XX_UCHE_MODE_CNTL, + REG_A8XX_UCHE_VARB_IDLE_TIMEOUT, + REG_A8XX_UCHE_GBIF_GX_CONFIG, + REG_A8XX_UCHE_CCHE_MODE_CNTL, + REG_A8XX_UCHE_CCHE_CACHE_WAYS, + REG_A8XX_UCHE_CACHE_WAYS, + REG_A8XX_UCHE_CCHE_GC_GMEM_RANGE_MIN, + REG_A8XX_UCHE_CCHE_GC_GMEM_RANGE_MIN + 1, + REG_A8XX_UCHE_CCHE_LPAC_GMEM_RANGE_MIN, + REG_A8XX_UCHE_CCHE_LPAC_GMEM_RANGE_MIN + 1, + REG_A8XX_UCHE_CCHE_TRAP_BASE, + REG_A8XX_UCHE_CCHE_TRAP_BASE + 1, + REG_A8XX_UCHE_CCHE_WRITE_THRU_BASE, + REG_A8XX_UCHE_CCHE_WRITE_THRU_BASE + 1, + REG_A8XX_UCHE_HW_DBG_CNTL, + REG_A8XX_UCHE_WRITE_THRU_BASE, + REG_A8XX_UCHE_WRITE_THRU_BASE + 1, + REG_A8XX_UCHE_TRAP_BASE, + REG_A8XX_UCHE_TRAP_BASE + 1, + REG_A8XX_UCHE_CLIENT_PF, + REG_A8XX_RB_CMP_NC_MODE_CNTL, + REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN, + REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN + 1, + REG_A6XX_TPL1_NC_MODE_CNTL, + REG_A6XX_TPL1_DBG_ECO_CNTL, + REG_A6XX_TPL1_DBG_ECO_CNTL1, + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(0), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(1), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(2), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(3), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(4), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(5), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(6), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(7), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(8), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(9), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(10), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(11), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(12), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(13), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(14), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(15), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(16), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(17), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(18), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(19), +}; +DECLARE_ADRENO_REGLIST_LIST(a840_pwrup_reglist); + +static const u32 a840_ifpc_reglist_regs[] = { + REG_A8XX_RBBM_NC_MODE_CNTL, + REG_A8XX_RBBM_SLICE_NC_MODE_CNTL, + REG_A6XX_SP_NC_MODE_CNTL, + REG_A6XX_SP_CHICKEN_BITS, + REG_A8XX_SP_SS_CHICKEN_BITS_0, + REG_A7XX_SP_CHICKEN_BITS_1, + REG_A7XX_SP_CHICKEN_BITS_2, + REG_A7XX_SP_CHICKEN_BITS_3, + REG_A8XX_SP_CHICKEN_BITS_4, + REG_A6XX_SP_PERFCTR_SHADER_MASK, + REG_A8XX_RBBM_SLICE_PERFCTR_CNTL, + REG_A8XX_RBBM_SLICE_INTERFACE_HANG_INT_CNTL, + REG_A7XX_SP_HLSQ_DBG_ECO_CNTL, + REG_A7XX_SP_HLSQ_DBG_ECO_CNTL_1, + REG_A7XX_SP_HLSQ_DBG_ECO_CNTL_2, + REG_A8XX_SP_HLSQ_DBG_ECO_CNTL_3, + REG_A8XX_SP_HLSQ_LPAC_GMEM_RANGE_MIN, + REG_A8XX_SP_HLSQ_LPAC_GMEM_RANGE_MIN + 1, + REG_A8XX_CP_INTERRUPT_STATUS_MASK_GLOBAL, + REG_A8XX_RBBM_PERFCTR_CNTL, + REG_A8XX_CP_PROTECT_GLOBAL(0), + REG_A8XX_CP_PROTECT_GLOBAL(1), + REG_A8XX_CP_PROTECT_GLOBAL(2), + REG_A8XX_CP_PROTECT_GLOBAL(3), + REG_A8XX_CP_PROTECT_GLOBAL(4), + REG_A8XX_CP_PROTECT_GLOBAL(5), + REG_A8XX_CP_PROTECT_GLOBAL(6), + REG_A8XX_CP_PROTECT_GLOBAL(7), + REG_A8XX_CP_PROTECT_GLOBAL(8), + REG_A8XX_CP_PROTECT_GLOBAL(9), + REG_A8XX_CP_PROTECT_GLOBAL(10), + REG_A8XX_CP_PROTECT_GLOBAL(11), + REG_A8XX_CP_PROTECT_GLOBAL(12), + REG_A8XX_CP_PROTECT_GLOBAL(13), + REG_A8XX_CP_PROTECT_GLOBAL(14), + REG_A8XX_CP_PROTECT_GLOBAL(15), + REG_A8XX_CP_PROTECT_GLOBAL(16), + REG_A8XX_CP_PROTECT_GLOBAL(17), + REG_A8XX_CP_PROTECT_GLOBAL(18), + REG_A8XX_CP_PROTECT_GLOBAL(19), + REG_A8XX_CP_PROTECT_GLOBAL(20), + REG_A8XX_CP_PROTECT_GLOBAL(21), + REG_A8XX_CP_PROTECT_GLOBAL(22), + REG_A8XX_CP_PROTECT_GLOBAL(23), + REG_A8XX_CP_PROTECT_GLOBAL(24), + REG_A8XX_CP_PROTECT_GLOBAL(25), + REG_A8XX_CP_PROTECT_GLOBAL(26), + REG_A8XX_CP_PROTECT_GLOBAL(27), + REG_A8XX_CP_PROTECT_GLOBAL(28), + REG_A8XX_CP_PROTECT_GLOBAL(29), + REG_A8XX_CP_PROTECT_GLOBAL(30), + REG_A8XX_CP_PROTECT_GLOBAL(31), + REG_A8XX_CP_PROTECT_GLOBAL(32), + REG_A8XX_CP_PROTECT_GLOBAL(33), + REG_A8XX_CP_PROTECT_GLOBAL(34), + REG_A8XX_CP_PROTECT_GLOBAL(35), + REG_A8XX_CP_PROTECT_GLOBAL(36), + REG_A8XX_CP_PROTECT_GLOBAL(37), + REG_A8XX_CP_PROTECT_GLOBAL(38), + REG_A8XX_CP_PROTECT_GLOBAL(39), + REG_A8XX_CP_PROTECT_GLOBAL(40), + REG_A8XX_CP_PROTECT_GLOBAL(41), + REG_A8XX_CP_PROTECT_GLOBAL(42), + REG_A8XX_CP_PROTECT_GLOBAL(43), + REG_A8XX_CP_PROTECT_GLOBAL(44), + REG_A8XX_CP_PROTECT_GLOBAL(45), + REG_A8XX_CP_PROTECT_GLOBAL(46), + REG_A8XX_CP_PROTECT_GLOBAL(47), + REG_A8XX_CP_PROTECT_GLOBAL(48), + REG_A8XX_CP_PROTECT_GLOBAL(49), + REG_A8XX_CP_PROTECT_GLOBAL(50), + REG_A8XX_CP_PROTECT_GLOBAL(51), + REG_A8XX_CP_PROTECT_GLOBAL(52), + REG_A8XX_CP_PROTECT_GLOBAL(53), + REG_A8XX_CP_PROTECT_GLOBAL(54), + REG_A8XX_CP_PROTECT_GLOBAL(55), + REG_A8XX_CP_PROTECT_GLOBAL(56), + REG_A8XX_CP_PROTECT_GLOBAL(57), + REG_A8XX_CP_PROTECT_GLOBAL(58), + REG_A8XX_CP_PROTECT_GLOBAL(59), + REG_A8XX_CP_PROTECT_GLOBAL(60), + REG_A8XX_CP_PROTECT_GLOBAL(61), + REG_A8XX_CP_PROTECT_GLOBAL(62), + REG_A8XX_CP_PROTECT_GLOBAL(63), +}; +DECLARE_ADRENO_REGLIST_LIST(a840_ifpc_reglist); + +static const struct adreno_reglist_pipe a840_dyn_pwrup_reglist_regs[] = { + { REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_GRAS_NC_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_GRAS_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_1, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_2, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_3, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_4, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_VIS_STREAM_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A7XX_RB_CCU_CNTL, 0, BIT(PIPE_BR) }, + { REG_A7XX_RB_CCU_DBG_ECO_CNTL, 0, BIT(PIPE_BR)}, + { REG_A8XX_RB_CCU_NC_MODE_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_CMP_NC_MODE_CNTL, 0, BIT(PIPE_BR) }, + { REG_A6XX_RB_RBP_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0, BIT(PIPE_BR) }, + { REG_A6XX_RB_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0, BIT(PIPE_BR) }, + { REG_A7XX_VFD_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BV_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BR_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_LP_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_CP_HW_FAULT_STATUS_MASK_PIPE, 0, BIT(PIPE_BR) | + BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) | + BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) }, + { REG_A8XX_CP_INTERRUPT_STATUS_MASK_PIPE, 0, BIT(PIPE_BR) | + BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) | + BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) }, + { REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC)}, + { REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) }, + { REG_A8XX_RB_GC_GMEM_PROTECT, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_LPAC_GMEM_PROTECT, 0, BIT(PIPE_BR) }, + { REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE, 0, BIT(PIPE_BR) }, +}; +DECLARE_ADRENO_REGLIST_PIPE_LIST(a840_dyn_pwrup_reglist); + static const struct adreno_info a8xx_gpus[] = { { .chip_ids = ADRENO_CHIP_IDS(0x44070001), @@ -1940,11 +2119,15 @@ static const struct adreno_info a8xx_gpus[] = { .gmem = 18 * SZ_1M, .inactive_period = DRM_MSM_INACTIVE_PERIOD, .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT | - ADRENO_QUIRK_HAS_HW_APRIV, + ADRENO_QUIRK_HAS_HW_APRIV | + ADRENO_QUIRK_IFPC, .funcs = &a8xx_gpu_funcs, .a6xx = &(const struct a6xx_info) { .protect = &a840_protect, .nonctxt_reglist = a840_nonctxt_regs, + .pwrup_reglist = &a840_pwrup_reglist, + .dyn_pwrup_reglist = &a840_dyn_pwrup_reglist, + .ifpc_reglist = &a840_ifpc_reglist, .gbif_cx = a840_gbif, .max_slices = 3, .gmu_chipid = 0x8020100, diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index d5c547d347e2..d6782bdde067 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -183,7 +183,7 @@ void a8xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring) /* Update HW if this is the current ring and we are not in preempt*/ if (!a6xx_in_preempt(a6xx_gpu)) { if (a6xx_gpu->cur_ring == ring) - gpu_write(gpu, REG_A6XX_CP_RB_WPTR, wptr); + a6xx_fenced_write(a6xx_gpu, REG_A6XX_CP_RB_WPTR, wptr, BIT(0), false); else ring->restore_wptr = true; } else { @@ -396,8 +396,87 @@ static void a8xx_nonctxt_config(struct msm_gpu *gpu, u32 *gmem_protect) a8xx_aperture_clear(gpu); } +static void a8xx_patch_pwrup_reglist(struct msm_gpu *gpu) +{ + const struct adreno_reglist_pipe_list *dyn_pwrup_reglist; + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + const struct adreno_reglist_list *reglist; + void *ptr = a6xx_gpu->pwrup_reglist_ptr; + struct cpu_gpu_lock *lock = ptr; + u32 *dest = (u32 *)&lock->regs[0]; + u32 dyn_pwrup_reglist_count = 0; + int i; + + lock->gpu_req = lock->cpu_req = lock->turn = 0; + + reglist = adreno_gpu->info->a6xx->ifpc_reglist; + if (reglist) { + lock->ifpc_list_len = reglist->count; + + /* + * For each entry in each of the lists, write the offset and the current + * register value into the GPU buffer + */ + for (i = 0; i < reglist->count; i++) { + *dest++ = reglist->regs[i]; + *dest++ = gpu_read(gpu, reglist->regs[i]); + } + } + + reglist = adreno_gpu->info->a6xx->pwrup_reglist; + if (reglist) { + lock->preemption_list_len = reglist->count; + + for (i = 0; i < reglist->count; i++) { + *dest++ = reglist->regs[i]; + *dest++ = gpu_read(gpu, reglist->regs[i]); + } + } + + /* + * The overall register list is composed of + * 1. Static IFPC-only registers + * 2. Static IFPC + preemption registers + * 3. Dynamic IFPC + preemption registers (ex: perfcounter selects) + * + * The first two lists are static. Size of these lists are stored as + * number of pairs in ifpc_list_len and preemption_list_len + * respectively. With concurrent binning, Some of the perfcounter + * registers being virtualized, CP needs to know the pipe id to program + * the aperture inorder to restore the same. Thus, third list is a + * dynamic list with triplets as + * (
), and the length is + * stored as number for triplets in dynamic_list_len. + */ + dyn_pwrup_reglist = adreno_gpu->info->a6xx->dyn_pwrup_reglist; + if (!dyn_pwrup_reglist) + goto done; + + for (u32 pipe_id = PIPE_BR; pipe_id <= PIPE_DDE_BV; pipe_id++) { + for (i = 0; i < dyn_pwrup_reglist->count; i++) { + if (!(dyn_pwrup_reglist->regs[i].pipe & BIT(pipe_id))) + continue; + *dest++ = A8XX_CP_APERTURE_CNTL_HOST_PIPEID(pipe_id); + *dest++ = dyn_pwrup_reglist->regs[i].offset; + *dest++ = a8xx_read_pipe_slice(gpu, + pipe_id, + a8xx_get_first_slice(a6xx_gpu), + dyn_pwrup_reglist->regs[i].offset); + dyn_pwrup_reglist_count++; + } + } + + lock->dynamic_list_len = dyn_pwrup_reglist_count; + +done: + a8xx_aperture_clear(gpu); +} + static int a8xx_cp_init(struct msm_gpu *gpu) { + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); struct msm_ringbuffer *ring = gpu->rb[0]; u32 mask; @@ -405,7 +484,7 @@ static int a8xx_cp_init(struct msm_gpu *gpu) OUT_PKT7(ring, CP_THREAD_CONTROL, 1); OUT_RING(ring, BIT(27)); - OUT_PKT7(ring, CP_ME_INIT, 4); + OUT_PKT7(ring, CP_ME_INIT, 7); /* Use multiple HW contexts */ mask = BIT(0); @@ -419,6 +498,9 @@ static int a8xx_cp_init(struct msm_gpu *gpu) /* Disable save/restore of performance counters across preemption */ mask |= BIT(6); + /* Enable the register init list with the spinlock */ + mask |= BIT(8); + OUT_RING(ring, mask); /* Enable multiple hardware contexts */ @@ -430,6 +512,14 @@ static int a8xx_cp_init(struct msm_gpu *gpu) /* Operation mode mask */ OUT_RING(ring, 0x00000002); + /* Lo address */ + OUT_RING(ring, lower_32_bits(a6xx_gpu->pwrup_reglist_iova)); + /* Hi address */ + OUT_RING(ring, upper_32_bits(a6xx_gpu->pwrup_reglist_iova)); + + /* Enable dyn pwrup list with triplets (offset, value, pipe) */ + OUT_RING(ring, BIT(31)); + a6xx_flush(gpu, ring); return a8xx_idle(gpu, ring) ? 0 : -EINVAL; } @@ -712,6 +802,11 @@ static int hw_init(struct msm_gpu *gpu) WARN_ON(!gmem_protect); a8xx_aperture_clear(gpu); + if (!a6xx_gpu->pwrup_reglist_emitted) { + a8xx_patch_pwrup_reglist(gpu); + a6xx_gpu->pwrup_reglist_emitted = true; + } + /* Enable hardware clockgating */ a8xx_set_hwcg(gpu, true); out: From a693602ef56f6bf89fb497f3e3410785b8ef05cc Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:04 +0530 Subject: [PATCH 570/712] drm/msm/a8xx: Preemption support for A840 The programing sequence related to preemption is unchanged from A7x. But there is some code churn due to register shuffling in A8x. So, split out the common code into a header file for code sharing and add/update additional changes required to support preemption feature on A8x GPUs. Finally, enable the preemption quirk in A840's catalog to enable this feature. Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714682/ Message-ID: <20260327-a8xx-gpu-batch2-v2-15-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 1 + drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 7 +- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 5 + drivers/gpu/drm/msm/adreno/a6xx_preempt.c | 77 +------ drivers/gpu/drm/msm/adreno/a6xx_preempt.h | 82 +++++++ drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 37 +++- drivers/gpu/drm/msm/adreno/a8xx_preempt.c | 259 ++++++++++++++++++++++ 8 files changed, 389 insertions(+), 80 deletions(-) create mode 100644 drivers/gpu/drm/msm/adreno/a6xx_preempt.h create mode 100644 drivers/gpu/drm/msm/adreno/a8xx_preempt.c diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 8b94c5f1cb68..ba45e99be05b 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -25,6 +25,7 @@ adreno-y := \ adreno/a6xx_hfi.o \ adreno/a6xx_preempt.o \ adreno/a8xx_gpu.o \ + adreno/a8xx_preempt.o \ adreno-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o \ diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 53548f6e891b..21f5a685196b 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -2120,6 +2120,7 @@ static const struct adreno_info a8xx_gpus[] = { .inactive_period = DRM_MSM_INACTIVE_PERIOD, .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT | ADRENO_QUIRK_HAS_HW_APRIV | + ADRENO_QUIRK_PREEMPTION | ADRENO_QUIRK_IFPC, .funcs = &a8xx_gpu_funcs, .a6xx = &(const struct a6xx_info) { diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index fb52557e22d8..ae592022bebc 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -410,7 +410,7 @@ static void a6xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) a6xx_flush(gpu, ring); } -static void a6xx_emit_set_pseudo_reg(struct msm_ringbuffer *ring, +void a6xx_emit_set_pseudo_reg(struct msm_ringbuffer *ring, struct a6xx_gpu *a6xx_gpu, struct msm_gpu_submitqueue *queue) { u64 preempt_postamble; @@ -620,7 +620,10 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) a6xx_flush(gpu, ring); /* Check to see if we need to start preemption */ - a6xx_preempt_trigger(gpu); + if (adreno_is_a8xx(adreno_gpu)) + a8xx_preempt_trigger(gpu); + else + a6xx_preempt_trigger(gpu); } static void a6xx_set_hwcg(struct msm_gpu *gpu, bool state) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index a4434a6a56dd..eb431e5e00b1 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -278,6 +278,8 @@ void a6xx_preempt_hw_init(struct msm_gpu *gpu); void a6xx_preempt_trigger(struct msm_gpu *gpu); void a6xx_preempt_irq(struct msm_gpu *gpu); void a6xx_preempt_fini(struct msm_gpu *gpu); +void a6xx_emit_set_pseudo_reg(struct msm_ringbuffer *ring, + struct a6xx_gpu *a6xx_gpu, struct msm_gpu_submitqueue *queue); int a6xx_preempt_submitqueue_setup(struct msm_gpu *gpu, struct msm_gpu_submitqueue *queue); void a6xx_preempt_submitqueue_close(struct msm_gpu *gpu, @@ -327,6 +329,9 @@ void a8xx_gpu_get_slice_info(struct msm_gpu *gpu); int a8xx_hw_init(struct msm_gpu *gpu); irqreturn_t a8xx_irq(struct msm_gpu *gpu); void a8xx_llc_activate(struct a6xx_gpu *a6xx_gpu); +void a8xx_preempt_hw_init(struct msm_gpu *gpu); +void a8xx_preempt_trigger(struct msm_gpu *gpu); +void a8xx_preempt_irq(struct msm_gpu *gpu); bool a8xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring); void a8xx_recover(struct msm_gpu *gpu); #endif /* __A6XX_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/adreno/a6xx_preempt.c b/drivers/gpu/drm/msm/adreno/a6xx_preempt.c index 747a22afad9f..df4cbf42e9a4 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_preempt.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_preempt.c @@ -6,85 +6,10 @@ #include "msm_gem.h" #include "a6xx_gpu.h" #include "a6xx_gmu.xml.h" +#include "a6xx_preempt.h" #include "msm_mmu.h" #include "msm_gpu_trace.h" -/* - * Try to transition the preemption state from old to new. Return - * true on success or false if the original state wasn't 'old' - */ -static inline bool try_preempt_state(struct a6xx_gpu *a6xx_gpu, - enum a6xx_preempt_state old, enum a6xx_preempt_state new) -{ - enum a6xx_preempt_state cur = atomic_cmpxchg(&a6xx_gpu->preempt_state, - old, new); - - return (cur == old); -} - -/* - * Force the preemption state to the specified state. This is used in cases - * where the current state is known and won't change - */ -static inline void set_preempt_state(struct a6xx_gpu *gpu, - enum a6xx_preempt_state new) -{ - /* - * preempt_state may be read by other cores trying to trigger a - * preemption or in the interrupt handler so barriers are needed - * before... - */ - smp_mb__before_atomic(); - atomic_set(&gpu->preempt_state, new); - /* ... and after*/ - smp_mb__after_atomic(); -} - -/* Write the most recent wptr for the given ring into the hardware */ -static inline void update_wptr(struct a6xx_gpu *a6xx_gpu, struct msm_ringbuffer *ring) -{ - unsigned long flags; - uint32_t wptr; - - spin_lock_irqsave(&ring->preempt_lock, flags); - - if (ring->restore_wptr) { - wptr = get_wptr(ring); - - a6xx_fenced_write(a6xx_gpu, REG_A6XX_CP_RB_WPTR, wptr, BIT(0), false); - - ring->restore_wptr = false; - } - - spin_unlock_irqrestore(&ring->preempt_lock, flags); -} - -/* Return the highest priority ringbuffer with something in it */ -static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu) -{ - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); - - unsigned long flags; - int i; - - for (i = 0; i < gpu->nr_rings; i++) { - bool empty; - struct msm_ringbuffer *ring = gpu->rb[i]; - - spin_lock_irqsave(&ring->preempt_lock, flags); - empty = (get_wptr(ring) == gpu->funcs->get_rptr(gpu, ring)); - if (!empty && ring == a6xx_gpu->cur_ring) - empty = ring->memptrs->fence == a6xx_gpu->last_seqno[i]; - spin_unlock_irqrestore(&ring->preempt_lock, flags); - - if (!empty) - return ring; - } - - return NULL; -} - static void a6xx_preempt_timer(struct timer_list *t) { struct a6xx_gpu *a6xx_gpu = timer_container_of(a6xx_gpu, t, diff --git a/drivers/gpu/drm/msm/adreno/a6xx_preempt.h b/drivers/gpu/drm/msm/adreno/a6xx_preempt.h new file mode 100644 index 000000000000..df36c945b836 --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/a6xx_preempt.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2023 Collabora, Ltd. */ +/* Copyright (c) 2024 Valve Corporation */ +/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ + +/* + * Try to transition the preemption state from old to new. Return + * true on success or false if the original state wasn't 'old' + */ +static inline bool try_preempt_state(struct a6xx_gpu *a6xx_gpu, + enum a6xx_preempt_state old, enum a6xx_preempt_state new) +{ + enum a6xx_preempt_state cur = atomic_cmpxchg(&a6xx_gpu->preempt_state, + old, new); + + return (cur == old); +} + +/* + * Force the preemption state to the specified state. This is used in cases + * where the current state is known and won't change + */ +static inline void set_preempt_state(struct a6xx_gpu *gpu, + enum a6xx_preempt_state new) +{ + /* + * preempt_state may be read by other cores trying to trigger a + * preemption or in the interrupt handler so barriers are needed + * before... + */ + smp_mb__before_atomic(); + atomic_set(&gpu->preempt_state, new); + /* ... and after */ + smp_mb__after_atomic(); +} + +/* Write the most recent wptr for the given ring into the hardware */ +static inline void update_wptr(struct a6xx_gpu *a6xx_gpu, struct msm_ringbuffer *ring) +{ + unsigned long flags; + uint32_t wptr; + + spin_lock_irqsave(&ring->preempt_lock, flags); + + if (ring->restore_wptr) { + wptr = get_wptr(ring); + + a6xx_fenced_write(a6xx_gpu, REG_A6XX_CP_RB_WPTR, wptr, BIT(0), false); + + ring->restore_wptr = false; + } + + spin_unlock_irqrestore(&ring->preempt_lock, flags); +} + +/* Return the highest priority ringbuffer with something in it */ +static inline struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + + unsigned long flags; + int i; + + for (i = 0; i < gpu->nr_rings; i++) { + bool empty; + struct msm_ringbuffer *ring = gpu->rb[i]; + + spin_lock_irqsave(&ring->preempt_lock, flags); + empty = (get_wptr(ring) == gpu->funcs->get_rptr(gpu, ring)); + if (!empty && ring == a6xx_gpu->cur_ring) + empty = ring->memptrs->fence == a6xx_gpu->last_seqno[i]; + spin_unlock_irqrestore(&ring->preempt_lock, flags); + + if (!empty) + return ring; + } + + return NULL; +} + diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index d6782bdde067..ccfccc45133f 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -473,6 +473,34 @@ static void a8xx_patch_pwrup_reglist(struct msm_gpu *gpu) a8xx_aperture_clear(gpu); } +static int a8xx_preempt_start(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + struct msm_ringbuffer *ring = gpu->rb[0]; + + if (gpu->nr_rings <= 1) + return 0; + + /* Turn CP protection off */ + OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1); + OUT_RING(ring, 0); + + a6xx_emit_set_pseudo_reg(ring, a6xx_gpu, NULL); + + /* Yield the floor on command completion */ + OUT_PKT7(ring, CP_CONTEXT_SWITCH_YIELD, 4); + OUT_RING(ring, 0x00); + OUT_RING(ring, 0x00); + OUT_RING(ring, 0x00); + /* Generate interrupt on preemption completion */ + OUT_RING(ring, 0x00); + + a6xx_flush(gpu, ring); + + return a8xx_idle(gpu, ring) ? 0 : -EINVAL; +} + static int a8xx_cp_init(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); @@ -748,6 +776,8 @@ static int hw_init(struct msm_gpu *gpu) gpu_write64(gpu, REG_A6XX_CP_RB_RPTR_ADDR, shadowptr(a6xx_gpu, gpu->rb[0])); gpu_write64(gpu, REG_A8XX_CP_RB_RPTR_ADDR_BV, rbmemptr(gpu->rb[0], bv_rptr)); + a8xx_preempt_hw_init(gpu); + for (i = 0; i < gpu->nr_rings; i++) a6xx_gpu->shadow[i] = 0; @@ -810,6 +840,9 @@ static int hw_init(struct msm_gpu *gpu) /* Enable hardware clockgating */ a8xx_set_hwcg(gpu, true); out: + /* Last step - yield the ringbuffer */ + a8xx_preempt_start(gpu); + /* * Tell the GMU that we are done touching the GPU and it can start power * management @@ -1219,11 +1252,11 @@ irqreturn_t a8xx_irq(struct msm_gpu *gpu) if (status & A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS) { msm_gpu_retire(gpu); - a6xx_preempt_trigger(gpu); + a8xx_preempt_trigger(gpu); } if (status & A6XX_RBBM_INT_0_MASK_CP_SW) - a6xx_preempt_irq(gpu); + a8xx_preempt_irq(gpu); return IRQ_HANDLED; } diff --git a/drivers/gpu/drm/msm/adreno/a8xx_preempt.c b/drivers/gpu/drm/msm/adreno/a8xx_preempt.c new file mode 100644 index 000000000000..3d8c33ba722e --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/a8xx_preempt.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ + +#include "msm_gem.h" +#include "a6xx_gpu.h" +#include "a6xx_gmu.xml.h" +#include "a6xx_preempt.h" +#include "msm_mmu.h" +#include "msm_gpu_trace.h" + +static void preempt_prepare_postamble(struct a6xx_gpu *a6xx_gpu) +{ + u32 *postamble = a6xx_gpu->preempt_postamble_ptr; + u32 count = 0; + + postamble[count++] = PKT7(CP_REG_RMW, 3); + postamble[count++] = REG_A8XX_RBBM_PERFCTR_SRAM_INIT_CMD; + postamble[count++] = 0; + postamble[count++] = 1; + + postamble[count++] = PKT7(CP_WAIT_REG_MEM, 6); + postamble[count++] = CP_WAIT_REG_MEM_0_FUNCTION(WRITE_EQ); + postamble[count++] = CP_WAIT_REG_MEM_POLL_ADDR_LO( + REG_A8XX_RBBM_PERFCTR_SRAM_INIT_STATUS); + postamble[count++] = CP_WAIT_REG_MEM_POLL_ADDR_HI(0); + postamble[count++] = CP_WAIT_REG_MEM_3_REF(0x1); + postamble[count++] = CP_WAIT_REG_MEM_4_MASK(0x1); + postamble[count++] = CP_WAIT_REG_MEM_5_DELAY_LOOP_CYCLES(0); + + a6xx_gpu->preempt_postamble_len = count; + + a6xx_gpu->postamble_enabled = true; +} + +static void preempt_disable_postamble(struct a6xx_gpu *a6xx_gpu) +{ + u32 *postamble = a6xx_gpu->preempt_postamble_ptr; + + /* + * Disable the postamble by replacing the first packet header with a NOP + * that covers the whole buffer. + */ + *postamble = PKT7(CP_NOP, (a6xx_gpu->preempt_postamble_len - 1)); + + a6xx_gpu->postamble_enabled = false; +} + +/* + * Set preemption keepalive vote. Please note that this vote is different from the one used in + * a8xx_irq() + */ +static void a8xx_preempt_keepalive_vote(struct msm_gpu *gpu, bool on) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + + gmu_write(&a6xx_gpu->gmu, REG_A8XX_GMU_PWR_COL_PREEMPT_KEEPALIVE, on); +} + +void a8xx_preempt_irq(struct msm_gpu *gpu) +{ + uint32_t status; + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + struct drm_device *dev = gpu->dev; + + if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING)) + return; + + /* Delete the preemption watchdog timer */ + timer_delete(&a6xx_gpu->preempt_timer); + + /* + * The hardware should be setting the stop bit of CP_CONTEXT_SWITCH_CNTL + * to zero before firing the interrupt, but there is a non zero chance + * of a hardware condition or a software race that could set it again + * before we have a chance to finish. If that happens, log and go for + * recovery + */ + status = gpu_read(gpu, REG_A8XX_CP_CONTEXT_SWITCH_CNTL); + if (unlikely(status & A8XX_CP_CONTEXT_SWITCH_CNTL_STOP)) { + DRM_DEV_ERROR(&gpu->pdev->dev, + "!!!!!!!!!!!!!!!! preemption faulted !!!!!!!!!!!!!! irq\n"); + set_preempt_state(a6xx_gpu, PREEMPT_FAULTED); + dev_err(dev->dev, "%s: Preemption failed to complete\n", + gpu->name); + kthread_queue_work(gpu->worker, &gpu->recover_work); + return; + } + + a6xx_gpu->cur_ring = a6xx_gpu->next_ring; + a6xx_gpu->next_ring = NULL; + + set_preempt_state(a6xx_gpu, PREEMPT_FINISH); + + update_wptr(a6xx_gpu, a6xx_gpu->cur_ring); + + set_preempt_state(a6xx_gpu, PREEMPT_NONE); + + a8xx_preempt_keepalive_vote(gpu, false); + + trace_msm_gpu_preemption_irq(a6xx_gpu->cur_ring->id); + + /* + * Retrigger preemption to avoid a deadlock that might occur when preemption + * is skipped due to it being already in flight when requested. + */ + a8xx_preempt_trigger(gpu); +} + +void a8xx_preempt_hw_init(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + int i; + + /* No preemption if we only have one ring */ + if (gpu->nr_rings == 1) + return; + + for (i = 0; i < gpu->nr_rings; i++) { + struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[i]; + + record_ptr->wptr = 0; + record_ptr->rptr = 0; + record_ptr->rptr_addr = shadowptr(a6xx_gpu, gpu->rb[i]); + record_ptr->info = 0; + record_ptr->data = 0; + record_ptr->rbase = gpu->rb[i]->iova; + } + + /* Write a 0 to signal that we aren't switching pagetables */ + gpu_write64(gpu, REG_A8XX_CP_CONTEXT_SWITCH_SMMU_INFO, 0); + + /* Enable the GMEM save/restore feature for preemption */ + gpu_write(gpu, REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE, 0x1); + + /* Reset the preemption state */ + set_preempt_state(a6xx_gpu, PREEMPT_NONE); + + spin_lock_init(&a6xx_gpu->eval_lock); + + /* Always come up on rb 0 */ + a6xx_gpu->cur_ring = gpu->rb[0]; +} + +void a8xx_preempt_trigger(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + unsigned long flags; + struct msm_ringbuffer *ring; + unsigned int cntl; + bool sysprof; + + if (gpu->nr_rings == 1) + return; + + /* + * Lock to make sure another thread attempting preemption doesn't skip it + * while we are still evaluating the next ring. This makes sure the other + * thread does start preemption if we abort it and avoids a soft lock. + */ + spin_lock_irqsave(&a6xx_gpu->eval_lock, flags); + + /* + * Try to start preemption by moving from NONE to START. If + * unsuccessful, a preemption is already in flight + */ + if (!try_preempt_state(a6xx_gpu, PREEMPT_NONE, PREEMPT_START)) { + spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags); + return; + } + + cntl = A8XX_CP_CONTEXT_SWITCH_CNTL_LEVEL(a6xx_gpu->preempt_level); + + if (a6xx_gpu->skip_save_restore) + cntl |= A8XX_CP_CONTEXT_SWITCH_CNTL_SKIP_SAVE_RESTORE; + + if (a6xx_gpu->uses_gmem) + cntl |= A8XX_CP_CONTEXT_SWITCH_CNTL_USES_GMEM; + + cntl |= A8XX_CP_CONTEXT_SWITCH_CNTL_STOP; + + /* Get the next ring to preempt to */ + ring = get_next_ring(gpu); + + /* + * If no ring is populated or the highest priority ring is the current + * one do nothing except to update the wptr to the latest and greatest + */ + if (!ring || (a6xx_gpu->cur_ring == ring)) { + set_preempt_state(a6xx_gpu, PREEMPT_FINISH); + update_wptr(a6xx_gpu, a6xx_gpu->cur_ring); + set_preempt_state(a6xx_gpu, PREEMPT_NONE); + spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags); + return; + } + + spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags); + + spin_lock_irqsave(&ring->preempt_lock, flags); + + struct a7xx_cp_smmu_info *smmu_info_ptr = + a6xx_gpu->preempt_smmu[ring->id]; + struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[ring->id]; + u64 ttbr0 = ring->memptrs->ttbr0; + u32 context_idr = ring->memptrs->context_idr; + + smmu_info_ptr->ttbr0 = ttbr0; + smmu_info_ptr->context_idr = context_idr; + record_ptr->wptr = get_wptr(ring); + + /* + * The GPU will write the wptr we set above when we preempt. Reset + * restore_wptr to make sure that we don't write WPTR to the same + * thing twice. It's still possible subsequent submissions will update + * wptr again, in which case they will set the flag to true. This has + * to be protected by the lock for setting the flag and updating wptr + * to be atomic. + */ + ring->restore_wptr = false; + + trace_msm_gpu_preemption_trigger(a6xx_gpu->cur_ring->id, ring->id); + + spin_unlock_irqrestore(&ring->preempt_lock, flags); + + /* Set the keepalive bit to keep the GPU ON until preemption is complete */ + a8xx_preempt_keepalive_vote(gpu, true); + + a6xx_fenced_write(a6xx_gpu, + REG_A8XX_CP_CONTEXT_SWITCH_SMMU_INFO, a6xx_gpu->preempt_smmu_iova[ring->id], + BIT(1), true); + + a6xx_fenced_write(a6xx_gpu, + REG_A8XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR, + a6xx_gpu->preempt_iova[ring->id], BIT(1), true); + + a6xx_gpu->next_ring = ring; + + /* Start a timer to catch a stuck preemption */ + mod_timer(&a6xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000)); + + /* Enable or disable postamble as needed */ + sysprof = refcount_read(&a6xx_gpu->base.base.sysprof_active) > 1; + + if (!sysprof && !a6xx_gpu->postamble_enabled) + preempt_prepare_postamble(a6xx_gpu); + + if (sysprof && a6xx_gpu->postamble_enabled) + preempt_disable_postamble(a6xx_gpu); + + /* Set the preemption state to triggered */ + set_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED); + + /* Trigger the preemption */ + a6xx_fenced_write(a6xx_gpu, REG_A8XX_CP_CONTEXT_SWITCH_CNTL, cntl, BIT(1), false); +} + From 7fad33097e67781ad2a295652702788a5ab8e065 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:05 +0530 Subject: [PATCH 571/712] drm/msm/a6xx: Enable Preemption on X2-85 Add the save-restore register lists and set the necessary quirk flags in the catalog to enable the Preemption feature on Adreno X2-85 GPU. Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/714684/ Message-ID: <20260327-a8xx-gpu-batch2-v2-16-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 21f5a685196b..550ff3a9b82e 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1761,6 +1761,44 @@ static const u32 x285_protect_regs[] = { DECLARE_ADRENO_PROTECT(x285_protect, 15); +static const struct adreno_reglist_pipe x285_dyn_pwrup_reglist_regs[] = { + { REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_GRAS_NC_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_GRAS_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_1, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_2, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_3, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_4, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_VIS_STREAM_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A7XX_RB_CCU_CNTL, 0, BIT(PIPE_BR) }, + { REG_A7XX_RB_CCU_DBG_ECO_CNTL, 0, BIT(PIPE_BR)}, + { REG_A8XX_RB_CCU_NC_MODE_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_CMP_NC_MODE_CNTL, 0, BIT(PIPE_BR) }, + { REG_A6XX_RB_RBP_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0, BIT(PIPE_BR) }, + { REG_A7XX_VFD_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BV_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BR_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_LP_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_CP_HW_FAULT_STATUS_MASK_PIPE, 0, BIT(PIPE_BR) | + BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) | + BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) }, + { REG_A8XX_CP_INTERRUPT_STATUS_MASK_PIPE, 0, BIT(PIPE_BR) | + BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) | + BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) }, + { REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC)}, + { REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) }, + { REG_A8XX_RB_GC_GMEM_PROTECT, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_LPAC_GMEM_PROTECT, 0, BIT(PIPE_BR) }, + { REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE, 0, BIT(PIPE_BR) }, +}; +DECLARE_ADRENO_REGLIST_PIPE_LIST(x285_dyn_pwrup_reglist); + static const struct adreno_reglist_pipe a840_nonctxt_regs[] = { { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) }, { REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00000800, BIT(PIPE_BV) | BIT(PIPE_BR) }, @@ -2082,11 +2120,15 @@ static const struct adreno_info a8xx_gpus[] = { .inactive_period = DRM_MSM_INACTIVE_PERIOD, .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT | ADRENO_QUIRK_HAS_HW_APRIV | + ADRENO_QUIRK_PREEMPTION | ADRENO_QUIRK_SOFTFUSE, .funcs = &a8xx_gpu_funcs, .a6xx = &(const struct a6xx_info) { .protect = &x285_protect, .nonctxt_reglist = x285_nonctxt_regs, + .pwrup_reglist = &a840_pwrup_reglist, + .dyn_pwrup_reglist = &x285_dyn_pwrup_reglist, + .ifpc_reglist = &a840_ifpc_reglist, .gbif_cx = a840_gbif, .max_slices = 4, .gmu_chipid = 0x8010100, From 64ac64bb62064dbfbb66964331f5a2af6adeb03b Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:06 +0530 Subject: [PATCH 572/712] drm/msm/adreno: Expose a PARAM to check AQE support AQE (Applicaton Qrisc Engine) is required to support VK ray-pipeline. Two conditions should be met to use this HW: 1. AQE firmware should be loaded and programmed 2. Preemption support Expose a new MSM_PARAM to allow userspace to query its support. Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/714685/ Message-ID: <20260327-a8xx-gpu-batch2-v2-17-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 13 +++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.c | 4 ++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + include/uapi/drm/msm_drm.h | 1 + 4 files changed, 19 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index ae592022bebc..615509c8917e 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -2604,6 +2604,17 @@ static int a6xx_set_supported_hw(struct device *dev, struct a6xx_gpu *a6xx_gpu, return 0; } +static bool a6xx_aqe_is_enabled(struct adreno_gpu *adreno_gpu) +{ + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + + /* + * AQE uses preemption context record as scratch pad, so check if + * preemption is enabled + */ + return (adreno_gpu->base.nr_rings > 1) && !!a6xx_gpu->aqe_bo; +} + static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; @@ -2803,6 +2814,7 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = { .bus_halt = a6xx_bus_clear_pending_transactions, .mmu_fault_handler = a6xx_fault_handler, .gx_is_on = a7xx_gmu_gx_is_on, + .aqe_is_enabled = a6xx_aqe_is_enabled, }; const struct adreno_gpu_funcs a8xx_gpu_funcs = { @@ -2831,4 +2843,5 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = { .bus_halt = a8xx_bus_clear_pending_transactions, .mmu_fault_handler = a8xx_fault_handler, .gx_is_on = a8xx_gmu_gx_is_on, + .aqe_is_enabled = a6xx_aqe_is_enabled, }; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 0dbeb332f8d1..85d135a9d336 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -441,6 +441,10 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, case MSM_PARAM_HAS_PRR: *value = adreno_smmu_has_prr(gpu); return 0; + case MSM_PARAM_AQE: + *value = !!(adreno_gpu->funcs->aqe_is_enabled && + adreno_gpu->funcs->aqe_is_enabled(adreno_gpu)); + return 0; default: return UERR(EINVAL, drm, "%s: invalid param: %u", gpu->name, param); } diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 834f6fd2a89e..ec643b84646b 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -80,6 +80,7 @@ struct adreno_gpu_funcs { void (*bus_halt)(struct adreno_gpu *adreno_gpu, bool gx_off); int (*mmu_fault_handler)(void *arg, unsigned long iova, int flags, void *data); bool (*gx_is_on)(struct adreno_gpu *adreno_gpu); + bool (*aqe_is_enabled)(struct adreno_gpu *adreno_gpu); }; struct adreno_reglist { diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 5c67294edc95..b99098792371 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -117,6 +117,7 @@ struct drm_msm_timespec { * ioctl will throw -EPIPE. */ #define MSM_PARAM_EN_VM_BIND 0x16 /* WO, once */ +#define MSM_PARAM_AQE 0x17 /* RO */ /* For backwards compat. The original support for preemption was based on * a single ring per priority level so # of priority levels equals the # From a8502a79e832b861e99218cbd2d8f4312d62e225 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 31 Mar 2026 13:42:28 -0700 Subject: [PATCH 573/712] bpf: Fix regsafe() for pointers to packet In case rold->reg->range == BEYOND_PKT_END && rcur->reg->range == N regsafe() may return true which may lead to current state with valid packet range not being explored. Fix the bug. Fixes: 6d94e741a8ff ("bpf: Support for pointers beyond pkt_end.") Signed-off-by: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Reviewed-by: Daniel Borkmann Reviewed-by: Amery Hung Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/20260331204228.26726-1-alexei.starovoitov@gmail.com --- kernel/bpf/verifier.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f108c01ff6d0..a3388cb8fcbd 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19915,8 +19915,13 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, * since someone could have accessed through (ptr - k), or * even done ptr -= k in a register, to get a safe access. */ - if (rold->range > rcur->range) + if (rold->range < 0 || rcur->range < 0) { + /* special case for [BEYOND|AT]_PKT_END */ + if (rold->range != rcur->range) + return false; + } else if (rold->range > rcur->range) { return false; + } /* If the offsets don't match, we can't trust our alignment; * nor can we be sure that we won't fall out of range. */ From 9ca562bb8e66978b53028fa32b1a190708e6a091 Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Mon, 30 Mar 2026 16:46:24 +0800 Subject: [PATCH 574/712] net: ipv6: flowlabel: defer exclusive option free until RCU teardown `ip6fl_seq_show()` walks the global flowlabel hash under the seq-file RCU read-side lock and prints `fl->opt->opt_nflen` when an option block is present. Exclusive flowlabels currently free `fl->opt` as soon as `fl->users` drops to zero in `fl_release()`. However, the surrounding `struct ip6_flowlabel` remains visible in the global hash table until later garbage collection removes it and `fl_free_rcu()` finally tears it down. A concurrent `/proc/net/ip6_flowlabel` reader can therefore race that early `kfree()` and dereference freed option state, triggering a crash in `ip6fl_seq_show()`. Fix this by keeping `fl->opt` alive until `fl_free_rcu()`. That matches the lifetime already required for the enclosing flowlabel while readers can still reach it under RCU. Fixes: d3aedd5ebd4b ("ipv6 flowlabel: Convert hash list to RCU.") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/07351f0ec47bcee289576f39f9354f4a64add6e4.1774855883.git.zcliangcn@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_flowlabel.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 7c12bf75beed..c92f98c6f6ec 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -133,11 +133,6 @@ static void fl_release(struct ip6_flowlabel *fl) if (time_after(ttd, fl->expires)) fl->expires = ttd; ttd = fl->expires; - if (fl->opt && fl->share == IPV6_FL_S_EXCL) { - struct ipv6_txoptions *opt = fl->opt; - fl->opt = NULL; - kfree(opt); - } if (!timer_pending(&ip6_fl_gc_timer) || time_after(ip6_fl_gc_timer.expires, ttd)) mod_timer(&ip6_fl_gc_timer, ttd); From c76fef7dcd9372e3476d4df5e0a72ed5919a814b Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 31 Mar 2026 23:10:20 +0200 Subject: [PATCH 575/712] bpf: Fix grace period wait for tracepoint bpf_link Recently, tracepoints were switched from using disabled preemption (which acts as RCU read section) to SRCU-fast when they are not faultable. This means that to do a proper grace period wait for programs running in such tracepoints, we must use SRCU's grace period wait. This is only for non-faultable tracepoints, faultable ones continue using RCU Tasks Trace. However, bpf_link_free() currently does call_rcu() for all cases when the link is non-sleepable (hence, for tracepoints, non-faultable). Fix this by doing a call_srcu() grace period wait. As far RCU Tasks Trace gp -> RCU gp chaining is concerned, it is deemed unnecessary for tracepoint programs. The link and program are either accessed under RCU Tasks Trace protection, or SRCU-fast protection now. The earlier logic of chaining both RCU Tasks Trace and RCU gp waits was to generalize the logic, even if it conceded an extra RCU gp wait, however that is unnecessary for tracepoints even before this change. In practice no cost was paid since rcu_trace_implies_rcu_gp() was always true. Hence we need not chaining any RCU gp after the SRCU gp. For instance, in the non-faultable raw tracepoint, the RCU read section of the program in __bpf_trace_run() is enclosed in the SRCU gp, likewise for faultable raw tracepoint, the program is under the RCU Tasks Trace protection. Hence, the outermost scope can be waited upon to ensure correctness. Also, sleepable programs cannot be attached to non-faultable tracepoints, so whenever program or link is sleepable, only RCU Tasks Trace protection is being used for the link and prog. Fixes: a46023d5616e ("tracing: Guard __DECLARE_TRACE() use of __DO_TRACE_CALL() with SRCU-fast") Reviewed-by: Sun Jian Reviewed-by: Puranjay Mohan Acked-by: Andrii Nakryiko Signed-off-by: Kumar Kartikeya Dwivedi Acked-by: Steven Rostedt (Google) Link: https://lore.kernel.org/r/20260331211021.1632902-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 4 ++++ include/linux/tracepoint.h | 20 ++++++++++++++++++++ kernel/bpf/syscall.c | 25 +++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 05b34a6355b0..35b1e25bd104 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1854,6 +1854,10 @@ struct bpf_link_ops { * target hook is sleepable, we'll go through tasks trace RCU GP and * then "classic" RCU GP; this need for chaining tasks trace and * classic RCU GPs is designated by setting bpf_link->sleepable flag + * + * For non-sleepable tracepoint links we go through SRCU gp instead, + * since RCU is not used in that case. Sleepable tracepoints still + * follow the scheme above. */ void (*dealloc_deferred)(struct bpf_link *link); int (*detach)(struct bpf_link *link); diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 22ca1c8b54f3..1d7f29f5e901 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -122,6 +122,22 @@ static inline bool tracepoint_is_faultable(struct tracepoint *tp) { return tp->ext && tp->ext->faultable; } +/* + * Run RCU callback with the appropriate grace period wait for non-faultable + * tracepoints, e.g., those used in atomic context. + */ +static inline void call_tracepoint_unregister_atomic(struct rcu_head *rcu, rcu_callback_t func) +{ + call_srcu(&tracepoint_srcu, rcu, func); +} +/* + * Run RCU callback with the appropriate grace period wait for faultable + * tracepoints, e.g., those used in syscall context. + */ +static inline void call_tracepoint_unregister_syscall(struct rcu_head *rcu, rcu_callback_t func) +{ + call_rcu_tasks_trace(rcu, func); +} #else static inline void tracepoint_synchronize_unregister(void) { } @@ -129,6 +145,10 @@ static inline bool tracepoint_is_faultable(struct tracepoint *tp) { return false; } +static inline void call_tracepoint_unregister_atomic(struct rcu_head *rcu, rcu_callback_t func) +{ } +static inline void call_tracepoint_unregister_syscall(struct rcu_head *rcu, rcu_callback_t func) +{ } #endif #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 274039e36465..700938782bed 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3261,6 +3261,18 @@ static void bpf_link_defer_dealloc_rcu_gp(struct rcu_head *rcu) bpf_link_dealloc(link); } +static bool bpf_link_is_tracepoint(struct bpf_link *link) +{ + /* + * Only these combinations support a tracepoint bpf_link. + * BPF_LINK_TYPE_TRACING raw_tp progs are hardcoded to use + * bpf_raw_tp_link_lops and thus dealloc_deferred(), see + * bpf_raw_tp_link_attach(). + */ + return link->type == BPF_LINK_TYPE_RAW_TRACEPOINT || + (link->type == BPF_LINK_TYPE_TRACING && link->attach_type == BPF_TRACE_RAW_TP); +} + static void bpf_link_defer_dealloc_mult_rcu_gp(struct rcu_head *rcu) { if (rcu_trace_implies_rcu_gp()) @@ -3279,16 +3291,25 @@ static void bpf_link_free(struct bpf_link *link) if (link->prog) ops->release(link); if (ops->dealloc_deferred) { - /* Schedule BPF link deallocation, which will only then + /* + * Schedule BPF link deallocation, which will only then * trigger putting BPF program refcount. * If underlying BPF program is sleepable or BPF link's target * attach hookpoint is sleepable or otherwise requires RCU GPs * to ensure link and its underlying BPF program is not * reachable anymore, we need to first wait for RCU tasks - * trace sync, and then go through "classic" RCU grace period + * trace sync, and then go through "classic" RCU grace period. + * + * For tracepoint BPF links, we need to go through SRCU grace + * period wait instead when non-faultable tracepoint is used. We + * don't need to chain SRCU grace period waits, however, for the + * faultable case, since it exclusively uses RCU Tasks Trace. */ if (link->sleepable || (link->prog && link->prog->sleepable)) call_rcu_tasks_trace(&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp); + /* We need to do a SRCU grace period wait for non-faultable tracepoint BPF links. */ + else if (bpf_link_is_tracepoint(link)) + call_tracepoint_unregister_atomic(&link->rcu, bpf_link_defer_dealloc_rcu_gp); else call_rcu(&link->rcu, bpf_link_defer_dealloc_rcu_gp); } else if (ops->dealloc) { From 78ec5bf2f589ec7fd8f169394bfeca541b077317 Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Mon, 30 Mar 2026 13:11:27 -0700 Subject: [PATCH 576/712] fs/smb/client: fix out-of-bounds read in cifs_sanitize_prepath When cifs_sanitize_prepath is called with an empty string or a string containing only delimiters (e.g., "/"), the current logic attempts to check *(cursor2 - 1) before cursor2 has advanced. This results in an out-of-bounds read. This patch adds an early exit check after stripping prepended delimiters. If no path content remains, the function returns NULL. The bug was identified via manual audit and verified using a standalone test case compiled with AddressSanitizer, which triggered a SEGV on affected inputs. Signed-off-by: Fredric Cover Reviewed-by: Henrique Carvalho <[2]henrique.carvalho@suse.com> Signed-off-by: Steve French --- fs/smb/client/fs_context.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index a4a7c7eee038..a46764c24710 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -588,6 +588,10 @@ char *cifs_sanitize_prepath(char *prepath, gfp_t gfp) while (IS_DELIM(*cursor1)) cursor1++; + /* exit in case of only delimiters */ + if (!*cursor1) + return NULL; + /* copy the first letter */ *cursor2 = *cursor1; From 5dd8025a49c268ab6b94d978532af3ad341132a7 Mon Sep 17 00:00:00 2001 From: Li Xiasong Date: Mon, 30 Mar 2026 20:03:35 +0800 Subject: [PATCH 577/712] mptcp: fix soft lockup in mptcp_recvmsg() syzbot reported a soft lockup in mptcp_recvmsg() [0]. When receiving data with MSG_PEEK | MSG_WAITALL flags, the skb is not removed from the sk_receive_queue. This causes sk_wait_data() to always find available data and never perform actual waiting, leading to a soft lockup. Fix this by adding a 'last' parameter to track the last peeked skb. This allows sk_wait_data() to make informed waiting decisions and prevent infinite loops when MSG_PEEK is used. [0]: watchdog: BUG: soft lockup - CPU#2 stuck for 156s! [server:1963] Modules linked in: CPU: 2 UID: 0 PID: 1963 Comm: server Not tainted 6.19.0-rc8 #61 PREEMPT(none) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:sk_wait_data+0x15/0x190 Code: 80 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 41 56 41 55 41 54 49 89 f4 55 48 89 d5 53 48 89 fb <48> 83 ec 30 65 48 8b 05 17 a4 6b 01 48 89 44 24 28 31 c0 65 48 8b RSP: 0018:ffffc90000603ca0 EFLAGS: 00000246 RAX: 0000000000000000 RBX: ffff888102bf0800 RCX: 0000000000000001 RDX: 0000000000000000 RSI: ffffc90000603d18 RDI: ffff888102bf0800 RBP: 0000000000000000 R08: 0000000000000002 R09: 0000000000000101 R10: 0000000000000000 R11: 0000000000000075 R12: ffffc90000603d18 R13: ffff888102bf0800 R14: ffff888102bf0800 R15: 0000000000000000 FS: 00007f6e38b8c4c0(0000) GS:ffff8881b877e000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000055aa7bff1680 CR3: 0000000105cbe000 CR4: 00000000000006f0 Call Trace: mptcp_recvmsg+0x547/0x8c0 net/mptcp/protocol.c:2329 inet_recvmsg+0x11f/0x130 net/ipv4/af_inet.c:891 sock_recvmsg+0x94/0xc0 net/socket.c:1100 __sys_recvfrom+0xb2/0x130 net/socket.c:2256 __x64_sys_recvfrom+0x1f/0x30 net/socket.c:2267 do_syscall_64+0x59/0x2d0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x76/0x7e arch/x86/entry/entry_64.S:131 RIP: 0033:0x7f6e386a4a1d Code: 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 8d 05 f1 de 2c 00 41 89 ca 8b 00 85 c0 75 20 45 31 c9 45 31 c0 b8 2d 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 6b f3 c3 66 0f 1f 84 00 00 00 00 00 41 56 41 RSP: 002b:00007ffc3c4bb078 EFLAGS: 00000246 ORIG_RAX: 000000000000002d RAX: ffffffffffffffda RBX: 000000000000861e RCX: 00007f6e386a4a1d RDX: 00000000000003ff RSI: 00007ffc3c4bb150 RDI: 0000000000000004 RBP: 00007ffc3c4bb570 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000103 R11: 0000000000000246 R12: 00005605dbc00be0 R13: 00007ffc3c4bb650 R14: 0000000000000000 R15: 0000000000000000 Fixes: 8e04ce45a8db ("mptcp: fix MSG_PEEK stream corruption") Signed-off-by: Li Xiasong Reviewed-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260330120335.659027-1-lixiasong1@huawei.com Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index cf1852b99963..65c3bb8016f4 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2006,7 +2006,7 @@ static void mptcp_eat_recv_skb(struct sock *sk, struct sk_buff *skb) static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg, size_t len, int flags, int copied_total, struct scm_timestamping_internal *tss, - int *cmsg_flags) + int *cmsg_flags, struct sk_buff **last) { struct mptcp_sock *msk = mptcp_sk(sk); struct sk_buff *skb, *tmp; @@ -2023,6 +2023,7 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg, /* skip already peeked skbs */ if (total_data_len + data_len <= copied_total) { total_data_len += data_len; + *last = skb; continue; } @@ -2058,6 +2059,8 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg, } mptcp_eat_recv_skb(sk, skb); + } else { + *last = skb; } if (copied >= len) @@ -2288,10 +2291,12 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, cmsg_flags = MPTCP_CMSG_INQ; while (copied < len) { + struct sk_buff *last = NULL; int err, bytes_read; bytes_read = __mptcp_recvmsg_mskq(sk, msg, len - copied, flags, - copied, &tss, &cmsg_flags); + copied, &tss, &cmsg_flags, + &last); if (unlikely(bytes_read < 0)) { if (!copied) copied = bytes_read; @@ -2343,7 +2348,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, pr_debug("block timeout %ld\n", timeo); mptcp_cleanup_rbuf(msk, copied); - err = sk_wait_data(sk, &timeo, NULL); + err = sk_wait_data(sk, &timeo, last); if (err < 0) { err = copied ? : err; goto out_err; From c0fd0fe745f5e8c568d898cd1513d0083e46204a Mon Sep 17 00:00:00 2001 From: Yufan Chen Date: Sun, 29 Mar 2026 00:32:57 +0800 Subject: [PATCH 578/712] net: ftgmac100: fix ring allocation unwind on open failure ftgmac100_alloc_rings() allocates rx_skbs, tx_skbs, rxdes, txdes, and rx_scratch in stages. On intermediate failures it returned -ENOMEM directly, leaking resources allocated earlier in the function. Rework the failure path to use staged local unwind labels and free allocated resources in reverse order before returning -ENOMEM. This matches common netdev allocation cleanup style. Fixes: d72e01a0430f ("ftgmac100: Use a scratch buffer for failed RX allocations") Cc: stable@vger.kernel.org Signed-off-by: Yufan Chen Link: https://patch.msgid.link/20260328163257.60836-1-yufan.chen@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/faraday/ftgmac100.c | 28 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 1e91e79c8134..6d2fe5c2f390 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -977,19 +977,19 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *), GFP_KERNEL); if (!priv->tx_skbs) - return -ENOMEM; + goto err_free_rx_skbs; /* Allocate descriptors */ priv->rxdes = dma_alloc_coherent(priv->dev, MAX_RX_QUEUE_ENTRIES * sizeof(struct ftgmac100_rxdes), &priv->rxdes_dma, GFP_KERNEL); if (!priv->rxdes) - return -ENOMEM; + goto err_free_tx_skbs; priv->txdes = dma_alloc_coherent(priv->dev, MAX_TX_QUEUE_ENTRIES * sizeof(struct ftgmac100_txdes), &priv->txdes_dma, GFP_KERNEL); if (!priv->txdes) - return -ENOMEM; + goto err_free_rxdes; /* Allocate scratch packet buffer */ priv->rx_scratch = dma_alloc_coherent(priv->dev, @@ -997,9 +997,29 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) &priv->rx_scratch_dma, GFP_KERNEL); if (!priv->rx_scratch) - return -ENOMEM; + goto err_free_txdes; return 0; + +err_free_txdes: + dma_free_coherent(priv->dev, + MAX_TX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_txdes), + priv->txdes, priv->txdes_dma); + priv->txdes = NULL; +err_free_rxdes: + dma_free_coherent(priv->dev, + MAX_RX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_rxdes), + priv->rxdes, priv->rxdes_dma); + priv->rxdes = NULL; +err_free_tx_skbs: + kfree(priv->tx_skbs); + priv->tx_skbs = NULL; +err_free_rx_skbs: + kfree(priv->rx_skbs); + priv->rx_skbs = NULL; + return -ENOMEM; } static void ftgmac100_init_rings(struct ftgmac100 *priv) From 48b3cd69265f346f64b93064723492da46206e9b Mon Sep 17 00:00:00 2001 From: Michal Piekos Date: Sat, 28 Mar 2026 09:55:51 +0100 Subject: [PATCH 579/712] net: stmmac: skip VLAN restore when VLAN hash ops are missing stmmac_vlan_restore() unconditionally calls stmmac_vlan_update() when NETIF_F_VLAN_FEATURES is set. On platforms where priv->hw->vlan (or ->update_vlan_hash) is not provided, stmmac_update_vlan_hash() returns -EINVAL via stmmac_do_void_callback(), resulting in a spurious "Failed to restore VLANs" error even when no VLAN filtering is in use. Remove not needed comment. Remove not used return value from stmmac_vlan_restore(). Tested on Orange Pi Zero 3. Fixes: bd7ad51253a7 ("net: stmmac: Fix VLAN HW state restore") Signed-off-by: Michal Piekos Link: https://patch.msgid.link/20260328-vlan-restore-error-v4-1-f88624c530dc@mmpsystems.pl Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 6827c99bde8c..13d3cac056be 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -156,7 +156,7 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue); static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue); static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode, u32 rxmode, u32 chan); -static int stmmac_vlan_restore(struct stmmac_priv *priv); +static void stmmac_vlan_restore(struct stmmac_priv *priv); #ifdef CONFIG_DEBUG_FS static const struct net_device_ops stmmac_netdev_ops; @@ -6859,21 +6859,15 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi return ret; } -static int stmmac_vlan_restore(struct stmmac_priv *priv) +static void stmmac_vlan_restore(struct stmmac_priv *priv) { - int ret; - if (!(priv->dev->features & NETIF_F_VLAN_FEATURES)) - return 0; + return; if (priv->hw->num_vlan) stmmac_restore_hw_vlan_rx_fltr(priv, priv->dev, priv->hw); - ret = stmmac_vlan_update(priv, priv->num_double_vlans); - if (ret) - netdev_err(priv->dev, "Failed to restore VLANs\n"); - - return ret; + stmmac_vlan_update(priv, priv->num_double_vlans); } static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf) From 8c0e0b4628e5fd98bc614378f1aff4c1c8c26310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Tue, 31 Mar 2026 11:20:20 +0200 Subject: [PATCH 580/712] drm/msm: Remove abuse of drm_exec internals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code was reading drm_exec internal state to determine whether the drm_exec structure had been initialized or not, and therefore needed cleaning up, relying on undocumented behaviour. Instead add a bool to struct msm_gem_submit to indicate whether drm_exec cleaning up is needed. Signed-off-by: Thomas Hellström Acked-by: Christian König Reviewed-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/715502/ Message-ID: <20260331092023.81616-3-thomas.hellstrom@linux.intel.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gem.h | 1 + drivers/gpu/drm/msm/msm_gem_submit.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 92ada1d69250..d8e45ded63ea 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -452,6 +452,7 @@ struct msm_gem_submit { bool bos_pinned : 1; bool fault_dumped:1;/* Limit devcoredump dumping to one per submit */ bool in_rb : 1; /* "sudo" mode, copy cmds into RB */ + bool has_exec : 1; /* @exec is initialized. */ struct msm_ringbuffer *ring; unsigned int nr_cmds; unsigned int nr_bos; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 75d9f3574370..26ea8a28be47 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -278,6 +278,7 @@ static int submit_lock_objects_vmbind(struct msm_gem_submit *submit) int ret = 0; drm_exec_init(&submit->exec, flags, submit->nr_bos); + submit->has_exec = true; drm_exec_until_all_locked (&submit->exec) { ret = drm_gpuvm_prepare_vm(submit->vm, exec, 1); @@ -304,6 +305,7 @@ static int submit_lock_objects(struct msm_gem_submit *submit) return submit_lock_objects_vmbind(submit); drm_exec_init(&submit->exec, flags, submit->nr_bos); + submit->has_exec = true; drm_exec_until_all_locked (&submit->exec) { ret = drm_exec_lock_obj(&submit->exec, @@ -523,7 +525,7 @@ static void submit_cleanup(struct msm_gem_submit *submit, bool error) if (error) submit_unpin_objects(submit); - if (submit->exec.objects) + if (submit->has_exec) drm_exec_fini(&submit->exec); /* if job wasn't enqueued to scheduler, early retirement: */ From a0dafdbd1049a8ea661a1a471be1b840bd8aed13 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Wed, 1 Apr 2026 16:01:27 +1030 Subject: [PATCH 581/712] ALSA: usb-audio: Exclude Scarlett 2i2 1st Gen (8016) from SKIP_IFACE_SETUP Same issue as the other 1st Gen Scarletts: QUIRK_FLAG_SKIP_IFACE_SETUP causes distorted audio on this revision of the Scarlett 2i2 1st Gen (1235:8016). Fixes: 38c322068a26 ("ALSA: usb-audio: Add QUIRK_FLAG_SKIP_IFACE_SETUP") Reported-by: lukas-reineke [https://github.com/geoffreybennett/linux-fcp/issues/54] Signed-off-by: Geoffrey D. Bennett Link: https://patch.msgid.link/acytr8aEUba4VXmZ@m.b4.vu Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 116da076a194..4cfa24c06fcd 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2435,6 +2435,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_VALIDATE_RATES), DEVICE_FLG(0x1235, 0x8006, 0), /* Focusrite Scarlett 2i2 1st Gen */ DEVICE_FLG(0x1235, 0x800a, 0), /* Focusrite Scarlett 2i4 1st Gen */ + DEVICE_FLG(0x1235, 0x8016, 0), /* Focusrite Scarlett 2i2 1st Gen */ DEVICE_FLG(0x1235, 0x801c, 0), /* Focusrite Scarlett Solo 1st Gen */ VENDOR_FLG(0x1235, /* Focusrite Novation */ QUIRK_FLAG_SKIP_CLOCK_SELECTOR | From 8c0ef7b56d6bbbc53f2d43d99c195144f01b0775 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 11 Mar 2026 22:14:00 -0700 Subject: [PATCH 582/712] lis3lv02d: fix kernel-doc warnings Use the correct kernel-doc format to avoid kernel-doc warnings: Warning: include/linux/lis3lv02d.h:125 struct member 'st_min_limits' not described in 'lis3lv02d_platform_data' Warning: include/linux/lis3lv02d.h:125 struct member 'st_max_limits' not described in 'lis3lv02d_platform_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260312051400.682991-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/lis3lv02d.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/lis3lv02d.h b/include/linux/lis3lv02d.h index b72b8cdba765..feb60ba4e30e 100644 --- a/include/linux/lis3lv02d.h +++ b/include/linux/lis3lv02d.h @@ -30,8 +30,8 @@ * @default_rate: Default sampling rate. 0 means reset default * @setup_resources: Interrupt line setup call back function * @release_resources: Interrupt line release call back function - * @st_min_limits[3]: Selftest acceptance minimum values - * @st_max_limits[3]: Selftest acceptance maximum values + * @st_min_limits: Selftest acceptance minimum values (x, y, z) + * @st_max_limits: Selftest acceptance maximum values (x, y, z) * @irq2: Irq line 2 number * * Platform data is used to setup the sensor chip. Meaning of the different From f40b1401b6ad0f4dadfca4e7a69744352a2e4f8f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 26 Mar 2026 19:04:36 +0100 Subject: [PATCH 583/712] lis3lv02d: Omit IRQF_ONESHOT if no threaded handler is provided The lis3lv02d started triggering a WARN in the IRQ code because it passes IRQF_ONESHOT to request_threaded_irq() even when thread_fn is NULL, which is an invalid combination. So set the flag only if thread_fn is non-NULL. Cc: Eric Piel Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Signed-off-by: Ard Biesheuvel Link: https://patch.msgid.link/20260326180436.14968-2-ardb@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lis3lv02d/lis3lv02d.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 9c68f8b1d5d6..21e8ad0a7444 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -1230,10 +1230,12 @@ int lis3lv02d_init_device(struct lis3lv02d *lis3) else thread_fn = NULL; + if (thread_fn) + irq_flags |= IRQF_ONESHOT; + err = request_threaded_irq(lis3->irq, lis302dl_interrupt, thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT | - irq_flags, + irq_flags | IRQF_TRIGGER_RISING, DRIVER_NAME, lis3); if (err < 0) { From e8d0ed37bd51da52da6225d278e330c2f18a6198 Mon Sep 17 00:00:00 2001 From: Ernestas Kulik Date: Tue, 24 Mar 2026 13:07:16 +0200 Subject: [PATCH 584/712] USB: serial: option: add MeiG Smart SRM825WN Add support for the SDX62-based MeiG Smart SRM825WN module. If#= 0: RNDIS If#= 1: RNDIS If#= 2: Diag If#= 3: AT If#= 4: AT If#= 5: NMEA T: Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 19 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2dee ProdID=4d38 Rev= 5.04 S: Manufacturer=MEIG S: Product=LTE-A Module S: SerialNumber=da47a175 C:* #Ifs= 6 Cfg#= 1 Atr=80 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=03 I:* If#= 0 Alt= 0 #EPs= 1 Cls=e0(wlcon) Sub=01 Prot=03 Driver=rndis_host E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Signed-off-by: Ernestas Kulik Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d222bf2c2539..313612114db9 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2441,6 +2441,9 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM815 and SRM825L */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825L */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825L */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */ { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ From 76522fcdbc3a02b568f5d957f7e66fc194abb893 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 26 Mar 2026 00:17:09 +0100 Subject: [PATCH 585/712] netfilter: flowtable: strictly check for maximum number of actions The maximum number of flowtable hardware offload actions in IPv6 is: * ethernet mangling (4 payload actions, 2 for each ethernet address) * SNAT (4 payload actions) * DNAT (4 payload actions) * Double VLAN (4 vlan actions, 2 for popping vlan, and 2 for pushing) for QinQ. * Redirect (1 action) Which makes 17, while the maximum is 16. But act_ct supports for tunnels actions too. Note that payload action operates at 32-bit word level, so mangling an IPv6 address takes 4 payload actions. Update flow_action_entry_next() calls to check for the maximum number of supported actions. While at it, rise the maximum number of actions per flow from 16 to 24 so this works fine with IPv6 setups. Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support") Reported-by: Hyunwoo Kim Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 196 +++++++++++++++++--------- 1 file changed, 130 insertions(+), 66 deletions(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 9b677e116487..93d0aa7f8fcc 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -14,6 +14,8 @@ #include #include +#define NF_FLOW_RULE_ACTION_MAX 24 + static struct workqueue_struct *nf_flow_offload_add_wq; static struct workqueue_struct *nf_flow_offload_del_wq; static struct workqueue_struct *nf_flow_offload_stats_wq; @@ -216,7 +218,12 @@ static void flow_offload_mangle(struct flow_action_entry *entry, static inline struct flow_action_entry * flow_action_entry_next(struct nf_flow_rule *flow_rule) { - int i = flow_rule->rule->action.num_entries++; + int i; + + if (unlikely(flow_rule->rule->action.num_entries >= NF_FLOW_RULE_ACTION_MAX)) + return NULL; + + i = flow_rule->rule->action.num_entries++; return &flow_rule->rule->action.entries[i]; } @@ -234,6 +241,9 @@ static int flow_offload_eth_src(struct net *net, u32 mask, val; u16 val16; + if (!entry0 || !entry1) + return -E2BIG; + this_tuple = &flow->tuplehash[dir].tuple; switch (this_tuple->xmit_type) { @@ -284,6 +294,9 @@ static int flow_offload_eth_dst(struct net *net, u8 nud_state; u16 val16; + if (!entry0 || !entry1) + return -E2BIG; + this_tuple = &flow->tuplehash[dir].tuple; switch (this_tuple->xmit_type) { @@ -325,16 +338,19 @@ static int flow_offload_eth_dst(struct net *net, return 0; } -static void flow_offload_ipv4_snat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv4_snat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask = ~htonl(0xffffffff); __be32 addr; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v4.s_addr; @@ -345,23 +361,27 @@ static void flow_offload_ipv4_snat(struct net *net, offset = offsetof(struct iphdr, daddr); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset, &addr, &mask); + return 0; } -static void flow_offload_ipv4_dnat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv4_dnat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask = ~htonl(0xffffffff); __be32 addr; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v4.s_addr; @@ -372,14 +392,15 @@ static void flow_offload_ipv4_dnat(struct net *net, offset = offsetof(struct iphdr, saddr); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset, &addr, &mask); + return 0; } -static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, +static int flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, unsigned int offset, const __be32 *addr, const __be32 *mask) { @@ -388,15 +409,20 @@ static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i++) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; + flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6, offset + i * sizeof(u32), &addr[i], mask); } + + return 0; } -static void flow_offload_ipv6_snat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv6_snat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { u32 mask = ~htonl(0xffffffff); const __be32 *addr; @@ -412,16 +438,16 @@ static void flow_offload_ipv6_snat(struct net *net, offset = offsetof(struct ipv6hdr, daddr); break; default: - return; + return -EOPNOTSUPP; } - flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); + return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); } -static void flow_offload_ipv6_dnat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv6_dnat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { u32 mask = ~htonl(0xffffffff); const __be32 *addr; @@ -437,10 +463,10 @@ static void flow_offload_ipv6_dnat(struct net *net, offset = offsetof(struct ipv6hdr, saddr); break; default: - return; + return -EOPNOTSUPP; } - flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); + return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); } static int flow_offload_l4proto(const struct flow_offload *flow) @@ -462,15 +488,18 @@ static int flow_offload_l4proto(const struct flow_offload *flow) return type; } -static void flow_offload_port_snat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_port_snat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask, port; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port); @@ -485,22 +514,26 @@ static void flow_offload_port_snat(struct net *net, mask = ~htonl(0xffff); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, &port, &mask); + return 0; } -static void flow_offload_port_dnat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_port_dnat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask, port; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_port); @@ -515,20 +548,24 @@ static void flow_offload_port_dnat(struct net *net, mask = ~htonl(0xffff0000); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, &port, &mask); + return 0; } -static void flow_offload_ipv4_checksum(struct net *net, - const struct flow_offload *flow, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv4_checksum(struct net *net, + const struct flow_offload *flow, + struct nf_flow_rule *flow_rule) { u8 protonum = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto; struct flow_action_entry *entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; + entry->id = FLOW_ACTION_CSUM; entry->csum_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR; @@ -540,12 +577,14 @@ static void flow_offload_ipv4_checksum(struct net *net, entry->csum_flags |= TCA_CSUM_UPDATE_FLAG_UDP; break; } + + return 0; } -static void flow_offload_redirect(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_redirect(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { const struct flow_offload_tuple *this_tuple, *other_tuple; struct flow_action_entry *entry; @@ -563,21 +602,28 @@ static void flow_offload_redirect(struct net *net, ifindex = other_tuple->iifidx; break; default: - return; + return -EOPNOTSUPP; } dev = dev_get_by_index(net, ifindex); if (!dev) - return; + return -ENODEV; entry = flow_action_entry_next(flow_rule); + if (!entry) { + dev_put(dev); + return -E2BIG; + } + entry->id = FLOW_ACTION_REDIRECT; entry->dev = dev; + + return 0; } -static void flow_offload_encap_tunnel(const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_encap_tunnel(const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { const struct flow_offload_tuple *this_tuple; struct flow_action_entry *entry; @@ -585,7 +631,7 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow, this_tuple = &flow->tuplehash[dir].tuple; if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) - return; + return 0; dst = this_tuple->dst_cache; if (dst && dst->lwtstate) { @@ -594,15 +640,19 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow, tun_info = lwt_tun_info(dst->lwtstate); if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; entry->id = FLOW_ACTION_TUNNEL_ENCAP; entry->tunnel = tun_info; } } + + return 0; } -static void flow_offload_decap_tunnel(const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_decap_tunnel(const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { const struct flow_offload_tuple *other_tuple; struct flow_action_entry *entry; @@ -610,7 +660,7 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow, other_tuple = &flow->tuplehash[!dir].tuple; if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) - return; + return 0; dst = other_tuple->dst_cache; if (dst && dst->lwtstate) { @@ -619,9 +669,13 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow, tun_info = lwt_tun_info(dst->lwtstate); if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; entry->id = FLOW_ACTION_TUNNEL_DECAP; } } + + return 0; } static int @@ -633,8 +687,9 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow, const struct flow_offload_tuple *tuple; int i; - flow_offload_decap_tunnel(flow, dir, flow_rule); - flow_offload_encap_tunnel(flow, dir, flow_rule); + if (flow_offload_decap_tunnel(flow, dir, flow_rule) < 0 || + flow_offload_encap_tunnel(flow, dir, flow_rule) < 0) + return -1; if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 || flow_offload_eth_dst(net, flow, dir, flow_rule) < 0) @@ -650,6 +705,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow, if (tuple->encap[i].proto == htons(ETH_P_8021Q)) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -1; entry->id = FLOW_ACTION_VLAN_POP; } } @@ -663,6 +720,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow, continue; entry = flow_action_entry_next(flow_rule); + if (!entry) + return -1; switch (other_tuple->encap[i].proto) { case htons(ETH_P_PPP_SES): @@ -688,18 +747,22 @@ int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow, return -1; if (test_bit(NF_FLOW_SNAT, &flow->flags)) { - flow_offload_ipv4_snat(net, flow, dir, flow_rule); - flow_offload_port_snat(net, flow, dir, flow_rule); + if (flow_offload_ipv4_snat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_snat(net, flow, dir, flow_rule) < 0) + return -1; } if (test_bit(NF_FLOW_DNAT, &flow->flags)) { - flow_offload_ipv4_dnat(net, flow, dir, flow_rule); - flow_offload_port_dnat(net, flow, dir, flow_rule); + if (flow_offload_ipv4_dnat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_dnat(net, flow, dir, flow_rule) < 0) + return -1; } if (test_bit(NF_FLOW_SNAT, &flow->flags) || test_bit(NF_FLOW_DNAT, &flow->flags)) - flow_offload_ipv4_checksum(net, flow, flow_rule); + if (flow_offload_ipv4_checksum(net, flow, flow_rule) < 0) + return -1; - flow_offload_redirect(net, flow, dir, flow_rule); + if (flow_offload_redirect(net, flow, dir, flow_rule) < 0) + return -1; return 0; } @@ -713,22 +776,23 @@ int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow, return -1; if (test_bit(NF_FLOW_SNAT, &flow->flags)) { - flow_offload_ipv6_snat(net, flow, dir, flow_rule); - flow_offload_port_snat(net, flow, dir, flow_rule); + if (flow_offload_ipv6_snat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_snat(net, flow, dir, flow_rule) < 0) + return -1; } if (test_bit(NF_FLOW_DNAT, &flow->flags)) { - flow_offload_ipv6_dnat(net, flow, dir, flow_rule); - flow_offload_port_dnat(net, flow, dir, flow_rule); + if (flow_offload_ipv6_dnat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_dnat(net, flow, dir, flow_rule) < 0) + return -1; } - flow_offload_redirect(net, flow, dir, flow_rule); + if (flow_offload_redirect(net, flow, dir, flow_rule) < 0) + return -1; return 0; } EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv6); -#define NF_FLOW_RULE_ACTION_MAX 16 - static struct nf_flow_rule * nf_flow_offload_rule_alloc(struct net *net, const struct flow_offload_work *offload, From 6d52a4a0520a6696bdde51caa11f2d6821cd0c01 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 26 Mar 2026 16:17:24 +0100 Subject: [PATCH 586/712] netfilter: nfnetlink_log: account for netlink header size This is a followup to an old bug fix: NLMSG_DONE needs to account for the netlink header size, not just the attribute size. This can result in a WARN splat + drop of the netlink message, but other than this there are no ill effects. Fixes: 9dfa1dfe4d5e ("netfilter: nf_log: account for size of NLMSG_DONE attribute") Reported-by: Yiming Qian Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index fcbe54940b2e..f80978c06fa0 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -726,7 +726,7 @@ nfulnl_log_packet(struct net *net, + nla_total_size(plen) /* prefix */ + nla_total_size(sizeof(struct nfulnl_msg_packet_hw)) + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp)) - + nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */ + + nlmsg_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */ if (in && skb_mac_header_was_set(skb)) { size += nla_total_size(skb->dev->hard_header_len) From a958a4f90ddd7de0800b33ca9d7b886b7d40f74e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 31 Mar 2026 23:13:36 +0200 Subject: [PATCH 587/712] netfilter: x_tables: ensure names are nul-terminated Reject names that lack a \0 character before feeding them to functions that expect c-strings. Fixes tag is the most recent commit that needs this change. Fixes: c38c4597e4bf ("netfilter: implement xt_cgroup cgroup2 path match") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_cgroup.c | 6 ++++++ net/netfilter/xt_rateest.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c index c437fbd59ec1..43d2ae2be628 100644 --- a/net/netfilter/xt_cgroup.c +++ b/net/netfilter/xt_cgroup.c @@ -65,6 +65,9 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par) info->priv = NULL; if (info->has_path) { + if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path)) + return -ENAMETOOLONG; + cgrp = cgroup_get_from_path(info->path); if (IS_ERR(cgrp)) { pr_info_ratelimited("invalid path, errno=%ld\n", @@ -102,6 +105,9 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par) info->priv = NULL; if (info->has_path) { + if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path)) + return -ENAMETOOLONG; + cgrp = cgroup_get_from_path(info->path); if (IS_ERR(cgrp)) { pr_info_ratelimited("invalid path, errno=%ld\n", diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 72324bd976af..b1d736c15fcb 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -91,6 +91,11 @@ static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) goto err1; } + if (strnlen(info->name1, sizeof(info->name1)) >= sizeof(info->name1)) + return -ENAMETOOLONG; + if (strnlen(info->name2, sizeof(info->name2)) >= sizeof(info->name2)) + return -ENAMETOOLONG; + ret = -ENOENT; est1 = xt_rateest_lookup(par->net, info->name1); if (!est1) From b7e8590987aa94c9dc51518fad0e58cb887b1db5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 30 Mar 2026 14:16:34 +0200 Subject: [PATCH 588/712] netfilter: ipset: use nla_strcmp for IPSET_ATTR_NAME attr IPSET_ATTR_NAME and IPSET_ATTR_NAMEREF are of NLA_STRING type, they cannot be treated like a c-string. They either have to be switched to NLA_NUL_STRING, or the compare operations need to use the nla functions. Fixes: f830837f0eed ("netfilter: ipset: list:set set type support") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 2 +- net/netfilter/ipset/ip_set_core.c | 4 ++-- net/netfilter/ipset/ip_set_list_set.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e9f4f845d760..b98331572ad2 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -309,7 +309,7 @@ enum { /* register and unregister set references */ extern ip_set_id_t ip_set_get_byname(struct net *net, - const char *name, struct ip_set **set); + const struct nlattr *name, struct ip_set **set); extern void ip_set_put_byindex(struct net *net, ip_set_id_t index); extern void ip_set_name_byindex(struct net *net, ip_set_id_t index, char *name); extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index); diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index a2fe711cb5e3..d0c9fe59c67d 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -821,7 +821,7 @@ EXPORT_SYMBOL_GPL(ip_set_del); * */ ip_set_id_t -ip_set_get_byname(struct net *net, const char *name, struct ip_set **set) +ip_set_get_byname(struct net *net, const struct nlattr *name, struct ip_set **set) { ip_set_id_t i, index = IPSET_INVALID_ID; struct ip_set *s; @@ -830,7 +830,7 @@ ip_set_get_byname(struct net *net, const char *name, struct ip_set **set) rcu_read_lock(); for (i = 0; i < inst->ip_set_max; i++) { s = rcu_dereference(inst->ip_set_list)[i]; - if (s && STRNCMP(s->name, name)) { + if (s && nla_strcmp(name, s->name) == 0) { __ip_set_get(s); index = i; *set = s; diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 98b91b34c314..1cef84f15e8c 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -367,7 +367,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], ret = ip_set_get_extensions(set, tb, &ext); if (ret) return ret; - e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s); + e.id = ip_set_get_byname(map->net, tb[IPSET_ATTR_NAME], &s); if (e.id == IPSET_INVALID_ID) return -IPSET_ERR_NAME; /* "Loop detection" */ @@ -389,7 +389,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_NAMEREF]) { e.refid = ip_set_get_byname(map->net, - nla_data(tb[IPSET_ATTR_NAMEREF]), + tb[IPSET_ATTR_NAMEREF], &s); if (e.refid == IPSET_INVALID_ID) { ret = -IPSET_ERR_NAMEREF; From a242a9ae58aa46ff7dae51ce64150a93957abe65 Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Mon, 30 Mar 2026 00:50:36 +0800 Subject: [PATCH 589/712] netfilter: nf_conntrack_helper: pass helper to expect cleanup nf_conntrack_helper_unregister() calls nf_ct_expect_iterate_destroy() to remove expectations belonging to the helper being unregistered. However, it passes NULL instead of the helper pointer as the data argument, so expect_iter_me() never matches any expectation and all of them survive the cleanup. After unregister returns, nfnl_cthelper_del() frees the helper object immediately. Subsequent expectation dumps or packet-driven init_conntrack() calls then dereference the freed exp->helper, causing a use-after-free. Pass the actual helper pointer so expectations referencing it are properly destroyed before the helper object is freed. BUG: KASAN: slab-use-after-free in string+0x38f/0x430 Read of size 1 at addr ffff888003b14d20 by task poc/103 Call Trace: string+0x38f/0x430 vsnprintf+0x3cc/0x1170 seq_printf+0x17a/0x240 exp_seq_show+0x2e5/0x560 seq_read_iter+0x419/0x1280 proc_reg_read+0x1ac/0x270 vfs_read+0x179/0x930 ksys_read+0xef/0x1c0 Freed by task 103: The buggy address is located 32 bytes inside of freed 192-byte region [ffff888003b14d00, ffff888003b14dc0) Fixes: ac7b84839003 ("netfilter: expect: add and use nf_ct_expect_iterate helpers") Signed-off-by: Qi Tang Reviewed-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 1b330ba6613b..a715304a53d8 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -415,7 +415,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) */ synchronize_rcu(); - nf_ct_expect_iterate_destroy(expect_iter_me, NULL); + nf_ct_expect_iterate_destroy(expect_iter_me, me); nf_ct_iterate_destroy(unhelp, me); /* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as From 35177c6877134a21315f37d57a5577846225623e Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Tue, 31 Mar 2026 14:17:12 +0800 Subject: [PATCH 590/712] netfilter: ctnetlink: zero expect NAT fields when CTA_EXPECT_NAT absent ctnetlink_alloc_expect() allocates expectations from a non-zeroing slab cache via nf_ct_expect_alloc(). When CTA_EXPECT_NAT is not present in the netlink message, saved_addr and saved_proto are never initialized. Stale data from a previous slab occupant can then be dumped to userspace by ctnetlink_exp_dump_expect(), which checks these fields to decide whether to emit CTA_EXPECT_NAT. The safe sibling nf_ct_expect_init(), used by the packet path, explicitly zeroes these fields. Zero saved_addr, saved_proto and dir in the else branch, guarded by IS_ENABLED(CONFIG_NF_NAT) since these fields only exist when NAT is enabled. Confirmed by priming the expect slab with NAT-bearing expectations, freeing them, creating a new expectation without CTA_EXPECT_NAT, and observing that the ctnetlink dump emits a spurious CTA_EXPECT_NAT containing stale data from the prior allocation. Fixes: 076a0ca02644 ("netfilter: ctnetlink: add NAT support for expectations") Reported-by: kernel test robot Signed-off-by: Qi Tang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 3f408f3713bb..38bd7124d9f7 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3588,6 +3588,12 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp, nf_ct_l3num(ct)); if (err < 0) goto err_out; +#if IS_ENABLED(CONFIG_NF_NAT) + } else { + memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); + memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); + exp->dir = 0; +#endif } return exp; err_out: From 917b61fa2042f11e2af4c428e43f08199586633a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 30 Mar 2026 11:26:22 +0200 Subject: [PATCH 591/712] netfilter: ctnetlink: ignore explicit helper on new expectations Use the existing master conntrack helper, anything else is not really supported and it just makes validation more complicated, so just ignore what helper userspace suggests for this expectation. This was uncovered when validating CTA_EXPECT_CLASS via different helper provided by userspace than the existing master conntrack helper: BUG: KASAN: slab-out-of-bounds in nf_ct_expect_related_report+0x2479/0x27c0 Read of size 4 at addr ffff8880043fe408 by task poc/102 Call Trace: nf_ct_expect_related_report+0x2479/0x27c0 ctnetlink_create_expect+0x22b/0x3b0 ctnetlink_new_expect+0x4bd/0x5c0 nfnetlink_rcv_msg+0x67a/0x950 netlink_rcv_skb+0x120/0x350 Allowing to read kernel memory bytes off the expectation boundary. CTA_EXPECT_HELP_NAME is still used to offer the helper name to userspace via netlink dump. Fixes: bd0779370588 ("netfilter: nfnetlink_queue: allow to attach expectations to conntracks") Reported-by: Qi Tang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 54 +++++----------------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 38bd7124d9f7..a20cd82446c5 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2636,7 +2636,6 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { static struct nf_conntrack_expect * ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, - struct nf_conntrack_helper *helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask); @@ -2865,7 +2864,6 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, { struct nlattr *cda[CTA_EXPECT_MAX+1]; struct nf_conntrack_tuple tuple, mask; - struct nf_conntrack_helper *helper = NULL; struct nf_conntrack_expect *exp; int err; @@ -2879,17 +2877,8 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, if (err < 0) return err; - if (cda[CTA_EXPECT_HELP_NAME]) { - const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); - - helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), - nf_ct_protonum(ct)); - if (helper == NULL) - return -EOPNOTSUPP; - } - exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, - helper, &tuple, &mask); + &tuple, &mask); if (IS_ERR(exp)) return PTR_ERR(exp); @@ -3528,11 +3517,11 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr, static struct nf_conntrack_expect * ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, - struct nf_conntrack_helper *helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask) { struct net *net = read_pnet(&ct->ct_net); + struct nf_conntrack_helper *helper; struct nf_conntrack_expect *exp; struct nf_conn_help *help; u32 class = 0; @@ -3542,7 +3531,11 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, if (!help) return ERR_PTR(-EOPNOTSUPP); - if (cda[CTA_EXPECT_CLASS] && helper) { + helper = rcu_dereference(help->helper); + if (!helper) + return ERR_PTR(-EOPNOTSUPP); + + if (cda[CTA_EXPECT_CLASS]) { class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS])); if (class > helper->expect_class_max) return ERR_PTR(-EINVAL); @@ -3576,8 +3569,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, #ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone; #endif - if (!helper) - helper = rcu_dereference(help->helper); rcu_assign_pointer(exp->helper, helper); exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; @@ -3609,7 +3600,6 @@ ctnetlink_create_expect(struct net *net, { struct nf_conntrack_tuple tuple, mask, master_tuple; struct nf_conntrack_tuple_hash *h = NULL; - struct nf_conntrack_helper *helper = NULL; struct nf_conntrack_expect *exp; struct nf_conn *ct; int err; @@ -3635,33 +3625,7 @@ ctnetlink_create_expect(struct net *net, ct = nf_ct_tuplehash_to_ctrack(h); rcu_read_lock(); - if (cda[CTA_EXPECT_HELP_NAME]) { - const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); - - helper = __nf_conntrack_helper_find(helpname, u3, - nf_ct_protonum(ct)); - if (helper == NULL) { - rcu_read_unlock(); -#ifdef CONFIG_MODULES - if (request_module("nfct-helper-%s", helpname) < 0) { - err = -EOPNOTSUPP; - goto err_ct; - } - rcu_read_lock(); - helper = __nf_conntrack_helper_find(helpname, u3, - nf_ct_protonum(ct)); - if (helper) { - err = -EAGAIN; - goto err_rcu; - } - rcu_read_unlock(); -#endif - err = -EOPNOTSUPP; - goto err_ct; - } - } - - exp = ctnetlink_alloc_expect(cda, ct, helper, &tuple, &mask); + exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask); if (IS_ERR(exp)) { err = PTR_ERR(exp); goto err_rcu; @@ -3671,8 +3635,8 @@ ctnetlink_create_expect(struct net *net, nf_ct_expect_put(exp); err_rcu: rcu_read_unlock(); -err_ct: nf_ct_put(ct); + return err; } From 9862ef9ab0a116c6dca98842aab7de13a252ae02 Mon Sep 17 00:00:00 2001 From: Yifan Wu Date: Mon, 30 Mar 2026 14:39:24 -0700 Subject: [PATCH 592/712] netfilter: ipset: drop logically empty buckets in mtype_del mtype_del() counts empty slots below n->pos in k, but it only drops the bucket when both n->pos and k are zero. This misses buckets whose live entries have all been removed while n->pos still points past deleted slots. Treat a bucket as empty when all positions below n->pos are unused and release it directly instead of shrinking it further. Fixes: 8af1c6fbd923 ("netfilter: ipset: Fix forceadd evaluation path") Cc: stable@vger.kernel.org Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Yifan Wu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Reviewed-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 181daa9c2019..b79e5dd2af03 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1098,7 +1098,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (!test_bit(i, n->used)) k++; } - if (n->pos == 0 && k == 0) { + if (k == n->pos) { t->hregion[r].ext_size -= ext_size(n->size, dsize); rcu_assign_pointer(hbucket(t, key), NULL); kfree_rcu(n, rcu); From 3d5d488f11776738deab9da336038add95d342d1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 31 Mar 2026 16:41:25 +0200 Subject: [PATCH 593/712] netfilter: x_tables: restrict xt_check_match/xt_check_target extensions for NFPROTO_ARP Weiming Shi says: xt_match and xt_target structs registered with NFPROTO_UNSPEC can be loaded by any protocol family through nft_compat. When such a match/target sets .hooks to restrict which hooks it may run on, the bitmask uses NF_INET_* constants. This is only correct for families whose hook layout matches NF_INET_*: IPv4, IPv6, INET, and bridge all share the same five hooks (PRE_ROUTING ... POST_ROUTING). ARP only has three hooks (IN=0, OUT=1, FORWARD=2) with different semantics. Because NF_ARP_OUT == 1 == NF_INET_LOCAL_IN, the .hooks validation silently passes for the wrong reasons, allowing matches to run on ARP chains where the hook assumptions (e.g. state->in being set on input hooks) do not hold. This leads to NULL pointer dereferences; xt_devgroup is one concrete example: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000044: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000220-0x0000000000000227] RIP: 0010:devgroup_mt+0xff/0x350 Call Trace: nft_match_eval (net/netfilter/nft_compat.c:407) nft_do_chain (net/netfilter/nf_tables_core.c:285) nft_do_chain_arp (net/netfilter/nft_chain_filter.c:61) nf_hook_slow (net/netfilter/core.c:623) arp_xmit (net/ipv4/arp.c:666) Kernel panic - not syncing: Fatal exception in interrupt Fix it by restricting arptables to NFPROTO_ARP extensions only. Note that arptables-legacy only supports: - arpt_CLASSIFY - arpt_mangle - arpt_MARK that provide explicit NFPROTO_ARP match/target declarations. Fixes: 9291747f118d ("netfilter: xtables: add device group match") Reported-by: Xiang Mei Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/x_tables.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index e594b3b7ad82..b39017c80548 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -501,6 +501,17 @@ int xt_check_match(struct xt_mtchk_param *par, par->match->table, par->table); return -EINVAL; } + + /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with + * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP + * support. + */ + if (par->family == NFPROTO_ARP && + par->match->family != NFPROTO_ARP) { + pr_info_ratelimited("%s_tables: %s match: not valid for this family\n", + xt_prefix[par->family], par->match->name); + return -EINVAL; + } if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) { char used[64], allow[64]; @@ -1016,6 +1027,18 @@ int xt_check_target(struct xt_tgchk_param *par, par->target->table, par->table); return -EINVAL; } + + /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with + * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP + * support. + */ + if (par->family == NFPROTO_ARP && + par->target->family != NFPROTO_ARP) { + pr_info_ratelimited("%s_tables: %s target: not valid for this family\n", + xt_prefix[par->family], par->target->name); + return -EINVAL; + } + if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) { char used[64], allow[64]; From da107398cbd4bbdb6bffecb2ce86d5c9384f4cec Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 31 Mar 2026 23:08:02 +0200 Subject: [PATCH 594/712] netfilter: nf_tables: reject immediate NF_QUEUE verdict nft_queue is always used from userspace nftables to deliver the NF_QUEUE verdict. Immediately emitting an NF_QUEUE verdict is never used by the userspace nft tools, so reject immediate NF_QUEUE verdicts. The arp family does not provide queue support, but such an immediate verdict is still reachable. Globally reject NF_QUEUE immediate verdicts to address this issue. Fixes: f342de4e2f33 ("netfilter: nf_tables: reject QUEUE/DROP verdict parameters") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 3922cff1bb3d..8c42247a176c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -11667,8 +11667,6 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, switch (data->verdict.code) { case NF_ACCEPT: case NF_DROP: - case NF_QUEUE: - break; case NFT_CONTINUE: case NFT_BREAK: case NFT_RETURN: @@ -11703,6 +11701,11 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, data->verdict.chain = chain; break; + case NF_QUEUE: + /* The nft_queue expression is used for this purpose, an + * immediate NF_QUEUE verdict should not ever be seen here. + */ + fallthrough; default: return -EINVAL; } From e74c38ef6f170179c0029b5744d6a14dfd543108 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Tue, 31 Mar 2026 13:19:16 +0000 Subject: [PATCH 595/712] ASoC: amd: ps: Fix missing leading zeros in subsystem_device SSID log Ensure that subsystem_device is printed with leading zeros when combined with subsystem_vendor to form the SSID. Without this, devices with upper bits unset may appear to have an incorrect SSID in the debug output. Signed-off-by: Simon Trimmer Link: https://patch.msgid.link/20260331131916.145546-1-simont@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/pci-ps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 3a20cc10d61f..9751cf0784a6 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -339,7 +339,7 @@ static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev) mach->mach_params.subsystem_device = acp_data->subsystem_device; mach->mach_params.subsystem_id_set = true; - dev_dbg(dev, "SSID %x%x\n", mach->mach_params.subsystem_vendor, + dev_dbg(dev, "SSID %x%04x\n", mach->mach_params.subsystem_vendor, mach->mach_params.subsystem_device); return mach; } From 6dcf9d0064ce2f3e3dfe5755f98b93abe6a98e1e Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 1 Apr 2026 10:45:35 +0800 Subject: [PATCH 596/712] cpufreq: governor: fix double free in cpufreq_dbs_governor_init() error path When kobject_init_and_add() fails, cpufreq_dbs_governor_init() calls kobject_put(&dbs_data->attr_set.kobj). The kobject release callback cpufreq_dbs_data_release() calls gov->exit(dbs_data) and kfree(dbs_data), but the current error path then calls gov->exit(dbs_data) and kfree(dbs_data) again, causing a double free. Keep the direct kfree(dbs_data) for the gov->init() failure path, but after kobject_init_and_add() has been called, let kobject_put() handle the cleanup through cpufreq_dbs_data_release(). Fixes: 4ebe36c94aed ("cpufreq: Fix kobject memleak") Signed-off-by: Guangshuo Li Reviewed-by: Zhongqiu Han Acked-by: Viresh Kumar Cc: All applicable Link: https://patch.msgid.link/20260401024535.1395801-1-lgs201920130244@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_governor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index acf101878733..86f35e451914 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -468,13 +468,13 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy) /* Failure, so roll back. */ pr_err("initialization failed (dbs_data kobject init error %d)\n", ret); - kobject_put(&dbs_data->attr_set.kobj); - policy->governor_data = NULL; if (!have_governor_per_policy()) gov->gdbs_data = NULL; - gov->exit(dbs_data); + + kobject_put(&dbs_data->attr_set.kobj); + goto free_policy_dbs_info; free_dbs_data: kfree(dbs_data); From 54ac9ff8f1196afc49d644a1625e0af1c9fcf7f5 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 31 Mar 2026 13:04:23 +0200 Subject: [PATCH 597/712] arm64: Use static call trampolines when kCFI is enabled Implement arm64 support for the 'unoptimized' static call variety, which routes all calls through a trampoline that performs a tail call to the chosen function, and wire it up for use when kCFI is enabled. This works around an issue with kCFI and generic static calls, where the prototypes of default handlers such as __static_call_nop() and __static_call_ret0() don't match the expected prototype of the call site, resulting in kCFI false positives [0]. Since static call targets may be located in modules loaded out of direct branching range, this needs an ADRP/LDR pair to load the branch target into R16 and a branch-to-register (BR) instruction to perform an indirect call. Unlike on x86, there is no pressing need on arm64 to avoid indirect calls at all cost, but hiding it from the compiler as is done here does have some benefits: - the literal is located in .rodata, which gives us the same robustness advantage that code patching does; - no D-cache pollution from fetching hash values from .text sections. From an execution speed PoV, this is unlikely to make any difference at all. Cc: Sami Tolvanen Cc: Sean Christopherson Cc: Kees Cook Cc: Peter Zijlstra Cc: Will McVicker Reported-by: Carlos Llamas Closes: https://lore.kernel.org/all/20260311225822.1565895-1-cmllamas@google.com/ [0] Signed-off-by: Ard Biesheuvel Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/static_call.h | 31 ++++++++++++++++++++++++++++ arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/static_call.c | 23 +++++++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 1 + 5 files changed, 57 insertions(+) create mode 100644 arch/arm64/include/asm/static_call.h create mode 100644 arch/arm64/kernel/static_call.c diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 38dba5f7e4d2..9ea19b74b6c3 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -252,6 +252,7 @@ config ARM64 select HAVE_RSEQ select HAVE_RUST if RUSTC_SUPPORTS_ARM64 select HAVE_STACKPROTECTOR + select HAVE_STATIC_CALL if CFI select HAVE_SYSCALL_TRACEPOINTS select HAVE_KPROBES select HAVE_KRETPROBES diff --git a/arch/arm64/include/asm/static_call.h b/arch/arm64/include/asm/static_call.h new file mode 100644 index 000000000000..30f6a046de4f --- /dev/null +++ b/arch/arm64/include/asm/static_call.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_STATIC_CALL_H +#define _ASM_STATIC_CALL_H + +#define __ARCH_DEFINE_STATIC_CALL_TRAMP(name, target) \ + asm(" .pushsection .static_call.text, \"ax\" \n" \ + " .align 4 \n" \ + " .globl " name " \n" \ + name ": \n" \ + " hint 34 /* BTI C */ \n" \ + " adrp x16, 1f \n" \ + " ldr x16, [x16, :lo12:1f] \n" \ + " br x16 \n" \ + " .type " name ", %function \n" \ + " .size " name ", . - " name " \n" \ + " .popsection \n" \ + " .pushsection .rodata, \"a\" \n" \ + " .align 3 \n" \ + "1: .quad " target " \n" \ + " .popsection \n") + +#define ARCH_DEFINE_STATIC_CALL_TRAMP(name, func) \ + __ARCH_DEFINE_STATIC_CALL_TRAMP(STATIC_CALL_TRAMP_STR(name), #func) + +#define ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name) \ + ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0) + +#define ARCH_DEFINE_STATIC_CALL_RET0_TRAMP(name) \ + ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0) + +#endif /* _ASM_STATIC_CALL_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 76f32e424065..fe627100d199 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_MODULES) += module.o module-plts.o obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o obj-$(CONFIG_HARDLOCKUP_DETECTOR_PERF) += watchdog_hld.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o +obj-$(CONFIG_HAVE_STATIC_CALL) += static_call.o obj-$(CONFIG_CPU_PM) += sleep.o suspend.o obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_EFI) += efi.o efi-rt-wrapper.o diff --git a/arch/arm64/kernel/static_call.c b/arch/arm64/kernel/static_call.c new file mode 100644 index 000000000000..8b3a19e10871 --- /dev/null +++ b/arch/arm64/kernel/static_call.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +void arch_static_call_transform(void *site, void *tramp, void *func, bool tail) +{ + u64 literal; + int ret; + + if (!func) + func = __static_call_return0; + + /* decode the instructions to discover the literal address */ + literal = ALIGN_DOWN((u64)tramp + 4, SZ_4K) + + aarch64_insn_adrp_get_offset(le32_to_cpup(tramp + 4)) + + 8 * aarch64_insn_decode_immediate(AARCH64_INSN_IMM_12, + le32_to_cpup(tramp + 8)); + + ret = aarch64_insn_write_literal_u64((void *)literal, (u64)func); + WARN_ON_ONCE(ret); +} +EXPORT_SYMBOL_GPL(arch_static_call_transform); diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index ad6133b89e7a..af9e81f3818a 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -191,6 +191,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT HYPERVISOR_TEXT + STATIC_CALL_TEXT *(.gnu.warning) } From 61a11cf4812726aceaee17c96432e1c08f6ed6cb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 31 Mar 2026 07:07:47 -0600 Subject: [PATCH 598/712] io_uring: protect remaining lockless ctx->rings accesses with RCU Commit 96189080265e addressed one case of ctx->rings being potentially accessed while a resize is happening on the ring, but there are still a few others that need handling. Add a helper for retrieving the rings associated with an io_uring context, and add some sanity checking to that to catch bad uses. ->rings_rcu is always valid, as long as it's used within RCU read lock. Any use of ->rings_rcu or ->rings inside either ->uring_lock or ->completion_lock is sane as well. Do the minimum fix for the current kernel, but set it up such that this basic infra can be extended for later kernels to make this harder to mess up in the future. Thanks to Junxi Qian for finding and debugging this issue. Cc: stable@vger.kernel.org Fixes: 79cfe9e59c2a ("io_uring/register: add IORING_REGISTER_RESIZE_RINGS") Reviewed-by: Junxi Qian Tested-by: Junxi Qian Link: https://lore.kernel.org/io-uring/20260330172348.89416-1-qjx1298677004@gmail.com/ Signed-off-by: Jens Axboe --- io_uring/io_uring.c | 7 +++++-- io_uring/io_uring.h | 34 +++++++++++++++++++++++++----- io_uring/wait.c | 50 ++++++++++++++++++++++++++++----------------- io_uring/wait.h | 7 +++++-- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 20ec8fdafcae..48f2f627319d 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -2015,7 +2015,7 @@ int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr) if (ctx->flags & IORING_SETUP_SQ_REWIND) entries = ctx->sq_entries; else - entries = io_sqring_entries(ctx); + entries = __io_sqring_entries(ctx); entries = min(nr, entries); if (unlikely(!entries)) @@ -2250,7 +2250,9 @@ static __poll_t io_uring_poll(struct file *file, poll_table *wait) */ poll_wait(file, &ctx->poll_wq, wait); - if (!io_sqring_full(ctx)) + rcu_read_lock(); + + if (!__io_sqring_full(ctx)) mask |= EPOLLOUT | EPOLLWRNORM; /* @@ -2270,6 +2272,7 @@ static __poll_t io_uring_poll(struct file *file, poll_table *wait) if (__io_cqring_events_user(ctx) || io_has_work(ctx)) mask |= EPOLLIN | EPOLLRDNORM; + rcu_read_unlock(); return mask; } diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h index 0fa844faf287..ee24bc5d77b3 100644 --- a/io_uring/io_uring.h +++ b/io_uring/io_uring.h @@ -142,16 +142,28 @@ struct io_wait_queue { #endif }; +static inline struct io_rings *io_get_rings(struct io_ring_ctx *ctx) +{ + return rcu_dereference_check(ctx->rings_rcu, + lockdep_is_held(&ctx->uring_lock) || + lockdep_is_held(&ctx->completion_lock)); +} + static inline bool io_should_wake(struct io_wait_queue *iowq) { struct io_ring_ctx *ctx = iowq->ctx; - int dist = READ_ONCE(ctx->rings->cq.tail) - (int) iowq->cq_tail; + struct io_rings *rings; + int dist; + + guard(rcu)(); + rings = io_get_rings(ctx); /* * Wake up if we have enough events, or if a timeout occurred since we * started waiting. For timeouts, we always want to return to userspace, * regardless of event count. */ + dist = READ_ONCE(rings->cq.tail) - (int) iowq->cq_tail; return dist >= 0 || atomic_read(&ctx->cq_timeouts) != iowq->nr_timeouts; } @@ -431,9 +443,9 @@ static inline void io_cqring_wake(struct io_ring_ctx *ctx) __io_wq_wake(&ctx->cq_wait); } -static inline bool io_sqring_full(struct io_ring_ctx *ctx) +static inline bool __io_sqring_full(struct io_ring_ctx *ctx) { - struct io_rings *r = ctx->rings; + struct io_rings *r = io_get_rings(ctx); /* * SQPOLL must use the actual sqring head, as using the cached_sq_head @@ -445,9 +457,15 @@ static inline bool io_sqring_full(struct io_ring_ctx *ctx) return READ_ONCE(r->sq.tail) - READ_ONCE(r->sq.head) == ctx->sq_entries; } -static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx) +static inline bool io_sqring_full(struct io_ring_ctx *ctx) { - struct io_rings *rings = ctx->rings; + guard(rcu)(); + return __io_sqring_full(ctx); +} + +static inline unsigned int __io_sqring_entries(struct io_ring_ctx *ctx) +{ + struct io_rings *rings = io_get_rings(ctx); unsigned int entries; /* make sure SQ entry isn't read before tail */ @@ -455,6 +473,12 @@ static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx) return min(entries, ctx->sq_entries); } +static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx) +{ + guard(rcu)(); + return __io_sqring_entries(ctx); +} + /* * Don't complete immediately but use deferred completion infrastructure. * Protected by ->uring_lock and can only be used either with diff --git a/io_uring/wait.c b/io_uring/wait.c index 0581cadf20ee..91df86ce0d18 100644 --- a/io_uring/wait.c +++ b/io_uring/wait.c @@ -79,12 +79,15 @@ static enum hrtimer_restart io_cqring_min_timer_wakeup(struct hrtimer *timer) if (io_has_work(ctx)) goto out_wake; /* got events since we started waiting, min timeout is done */ - if (iowq->cq_min_tail != READ_ONCE(ctx->rings->cq.tail)) - goto out_wake; - /* if we have any events and min timeout expired, we're done */ - if (io_cqring_events(ctx)) - goto out_wake; + scoped_guard(rcu) { + struct io_rings *rings = io_get_rings(ctx); + if (iowq->cq_min_tail != READ_ONCE(rings->cq.tail)) + goto out_wake; + /* if we have any events and min timeout expired, we're done */ + if (io_cqring_events(ctx)) + goto out_wake; + } /* * If using deferred task_work running and application is waiting on * more than one request, ensure we reset it now where we are switching @@ -186,9 +189,9 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, struct ext_arg *ext_arg) { struct io_wait_queue iowq; - struct io_rings *rings = ctx->rings; + struct io_rings *rings; ktime_t start_time; - int ret; + int ret, nr_wait; min_events = min_t(int, min_events, ctx->cq_entries); @@ -201,15 +204,23 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, if (unlikely(test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq))) io_cqring_do_overflow_flush(ctx); - if (__io_cqring_events_user(ctx) >= min_events) + + rcu_read_lock(); + rings = io_get_rings(ctx); + if (__io_cqring_events_user(ctx) >= min_events) { + rcu_read_unlock(); return 0; + } init_waitqueue_func_entry(&iowq.wq, io_wake_function); iowq.wq.private = current; INIT_LIST_HEAD(&iowq.wq.entry); iowq.ctx = ctx; - iowq.cq_tail = READ_ONCE(ctx->rings->cq.head) + min_events; - iowq.cq_min_tail = READ_ONCE(ctx->rings->cq.tail); + iowq.cq_tail = READ_ONCE(rings->cq.head) + min_events; + iowq.cq_min_tail = READ_ONCE(rings->cq.tail); + nr_wait = (int) iowq.cq_tail - READ_ONCE(rings->cq.tail); + rcu_read_unlock(); + rings = NULL; iowq.nr_timeouts = atomic_read(&ctx->cq_timeouts); iowq.hit_timeout = 0; iowq.min_timeout = ext_arg->min_time; @@ -240,14 +251,6 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, trace_io_uring_cqring_wait(ctx, min_events); do { unsigned long check_cq; - int nr_wait; - - /* if min timeout has been hit, don't reset wait count */ - if (!iowq.hit_timeout) - nr_wait = (int) iowq.cq_tail - - READ_ONCE(ctx->rings->cq.tail); - else - nr_wait = 1; if (ctx->flags & IORING_SETUP_DEFER_TASKRUN) { atomic_set(&ctx->cq_wait_nr, nr_wait); @@ -298,11 +301,20 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, break; } cond_resched(); + + /* if min timeout has been hit, don't reset wait count */ + if (!iowq.hit_timeout) + scoped_guard(rcu) + nr_wait = (int) iowq.cq_tail - + READ_ONCE(io_get_rings(ctx)->cq.tail); + else + nr_wait = 1; } while (1); if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN)) finish_wait(&ctx->cq_wait, &iowq.wq); restore_saved_sigmask_unless(ret == -EINTR); - return READ_ONCE(rings->cq.head) == READ_ONCE(rings->cq.tail) ? ret : 0; + guard(rcu)(); + return READ_ONCE(io_get_rings(ctx)->cq.head) == READ_ONCE(io_get_rings(ctx)->cq.tail) ? ret : 0; } diff --git a/io_uring/wait.h b/io_uring/wait.h index 5e236f74e1af..3a145fcfd3dd 100644 --- a/io_uring/wait.h +++ b/io_uring/wait.h @@ -28,12 +28,15 @@ void io_cqring_do_overflow_flush(struct io_ring_ctx *ctx); static inline unsigned int __io_cqring_events(struct io_ring_ctx *ctx) { - return ctx->cached_cq_tail - READ_ONCE(ctx->rings->cq.head); + struct io_rings *rings = io_get_rings(ctx); + return ctx->cached_cq_tail - READ_ONCE(rings->cq.head); } static inline unsigned int __io_cqring_events_user(struct io_ring_ctx *ctx) { - return READ_ONCE(ctx->rings->cq.tail) - READ_ONCE(ctx->rings->cq.head); + struct io_rings *rings = io_get_rings(ctx); + + return READ_ONCE(rings->cq.tail) - READ_ONCE(rings->cq.head); } /* From aa35dd6bdd033dea8aa3e20cbbbe10e06b2d044f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 31 Mar 2026 08:16:58 -0600 Subject: [PATCH 599/712] io_uring/bpf_filters: retain COW'ed settings on parse failures If io_parse_restrictions() fails, it ends up clearing any restrictions currently set. The intent is only to clear whatever it already applied, but it ends up clearing everything, including whatever settings may have been applied in a copy-on-write fashion already. Ensure that those are retained. Link: https://lore.kernel.org/io-uring/CAK8a0jzF-zaO5ZmdOrmfuxrhXuKg5m5+RDuO7tNvtj=kUYbW7Q@mail.gmail.com/ Reported-by: antonius Fixes: ed82f35b926b ("io_uring: allow registration of per-task restrictions") Signed-off-by: Jens Axboe --- io_uring/register.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/io_uring/register.c b/io_uring/register.c index 5f3eb018fb32..837324bf0223 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -178,9 +178,17 @@ static __cold int io_register_restrictions(struct io_ring_ctx *ctx, return -EBUSY; ret = io_parse_restrictions(arg, nr_args, &ctx->restrictions); - /* Reset all restrictions if an error happened */ + /* + * Reset all restrictions if an error happened, but retain any COW'ed + * settings. + */ if (ret < 0) { + struct io_bpf_filters *bpf = ctx->restrictions.bpf_filters; + bool cowed = ctx->restrictions.bpf_filters_cow; + memset(&ctx->restrictions, 0, sizeof(ctx->restrictions)); + ctx->restrictions.bpf_filters = bpf; + ctx->restrictions.bpf_filters_cow = cowed; return ret; } if (ctx->restrictions.op_registered) From cffff6df669a438ecac506dadd49a53d4475a796 Mon Sep 17 00:00:00 2001 From: Corey Hickey Date: Tue, 31 Mar 2026 14:49:06 -0700 Subject: [PATCH 600/712] hwmon: (asus-ec-sensors) Fix T_Sensor for PRIME X670E-PRO WIFI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the Asus PRIME X670E-PRO WIFI, the driver reports a constant value of zero for T_Sensor. On this board, the register for T_Sensor is at a different address, as found by experimentation and confirmed by comparison to an independent temperature reading. * sensor disconnected: -62.0°C * ambient temperature: +22.0°C * held between fingers: +30.0°C Introduce SENSOR_TEMP_T_SENSOR_ALT1 to support the PRIME X670E-PRO WIFI without causing a regression for other 600-series boards Fixes: e0444758dd1b ("hwmon: (asus-ec-sensors) add PRIME X670E-PRO WIFI") Signed-off-by: Corey Hickey Link: https://lore.kernel.org/r/20260331215414.368785-1-bugfood-ml@fatooh.org [groeck: Fixed typo, updated Fixes: reference] Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 86f444498650..adedaf0db10e 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -111,6 +111,8 @@ enum ec_sensors { ec_sensor_temp_mb, /* "T_Sensor" temperature sensor reading [℃] */ ec_sensor_temp_t_sensor, + /* like ec_sensor_temp_t_sensor, but at an alternate address [℃] */ + ec_sensor_temp_t_sensor_alt1, /* VRM temperature [℃] */ ec_sensor_temp_vrm, /* VRM east (right) temperature [℃] */ @@ -160,6 +162,7 @@ enum ec_sensors { #define SENSOR_TEMP_CPU_PACKAGE BIT(ec_sensor_temp_cpu_package) #define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb) #define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor) +#define SENSOR_TEMP_T_SENSOR_ALT1 BIT(ec_sensor_temp_t_sensor_alt1) #define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm) #define SENSOR_TEMP_VRME BIT(ec_sensor_temp_vrme) #define SENSOR_TEMP_VRMW BIT(ec_sensor_temp_vrmw) @@ -279,6 +282,8 @@ static const struct ec_sensor_info sensors_family_amd_600[] = { EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), [ec_sensor_temp_t_sensor] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36), + [ec_sensor_temp_t_sensor_alt1] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x37), [ec_sensor_fan_cpu_opt] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), [ec_sensor_temp_water_in] = @@ -519,7 +524,7 @@ static const struct ec_board_info board_info_prime_x570_pro = { static const struct ec_board_info board_info_prime_x670e_pro_wifi = { .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_MB | SENSOR_TEMP_VRM | - SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, + SENSOR_TEMP_T_SENSOR_ALT1 | SENSOR_FAN_CPU_OPT, .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH, .family = family_amd_600_series, }; From 429ebd815bbc06a0adbe8f1ffd0f328610381847 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 25 Mar 2026 04:59:02 +0200 Subject: [PATCH 601/712] drm/msm/mdp5: drop single flush support Support for using a single CTL for flushing both interfaces was not in use since the MDP5 driver dropped support for dual DSI configurations in the commit df3c7899946c ("drm/msm/mdp5: drop split display support"). Having the MDP 3.x support migrated to the DPU driver the single CTL flush is applicable to the platforms suspproted by the MDP5 driver. Drop it alltogether. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/713916/ Link: https://lore.kernel.org/r/20260325-mdp5-drop-single-flush-v1-1-862a38b4d2ec@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c | 90 ------------------------ drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.h | 1 - 2 files changed, 91 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c index fc183fe37f56..1eca140616c6 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c @@ -17,9 +17,6 @@ * a specific data path ID - REG_MDP5_CTL_*(, ...) * * Hardware capabilities determine the number of concurrent data paths - * - * In certain use cases (high-resolution dual pipe), one single CTL can be - * shared across multiple CRTCs. */ #define CTL_STAT_BUSY 0x1 @@ -46,11 +43,6 @@ struct mdp5_ctl { u32 pending_ctl_trigger; bool cursor_on; - - /* True if the current CTL has FLUSH bits pending for single FLUSH. */ - bool flush_pending; - - struct mdp5_ctl *pair; /* Paired CTL to be flushed together */ }; struct mdp5_ctl_manager { @@ -63,10 +55,6 @@ struct mdp5_ctl_manager { /* to filter out non-present bits in the current hardware config */ u32 flush_hw_mask; - /* status for single FLUSH */ - bool single_flush_supported; - u32 single_flush_pending_mask; - /* pool of CTLs + lock to protect resource allocation (ctls[i].busy) */ spinlock_t pool_lock; struct mdp5_ctl ctls[MAX_CTL]; @@ -485,31 +473,6 @@ static u32 fix_sw_flush(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, return sw_mask; } -static void fix_for_single_flush(struct mdp5_ctl *ctl, u32 *flush_mask, - u32 *flush_id) -{ - struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; - - if (ctl->pair) { - DBG("CTL %d FLUSH pending mask %x", ctl->id, *flush_mask); - ctl->flush_pending = true; - ctl_mgr->single_flush_pending_mask |= (*flush_mask); - *flush_mask = 0; - - if (ctl->pair->flush_pending) { - *flush_id = min_t(u32, ctl->id, ctl->pair->id); - *flush_mask = ctl_mgr->single_flush_pending_mask; - - ctl->flush_pending = false; - ctl->pair->flush_pending = false; - ctl_mgr->single_flush_pending_mask = 0; - - DBG("Single FLUSH mask %x,ID %d", *flush_mask, - *flush_id); - } - } -} - /** * mdp5_ctl_commit() - Register Flush * @@ -555,8 +518,6 @@ u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, curr_ctl_flush_mask = flush_mask; - fix_for_single_flush(ctl, &flush_mask, &flush_id); - if (!start) { ctl->flush_mask |= flush_mask; return curr_ctl_flush_mask; @@ -588,40 +549,6 @@ int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl) return WARN_ON(!ctl) ? -EINVAL : ctl->id; } -/* - * mdp5_ctl_pair() - Associate 2 booked CTLs for single FLUSH - */ -int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable) -{ - struct mdp5_ctl_manager *ctl_mgr = ctlx->ctlm; - struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); - - /* do nothing silently if hw doesn't support */ - if (!ctl_mgr->single_flush_supported) - return 0; - - if (!enable) { - ctlx->pair = NULL; - ctly->pair = NULL; - mdp5_write(mdp5_kms, REG_MDP5_SPARE_0, 0); - return 0; - } else if ((ctlx->pair != NULL) || (ctly->pair != NULL)) { - DRM_DEV_ERROR(ctl_mgr->dev->dev, "CTLs already paired\n"); - return -EINVAL; - } else if (!(ctlx->status & ctly->status & CTL_STAT_BOOKED)) { - DRM_DEV_ERROR(ctl_mgr->dev->dev, "Only pair booked CTLs\n"); - return -EINVAL; - } - - ctlx->pair = ctly; - ctly->pair = ctlx; - - mdp5_write(mdp5_kms, REG_MDP5_SPARE_0, - MDP5_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN); - - return 0; -} - /* * mdp5_ctl_request() - CTL allocation * @@ -687,8 +614,6 @@ struct mdp5_ctl_manager *mdp5_ctlm_init(struct drm_device *dev, { struct mdp5_ctl_manager *ctl_mgr; const struct mdp5_cfg_hw *hw_cfg = mdp5_cfg_get_hw_config(cfg_hnd); - int rev = mdp5_cfg_get_hw_rev(cfg_hnd); - unsigned dsi_cnt = 0; const struct mdp5_ctl_block *ctl_cfg = &hw_cfg->ctl; unsigned long flags; int c, ret; @@ -730,21 +655,6 @@ struct mdp5_ctl_manager *mdp5_ctlm_init(struct drm_device *dev, spin_lock_init(&ctl->hw_lock); } - /* - * In bonded DSI case, CTL0 and CTL1 are always assigned to two DSI - * interfaces to support single FLUSH feature (Flush CTL0 and CTL1 when - * only write into CTL0's FLUSH register) to keep two DSI pipes in sync. - * Single FLUSH is supported from hw rev v3.0. - */ - for (c = 0; c < ARRAY_SIZE(hw_cfg->intf.connect); c++) - if (hw_cfg->intf.connect[c] == INTF_DSI) - dsi_cnt++; - if ((rev >= 3) && (dsi_cnt > 1)) { - ctl_mgr->single_flush_supported = true; - /* Reserve CTL0/1 for INTF1/2 */ - ctl_mgr->ctls[0].status |= CTL_STAT_BOOKED; - ctl_mgr->ctls[1].status |= CTL_STAT_BOOKED; - } spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags); DBG("Pool of %d CTLs created.", ctl_mgr->nctl); diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.h index 9020e8efc4e4..0c45f7874c24 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.h +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.h @@ -35,7 +35,6 @@ int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, struct mdp5_pipeline *p, int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, int cursor_id, bool enable); -int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable); #define MAX_PIPE_STAGE 2 From e224e3a167bc97ed2a5a603acf7e16e4319437de Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 25 Mar 2026 05:06:06 +0200 Subject: [PATCH 602/712] drm/msm/mdp5: drop workarounds specific to MDP5 1.0 With support for MSM8974v1 being removed from the driver, there is no need to keep workarounds specific to that particular MDP5 revision. Drop them, slightly simplifying the logic. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/713918/ Link: https://lore.kernel.org/r/20260325-mdp5-further-drop-mdp1-0-v1-1-5ccee47fd1aa@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c index 500b7dc895d0..890d2f31510e 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c @@ -118,8 +118,6 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp, u32 width, bool hdecim) { const struct drm_format_info *info = drm_format_info(format->pixel_format); - struct mdp5_kms *mdp5_kms = get_kms(smp); - int rev = mdp5_cfg_get_hw_rev(mdp5_kms->cfg); int i, hsub, nplanes, nlines; uint32_t blkcfg = 0; @@ -133,7 +131,7 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp, * U and V components (splits them from Y if necessary) and packs * them together, writes to SMP using a single client. */ - if ((rev > 0) && (format->chroma_sample > CHROMA_FULL)) { + if (format->chroma_sample > CHROMA_FULL) { nplanes = 2; /* if decimation is enabled, HW decimates less on the @@ -151,10 +149,6 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp, n = DIV_ROUND_UP(fetch_stride * nlines, smp->blk_size); - /* for hw rev v1.00 */ - if (rev == 0) - n = roundup_pow_of_two(n); - blkcfg |= (n << (8 * i)); } From 469df8c2b571bb243d0da30424686aac14a8f068 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 25 Mar 2026 07:32:27 +0200 Subject: [PATCH 603/712] drm/msm/dpu: correct DP MST interface configuration Due to historical reasons we ended up with dummy values being specified for MST-related interfaces some of them had INTF_NONE, others had non-existing DP controller indices. Those workarounds are no longer necessary. Fix types and indices for all DP-MST related INTF instances. The only exception is INTF_3 on SC8180X, which has unique design. It can be used either with INTF_0 / DP0 or with INTF_4 / DP1. This interface is left with the dummy value until somebody implements necessary bits for that platform. Co-developed-by: Abhinav Kumar Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/713988/ Link: https://lore.kernel.org/r/20260325-fix-dp-mst-interfaces-v1-1-186d1de3fa1b@oss.qualcomm.com --- .../gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h | 6 +++--- .../drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h | 2 +- .../drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h | 13 ++++++------- .../gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h | 2 +- .../gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 9 ++++----- .../gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h | 2 +- .../drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h | 2 +- .../drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 7 +++---- 16 files changed, 28 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h index db79f9382f8b..2eef5f69832f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h @@ -377,7 +377,7 @@ static const struct dpu_intf_cfg sm8650_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h index 59caa2c2a87c..3889eb8a5428 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h @@ -419,7 +419,7 @@ static const struct dpu_intf_cfg sm8750_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x4bc, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h index 5e24309b4674..4eb4a0e9803e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h @@ -425,7 +425,7 @@ static const struct dpu_intf_cfg glymur_intf[] = { }, { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x400, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), @@ -457,7 +457,7 @@ static const struct dpu_intf_cfg glymur_intf[] = { }, { .name = "intf_7", .id = INTF_7, .base = 0x3b000, .len = 0x400, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_2, /* pair with intf_6 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18), @@ -465,7 +465,7 @@ static const struct dpu_intf_cfg glymur_intf[] = { }, { .name = "intf_8", .id = INTF_8, .base = 0x3c000, .len = 0x400, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_4 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h index bf1940d9c9e9..b7b06e45b529 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h @@ -417,7 +417,7 @@ static const struct dpu_intf_cfg kaanapali_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x190000, .len = 0x4bc, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h index 019135c9a831..b608d2bb6d48 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h @@ -258,7 +258,7 @@ static const struct dpu_intf_cfg sdm845_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x6b800, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h index e61e14572aff..9729a65d3e3a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h @@ -316,7 +316,7 @@ static const struct dpu_intf_cfg sm8150_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x6b800, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h index ffb89a03cfad..92a614686cb6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h @@ -230,7 +230,7 @@ static const struct dpu_intf_cfg sm7150_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x6b800, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h index 427ecd4cbf63..87488d3ed95f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h @@ -185,7 +185,7 @@ static const struct dpu_intf_cfg sm6150_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x6b800, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h index c481e964fca0..0e311d54ef18 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h @@ -301,7 +301,7 @@ static const struct dpu_intf_cfg sm8250_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x6b800, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h index 9afdfdb3be6f..d06c14f05faf 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h @@ -326,7 +326,7 @@ static const struct dpu_intf_cfg sm8350_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h index 07a2c286a7f6..0ba907711d36 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h @@ -288,7 +288,6 @@ static const struct dpu_dsc_cfg sc8280xp_dsc[] = { }, }; -/* TODO: INTF 3, 8 and 7 are used for MST, marked as INTF_NONE for now */ static const struct dpu_intf_cfg sc8280xp_intf[] = { { .name = "intf_0", .id = INTF_0, @@ -319,8 +318,8 @@ static const struct dpu_intf_cfg sc8280xp_intf[] = { }, { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, - .type = INTF_NONE, - .controller_id = MSM_DP_CONTROLLER_0, + .type = INTF_DP, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), @@ -351,16 +350,16 @@ static const struct dpu_intf_cfg sc8280xp_intf[] = { }, { .name = "intf_7", .id = INTF_7, .base = 0x3b000, .len = 0x280, - .type = INTF_NONE, - .controller_id = MSM_DP_CONTROLLER_2, + .type = INTF_DP, + .controller_id = MSM_DP_CONTROLLER_2, /* pair with intf_6 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 19), }, { .name = "intf_8", .id = INTF_8, .base = 0x3c000, .len = 0x280, - .type = INTF_NONE, - .controller_id = MSM_DP_CONTROLLER_1, + .type = INTF_DP, + .controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_8 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 13), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h index bdab0ebfe102..170a709f987c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h @@ -339,7 +339,7 @@ static const struct dpu_intf_cfg sm8450_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h index f3d85d173c56..9cc5e22e1228 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h @@ -315,7 +315,6 @@ static const struct dpu_wb_cfg sa8775p_wb[] = { }, }; -/* TODO: INTF 3, 6, 7 and 8 are used for MST, marked as INTF_NONE for now */ static const struct dpu_intf_cfg sa8775p_intf[] = { { .name = "intf_0", .id = INTF_0, @@ -346,7 +345,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = { }, { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), @@ -362,7 +361,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = { }, { .name = "intf_6", .id = INTF_6, .base = 0x3A000, .len = 0x280, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16), @@ -370,7 +369,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = { }, { .name = "intf_7", .id = INTF_7, .base = 0x3b000, .len = 0x280, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18), @@ -378,7 +377,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = { }, { .name = "intf_8", .id = INTF_8, .base = 0x3c000, .len = 0x280, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_4 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h index 5837e252f5d2..b3d0f221807b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h @@ -334,7 +334,7 @@ static const struct dpu_intf_cfg sm8550_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h index 9cc0b7ea3a30..7effe7120a56 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h @@ -334,7 +334,7 @@ static const struct dpu_intf_cfg sar2130p_intf[] = { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, .type = INTF_DP, - .controller_id = MSM_DP_CONTROLLER_1, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h index 10443368f682..7b3febf8742d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h @@ -303,7 +303,6 @@ static const struct dpu_wb_cfg x1e80100_wb[] = { }, }; -/* TODO: INTF 3, 8 and 7 are used for MST, marked as INTF_NONE for now */ static const struct dpu_intf_cfg x1e80100_intf[] = { { .name = "intf_0", .id = INTF_0, @@ -334,7 +333,7 @@ static const struct dpu_intf_cfg x1e80100_intf[] = { }, { .name = "intf_3", .id = INTF_3, .base = 0x37000, .len = 0x280, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), @@ -366,7 +365,7 @@ static const struct dpu_intf_cfg x1e80100_intf[] = { }, { .name = "intf_7", .id = INTF_7, .base = 0x3b000, .len = 0x280, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_2, /* pair with intf_6 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18), @@ -374,7 +373,7 @@ static const struct dpu_intf_cfg x1e80100_intf[] = { }, { .name = "intf_8", .id = INTF_8, .base = 0x3c000, .len = 0x280, - .type = INTF_NONE, + .type = INTF_DP, .controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_4 for DP MST */ .prog_fetch_lines_worst_case = 24, .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12), From 7090420420d5a7d7c88b21d16962f2a230be3ef3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 25 Mar 2026 07:35:44 +0200 Subject: [PATCH 604/712] drm/msm/dpu: drop INTF_0 on MSM8953 There is no INTF_0 on MSM8953. Currently catalog lists dummy INTF_NONE entry for it. Drop it from the catalog. Fixes: 7a6109ce1c2c ("drm/msm/dpu: Add support for MSM8953") Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/713990/ Link: https://lore.kernel.org/r/20260325-drop-8953-intf-v1-1-d80e214a1a75@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h index e8aabe43c9ff..859a97e8c07e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h @@ -121,13 +121,6 @@ static const struct dpu_dspp_cfg msm8953_dspp[] = { static const struct dpu_intf_cfg msm8953_intf[] = { { - .name = "intf_0", .id = INTF_0, - .base = 0x6a000, .len = 0x268, - .type = INTF_NONE, - .prog_fetch_lines_worst_case = 14, - .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 24), - .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 25), - }, { .name = "intf_1", .id = INTF_1, .base = 0x6a800, .len = 0x268, .type = INTF_DSI, From 2a0ffcd5456c4d9c395ad995d0298e3672d14ac9 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Mar 2026 20:46:53 +0200 Subject: [PATCH 605/712] drm/msm/dp: remove debugging prints with internal struct phy state These do not provide much value, and will become hard to maintain once the Generic PHY framework starts hiding the contents of struct phy from consumers. Signed-off-by: Vladimir Oltean Acked-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/714986/ Link: https://lore.kernel.org/r/20260327184706.1600329-16-vladimir.oltean@nxp.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 5fc261191cb7..4fd987bb7dad 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -1928,9 +1928,6 @@ void msm_dp_ctrl_phy_init(struct msm_dp_ctrl *msm_dp_ctrl) msm_dp_ctrl_phy_reset(ctrl); phy_init(phy); - - drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n", - phy, phy->init_count, phy->power_count); } void msm_dp_ctrl_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl) @@ -1943,8 +1940,6 @@ void msm_dp_ctrl_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl) msm_dp_ctrl_phy_reset(ctrl); phy_exit(phy); - drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n", - phy, phy->init_count, phy->power_count); } static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) @@ -1996,8 +1991,6 @@ static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) phy_exit(phy); phy_init(phy); - drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n", - phy, phy->init_count, phy->power_count); return 0; } @@ -2588,9 +2581,6 @@ void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl) /* aux channel down, reinit phy */ phy_exit(phy); phy_init(phy); - - drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n", - phy, phy->init_count, phy->power_count); } void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl) @@ -2606,13 +2596,7 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl) dev_pm_opp_set_rate(ctrl->dev, 0); msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); - DRM_DEBUG_DP("Before, phy=%p init_count=%d power_on=%d\n", - phy, phy->init_count, phy->power_count); - phy_power_off(phy); - - DRM_DEBUG_DP("After, phy=%p init_count=%d power_on=%d\n", - phy, phy->init_count, phy->power_count); } void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) @@ -2638,8 +2622,6 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); phy_power_off(phy); - drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n", - phy, phy->init_count, phy->power_count); } irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl) From 502455d8bef2f8502540102218c47fc12da2a04e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 31 Mar 2026 18:11:57 +0200 Subject: [PATCH 606/712] drm/msm/dpu: eliza: Use Eliza-specific CWB array The driver references CWB array from SM8650, but should use the Eliza specific, which has different register space sizes. This should not have noticeable impact on function but is indeed confusing, since the Eliza table is used for .cwb_count. Fixes: 0eb707bbc7fc ("drm/msm/dpu: Add support for Eliza SoC") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/715623/ Link: https://lore.kernel.org/r/20260331161156.211623-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h index b482a7e4e6c0..b93d32888972 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h @@ -353,7 +353,7 @@ const struct dpu_mdss_cfg dpu_eliza_cfg = { .wb_count = ARRAY_SIZE(eliza_wb), .wb = eliza_wb, .cwb_count = ARRAY_SIZE(eliza_cwb), - .cwb = sm8650_cwb, + .cwb = eliza_cwb, .intf_count = ARRAY_SIZE(eliza_intf), .intf = eliza_intf, .vbif = &sm8650_vbif, From f94aa7e9cf6821d28808d80e5c4f20e491d59a26 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 5 Mar 2026 01:47:12 +0200 Subject: [PATCH 607/712] dt-bindings: display/msm: move DSI PHY bindings to phy/ subdir Historically DSI PHY bindings landed to the display/msm subdir, however they describe PHYs and as such they should be in the phy/ subdir. Follow the example of other Qualcomm display-related PHYs (HDMI, eDP) and move bindings for the Qualcomm DSI PHYs to the correct subdir. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Acked-by: Vinod Koul Patchwork: https://patchwork.freedesktop.org/patch/709008/ Link: https://lore.kernel.org/r/20260305-msm-dsi-phy-v1-1-0a99ac665995@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../msm/dsi-phy-10nm.yaml => phy/qcom,dsi-phy-10nm.yaml} | 4 ++-- .../msm/dsi-phy-14nm.yaml => phy/qcom,dsi-phy-14nm.yaml} | 4 ++-- .../msm/dsi-phy-20nm.yaml => phy/qcom,dsi-phy-20nm.yaml} | 4 ++-- .../msm/dsi-phy-28nm.yaml => phy/qcom,dsi-phy-28nm.yaml} | 4 ++-- .../msm/dsi-phy-7nm.yaml => phy/qcom,dsi-phy-7nm.yaml} | 4 ++-- .../msm/dsi-phy-common.yaml => phy/qcom,dsi-phy-common.yaml} | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) rename Documentation/devicetree/bindings/{display/msm/dsi-phy-10nm.yaml => phy/qcom,dsi-phy-10nm.yaml} (96%) rename Documentation/devicetree/bindings/{display/msm/dsi-phy-14nm.yaml => phy/qcom,dsi-phy-14nm.yaml} (94%) rename Documentation/devicetree/bindings/{display/msm/dsi-phy-20nm.yaml => phy/qcom,dsi-phy-20nm.yaml} (93%) rename Documentation/devicetree/bindings/{display/msm/dsi-phy-28nm.yaml => phy/qcom,dsi-phy-28nm.yaml} (94%) rename Documentation/devicetree/bindings/{display/msm/dsi-phy-7nm.yaml => phy/qcom,dsi-phy-7nm.yaml} (95%) rename Documentation/devicetree/bindings/{display/msm/dsi-phy-common.yaml => phy/qcom,dsi-phy-common.yaml} (91%) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-10nm.yaml similarity index 96% rename from Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml rename to Documentation/devicetree/bindings/phy/qcom,dsi-phy-10nm.yaml index fc9abf090f0d..d98217747ad1 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-10nm.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/display/msm/dsi-phy-10nm.yaml# +$id: http://devicetree.org/schemas/phy/qcom,dsi-phy-10nm.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Display DSI 10nm PHY @@ -10,7 +10,7 @@ maintainers: - Krishna Manikandan allOf: - - $ref: dsi-phy-common.yaml# + - $ref: qcom,dsi-phy-common.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-14nm.yaml similarity index 94% rename from Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml rename to Documentation/devicetree/bindings/phy/qcom,dsi-phy-14nm.yaml index 206a9a4b3845..be31b9bac9d5 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-14nm.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/display/msm/dsi-phy-14nm.yaml# +$id: http://devicetree.org/schemas/phy/qcom,dsi-phy-14nm.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Display DSI 14nm PHY @@ -10,7 +10,7 @@ maintainers: - Krishna Manikandan allOf: - - $ref: dsi-phy-common.yaml# + - $ref: qcom,dsi-phy-common.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-20nm.yaml b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-20nm.yaml similarity index 93% rename from Documentation/devicetree/bindings/display/msm/dsi-phy-20nm.yaml rename to Documentation/devicetree/bindings/phy/qcom,dsi-phy-20nm.yaml index 93570052992a..1d135419d015 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-20nm.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-20nm.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/display/msm/dsi-phy-20nm.yaml# +$id: http://devicetree.org/schemas/phy/qcom,dsi-phy-20nm.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Display DSI 20nm PHY @@ -10,7 +10,7 @@ maintainers: - Krishna Manikandan allOf: - - $ref: dsi-phy-common.yaml# + - $ref: qcom,dsi-phy-common.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-28nm.yaml similarity index 94% rename from Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml rename to Documentation/devicetree/bindings/phy/qcom,dsi-phy-28nm.yaml index 371befa9f9d2..f8fe75fa29d7 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-28nm.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/display/msm/dsi-phy-28nm.yaml# +$id: http://devicetree.org/schemas/phy/qcom,dsi-phy-28nm.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Display DSI 28nm PHY @@ -10,7 +10,7 @@ maintainers: - Krishna Manikandan allOf: - - $ref: dsi-phy-common.yaml# + - $ref: qcom,dsi-phy-common.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml similarity index 95% rename from Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml rename to Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml index b5a0c1461250..966c70d746aa 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-7nm.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/display/msm/dsi-phy-7nm.yaml# +$id: http://devicetree.org/schemas/phy/qcom,dsi-phy-7nm.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Display DSI 7nm PHY @@ -10,7 +10,7 @@ maintainers: - Jonathan Marek allOf: - - $ref: dsi-phy-common.yaml# + - $ref: qcom,dsi-phy-common.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-common.yaml b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-common.yaml similarity index 91% rename from Documentation/devicetree/bindings/display/msm/dsi-phy-common.yaml rename to Documentation/devicetree/bindings/phy/qcom,dsi-phy-common.yaml index d0ce85a08b6d..849321e56b2f 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-common.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-common.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/display/msm/dsi-phy-common.yaml# +$id: http://devicetree.org/schemas/phy/qcom,dsi-phy-common.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Display DSI PHY Common Properties From a972d1b864e8efcfda0b387debac8ea2875a182c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 1 Feb 2025 15:58:28 +0000 Subject: [PATCH 608/712] drm/msm: Use of_get_available_child_by_name() Simplify zap_shader_load_mdt() by using of_get_available_child_by_name(). Signed-off-by: Biju Das Patchwork: https://patchwork.freedesktop.org/patch/635020/ Link: https://lore.kernel.org/r/20250201155830.39366-1-biju.das.jz@bp.renesas.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 85d135a9d336..66f80f2d12f9 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -45,8 +45,8 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname, return -EINVAL; } - np = of_get_child_by_name(dev->of_node, "zap-shader"); - if (!of_device_is_available(np)) { + np = of_get_available_child_by_name(dev->of_node, "zap-shader"); + if (!np) { zap_available = false; return -ENODEV; } From 328335a79487ec38d6b0e1aa807785b0f75e594d Mon Sep 17 00:00:00 2001 From: Gaurav Batra Date: Tue, 31 Mar 2026 17:30:22 -0500 Subject: [PATCH 609/712] powerpc/powernv/iommu: iommu incorrectly bypass DMA APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a PowerNV environment, for devices that supports DMA mask less than 64 bit but larger than 32 bits, iommu is incorrectly bypassing DMA APIs while allocating and mapping buffers for DMA operations. Devices are failing with ENOMEN during probe with the following messages amdgpu 0000:01:00.0: [drm] Detected VRAM RAM=4096M, BAR=4096M amdgpu 0000:01:00.0: [drm] RAM width 128bits GDDR5 amdgpu 0000:01:00.0: iommu: 64-bit OK but direct DMA is limited by 0 amdgpu 0000:01:00.0: dma_iommu_get_required_mask: returning bypass mask 0xfffffffffffffff amdgpu 0000:01:00.0: 4096M of VRAM memory ready amdgpu 0000:01:00.0: 32570M of GTT memory ready. amdgpu 0000:01:00.0: (-12) failed to allocate kernel bo amdgpu 0000:01:00.0: [drm] Debug VRAM access will use slowpath MM access amdgpu 0000:01:00.0: [drm] GART: num cpu pages 4096, num gpu pages 65536 amdgpu 0000:01:00.0: [drm] PCIE GART of 256M enabled (table at 0x000000F4FFF80000). amdgpu 0000:01:00.0: (-12) failed to allocate kernel bo amdgpu 0000:01:00.0: (-12) create WB bo failed amdgpu 0000:01:00.0: amdgpu_device_wb_init failed -12 amdgpu 0000:01:00.0: amdgpu_device_ip_init failed amdgpu 0000:01:00.0: Fatal error during GPU init amdgpu 0000:01:00.0: finishing device. amdgpu 0000:01:00.0: probe with driver amdgpu failed with error -12 amdgpu 0000:01:00.0: ttm finalized Fixes: 1471c517cf7d ("powerpc/iommu: bypass DMA APIs for coherent allocations for pre-mapped memory") Suggested-by: Ritesh Harjani (IBM) Reviewed-by: Ritesh Harjani (IBM) Reported-by: Dan Horák Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5039 Tested-by: Dan Horak Closes: https://lore.kernel.org/linuxppc-dev/20260313142351.609bc4c3efe1184f64ca5f44@danny.cz/ Signed-off-by: Gaurav Batra Closes: https://lore.kernel.org/linuxppc-dev/20260313142351.609bc4c3efe1184f64ca5f44@danny.cz/ [Maddy: Fixed tags] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260331223022.47488-1-gbatra@linux.ibm.com --- arch/powerpc/kernel/dma-iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 73e10bd4d56d..8b4de508d2eb 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -67,7 +67,7 @@ bool arch_dma_unmap_sg_direct(struct device *dev, struct scatterlist *sg, } bool arch_dma_alloc_direct(struct device *dev) { - if (dev->dma_ops_bypass) + if (dev->dma_ops_bypass && dev->bus_dma_limit) return true; return false; @@ -75,7 +75,7 @@ bool arch_dma_alloc_direct(struct device *dev) bool arch_dma_free_direct(struct device *dev, dma_addr_t dma_handle) { - if (!dev->dma_ops_bypass) + if (!dev->dma_ops_bypass || !dev->bus_dma_limit) return false; return is_direct_handle(dev, dma_handle); From 35d8945bf9b0c4d9586c7fa9fadeecf2cfc26c23 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Mon, 16 Mar 2026 01:28:22 +0800 Subject: [PATCH 610/712] MIPS: Loongson64: env: Check UARTs passed by LEFI cautiously Some firmware does not set nr_uarts properly and passes empty items. Iterate at most min(system->nr_uarts, MAX_UARTS) items to prevent out-of-bounds access, and ignore UARTs with addr 0 silently. Meanwhile, our DT only works with UPIO_MEM but theoretically firmware may pass other IO types, so explicitly check against that. Tested on Loongson-LS3A4000-7A1000-NUC-SE. Fixes: 3989ed418483 ("MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI") Cc: stable@vger.kernel.org Reviewed-by: Yao Zi Signed-off-by: Rong Zhang Reviewed-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/loongson64/env.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c index 11ddf02d6a15..7abcca7ab4ed 100644 --- a/arch/mips/loongson64/env.c +++ b/arch/mips/loongson64/env.c @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -106,9 +108,23 @@ static void __init lefi_fixup_fdt(struct system_loongson *system) is_loongson64g = (read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G; - for (i = 0; i < system->nr_uarts; i++) { + for (i = 0; i < min(system->nr_uarts, MAX_UARTS); i++) { uartdev = &system->uarts[i]; + /* + * Some firmware does not set nr_uarts properly and passes empty + * items. Ignore them silently. + */ + if (uartdev->uart_base == 0) + continue; + + /* Our DT only works with UPIO_MEM. */ + if (uartdev->iotype != UPIO_MEM) { + pr_warn("Ignore UART 0x%llx with iotype %u passed by firmware\n", + uartdev->uart_base, uartdev->iotype); + continue; + } + ret = lefi_fixup_fdt_serial(fdt_buf, uartdev->uart_base, uartdev->uartclk); /* From 43985a62bab9d35e5e9af41118ce2f44c01b97d2 Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Tue, 24 Feb 2026 10:22:50 +0800 Subject: [PATCH 611/712] mips: ralink: update CPU clock index Update CPU clock index to match the clock driver changes. Fixes: d34db686a3d7 ("clk: ralink: mtmips: fix clocks probe order in oldest ralink SoCs") Signed-off-by: Mieczyslaw Nalewaj Signed-off-by: Shiji Yang Reviewed-by: Sergio Paracuellos Signed-off-by: Thomas Bogendoerfer --- arch/mips/ralink/clk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c index 9db73fcac522..5c1eb46ef5d0 100644 --- a/arch/mips/ralink/clk.c +++ b/arch/mips/ralink/clk.c @@ -21,16 +21,16 @@ static const char *clk_cpu(int *idx) { switch (ralink_soc) { case RT2880_SOC: - *idx = 0; + *idx = 1; return "ralink,rt2880-sysc"; case RT3883_SOC: - *idx = 0; + *idx = 1; return "ralink,rt3883-sysc"; case RT305X_SOC_RT3050: - *idx = 0; + *idx = 1; return "ralink,rt3050-sysc"; case RT305X_SOC_RT3052: - *idx = 0; + *idx = 1; return "ralink,rt3052-sysc"; case RT305X_SOC_RT3350: *idx = 1; From d62cf1511743526f530a4c169424e50c757f5a5e Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 11:38:06 +0000 Subject: [PATCH 612/712] MIPS: SiByte: Bring back cache initialisation Bring back cache initialisation for Broadcom SiByte SB1 cores, which has been removed causing the kernel to hang at bootstrap right after: Dentry cache hash table entries: 524288 (order: 8, 4194304 bytes, linear) Inode-cache hash table entries: 262144 (order: 7, 2097152 bytes, linear) The cause of the problem is R4k cache handlers are also used by Broadcom SiByte SB1 cores, however with a different cache error exception handler and therefore not using CPU_R4K_CACHE_TLB: obj-$(CONFIG_CPU_R4K_CACHE_TLB) += c-r4k.o cex-gen.o tlb-r4k.o obj-$(CONFIG_CPU_SB1) += c-r4k.o cerr-sb1.o cex-sb1.o tlb-r4k.o (from arch/mips/mm/Makefile). Fixes: bbe4f634f48c ("mips: fix r3k_cache_init build regression") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v6.8+ Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/cache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index e3b4224c9a40..ad9b0430a28e 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -207,7 +207,8 @@ void cpu_cache_init(void) { if (IS_ENABLED(CONFIG_CPU_R3000) && cpu_has_3k_cache) r3k_cache_init(); - if (IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) && cpu_has_4k_cache) + if ((IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) || + IS_ENABLED(CONFIG_CPU_SB1)) && cpu_has_4k_cache) r4k_cache_init(); if (IS_ENABLED(CONFIG_CPU_CAVIUM_OCTEON) && cpu_has_octeon_cache) From ec8bf18814915460d9c617b556bf024efef26613 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 30 Mar 2026 02:54:09 +0100 Subject: [PATCH 613/712] MIPS: Fix the GCC version check for `__multi3' workaround It was only GCC 10 that fixed a MIPS64r6 code generation issue with a `__multi3' libcall inefficiently produced to perform 64-bit widening multiplication while suitable machine instructions exist to do such a calculation. The fix went in with GCC commit 48b2123f6336 ("re PR target/82981 (unnecessary __multi3 call for mips64r6 linux kernel)"). Adjust our code accordingly, removing build failures such as: mips64-linux-ld: lib/math/div64.o: in function `mul_u64_add_u64_div_u64': div64.c:(.text+0x84): undefined reference to `__multi3' with the GCC versions affected. Fixes: ebabcf17bcd7 ("MIPS: Implement __multi3 for GCC7 MIPS64r6 builds") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601140146.hMLODc6v-lkp@intel.com/ Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v4.15+ Reviewed-by: David Laight --- arch/mips/lib/multi3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/lib/multi3.c b/arch/mips/lib/multi3.c index 4c2483f410c2..92b3778bb56f 100644 --- a/arch/mips/lib/multi3.c +++ b/arch/mips/lib/multi3.c @@ -4,12 +4,12 @@ #include "libgcc.h" /* - * GCC 7 & older can suboptimally generate __multi3 calls for mips64r6, so for + * GCC 9 & older can suboptimally generate __multi3 calls for mips64r6, so for * that specific case only we implement that intrinsic here. * * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981 */ -#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 8) +#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 10) /* multiply 64-bit values, low 64-bits returned */ static inline long long notrace dmulu(long long a, long long b) @@ -51,4 +51,4 @@ ti_type notrace __multi3(ti_type a, ti_type b) } EXPORT_SYMBOL(__multi3); -#endif /* 64BIT && CPU_MIPSR6 && GCC7 */ +#endif /* 64BIT && CPU_MIPSR6 && GCC9 */ From 8374c2cb83b95b3c92f129fd56527225c20a058c Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 18:57:18 +0000 Subject: [PATCH 614/712] MIPS: Always record SEGBITS in cpu_data.vmbits With a 32-bit kernel running on 64-bit MIPS hardware the hardcoded value of `cpu_vmbits' only records the size of compatibility useg and does not reflect the size of native xuseg or the complete range of values allowed in the VPN2 field of TLB entries. An upcoming change will need the actual VPN2 value range permitted even in 32-bit kernel configurations, so always include the `vmbits' member in `struct cpuinfo_mips' and probe for SEGBITS when running on 64-bit hardware and resorting to the currently hardcoded value of 31 on 32-bit processors. No functional change for users of `cpu_vmbits'. Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/cpu-features.h | 1 - arch/mips/include/asm/cpu-info.h | 2 -- arch/mips/include/asm/mipsregs.h | 2 ++ arch/mips/kernel/cpu-probe.c | 13 ++++++++----- arch/mips/kernel/cpu-r3k-probe.c | 2 ++ 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index 404390bb87ea..3f11e5218e6c 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -484,7 +484,6 @@ # endif # ifndef cpu_vmbits # define cpu_vmbits cpu_data[0].vmbits -# define __NEED_VMBITS_PROBE # endif #endif diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index fd60837ce50b..211b578af6aa 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -80,9 +80,7 @@ struct cpuinfo_mips { int srsets; /* Shadow register sets */ int package;/* physical package number */ unsigned int globalnumber; -#ifdef CONFIG_64BIT int vmbits; /* Virtual memory size in bits */ -#endif void *data; /* Additional data */ unsigned int watch_reg_count; /* Number that exist */ unsigned int watch_reg_use_cnt; /* Usable by ptrace */ diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index f799c0d723da..12a095dbf9e2 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -1871,6 +1871,8 @@ do { \ #define read_c0_entryhi() __read_ulong_c0_register($10, 0) #define write_c0_entryhi(val) __write_ulong_c0_register($10, 0, val) +#define read_c0_entryhi_64() __read_64bit_c0_register($10, 0) +#define write_c0_entryhi_64(val) __write_64bit_c0_register($10, 0, val) #define read_c0_guestctl1() __read_32bit_c0_register($10, 4) #define write_c0_guestctl1(val) __write_32bit_c0_register($10, 4, val) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 1e49e05ac8b1..489612ed9d49 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -210,11 +210,14 @@ static inline void set_elf_base_platform(const char *plat) static inline void cpu_probe_vmbits(struct cpuinfo_mips *c) { -#ifdef __NEED_VMBITS_PROBE - write_c0_entryhi(0x3fffffffffffe000ULL); - back_to_back_c0_hazard(); - c->vmbits = fls64(read_c0_entryhi() & 0x3fffffffffffe000ULL); -#endif + int vmbits = 31; + + if (cpu_has_64bits) { + write_c0_entryhi_64(0x3fffffffffffe000ULL); + back_to_back_c0_hazard(); + vmbits = fls64(read_c0_entryhi_64() & 0x3fffffffffffe000ULL); + } + c->vmbits = vmbits; } static void set_isa(struct cpuinfo_mips *c, unsigned int isa) diff --git a/arch/mips/kernel/cpu-r3k-probe.c b/arch/mips/kernel/cpu-r3k-probe.c index 0c826f729f75..edcf04de0a6f 100644 --- a/arch/mips/kernel/cpu-r3k-probe.c +++ b/arch/mips/kernel/cpu-r3k-probe.c @@ -137,6 +137,8 @@ void cpu_probe(void) else cpu_set_nofpu_opts(c); + c->vmbits = 31; + reserve_exception_space(0, 0x400); } From 74283cfe216392c7b776ebf6045b5b15ed9dffcd Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 18:57:23 +0000 Subject: [PATCH 615/712] MIPS: mm: Suppress TLB uniquification on EHINV hardware Hardware that supports the EHINV feature, mandatory for R6 ISA and FTLB implementation, lets software mark TLB entries invalid, which eliminates the need to ensure no duplicate matching entries are ever created. This feature is already used by local_flush_tlb_all(), via the UNIQUE_ENTRYHI macro, making the preceding call to r4k_tlb_uniquify() superfluous. The next change will also modify uniquification code such that it'll become incompatible with the FTLB and MMID features, as well as MIPSr6 CPUs that do not implement 4KiB pages. Therefore prevent r4k_tlb_uniquify() from being used on EHINV hardware, as denoted by `cpu_has_tlbinv'. Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/tlb-r4k.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 44a662536148..27bb64469604 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -640,7 +640,8 @@ static void r4k_tlb_configure(void) temp_tlb_entry = current_cpu_data.tlbsize - 1; /* From this point on the ARC firmware is dead. */ - r4k_tlb_uniquify(); + if (!cpu_has_tlbinv) + r4k_tlb_uniquify(); local_flush_tlb_all(); /* Did I tell you that ARC SUCKS? */ From 540760b77b8fc49d39d1b2b76196e5ec57711a32 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 18:57:30 +0000 Subject: [PATCH 616/712] MIPS: mm: Rewrite TLB uniquification for the hidden bit feature Before the introduction of the EHINV feature, which lets software mark TLB entries invalid, certain older implementations of the MIPS ISA were equipped with an analogous bit, as a vendor extension, which however is hidden from software and only ever set at reset, and then any software write clears it, making the intended TLB entry valid. This feature makes it unsafe to read a TLB entry with TLBR, modify the page mask, and write the entry back with TLBWI, because this operation will implicitly clear the hidden bit and this may create a duplicate entry, as with the presence of the hidden bit there is no guarantee all the entries across the TLB are unique each. Usually the firmware has already uniquified TLB entries before handing control over, in which case we only need to guarantee at bootstrap no clash will happen with the VPN2 values chosen in local_flush_tlb_all(). However with systems such as Mikrotik RB532 we get handed the TLB as at reset, with the hidden bit set across the entries and possibly duplicate entries present. This then causes a machine check exception when page sizes are reset in r4k_tlb_uniquify() and prevents the system from booting. Rewrite the algorithm used in r4k_tlb_uniquify() then such as to avoid the reuse of ASID/VPN values across the TLB. Get rid of global entries first as they may be blocking the entire address space, e.g. 16 256MiB pages will exhaust the whole address space of a 32-bit CPU and a single big page can exhaust the 32-bit compatibility space on a 64-bit CPU. Details of the algorithm chosen are given across the code itself. Fixes: 9f048fa48740 ("MIPS: mm: Prevent a TLB shutdown on initial uniquification") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/tlb-r4k.c | 282 +++++++++++++++++++++++++++++++++-------- 1 file changed, 228 insertions(+), 54 deletions(-) diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 27bb64469604..08c3e65bf0f2 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -511,12 +513,229 @@ static int __init set_ntlb(char *str) __setup("ntlb=", set_ntlb); -/* Comparison function for EntryHi VPN fields. */ -static int r4k_vpn_cmp(const void *a, const void *b) +/* The start bit position of VPN2 and Mask in EntryHi/PageMask registers. */ +#define VPN2_SHIFT 13 + +/* Read full EntryHi even with CONFIG_32BIT. */ +static inline unsigned long long read_c0_entryhi_native(void) { - long v = *(unsigned long *)a - *(unsigned long *)b; - int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0; - return s ? (v != 0) | v >> s : v; + return cpu_has_64bits ? read_c0_entryhi_64() : read_c0_entryhi(); +} + +/* Write full EntryHi even with CONFIG_32BIT. */ +static inline void write_c0_entryhi_native(unsigned long long v) +{ + if (cpu_has_64bits) + write_c0_entryhi_64(v); + else + write_c0_entryhi(v); +} + +/* TLB entry state for uniquification. */ +struct tlbent { + unsigned long long wired:1; + unsigned long long global:1; + unsigned long long asid:10; + unsigned long long vpn:51; + unsigned long long pagesz:5; + unsigned long long index:14; +}; + +/* + * Comparison function for TLB entry sorting. Place wired entries first, + * then global entries, then order by the increasing VPN/ASID and the + * decreasing page size. This lets us avoid clashes with wired entries + * easily and get entries for larger pages out of the way first. + * + * We could group bits so as to reduce the number of comparisons, but this + * is seldom executed and not performance-critical, so prefer legibility. + */ +static int r4k_entry_cmp(const void *a, const void *b) +{ + struct tlbent ea = *(struct tlbent *)a, eb = *(struct tlbent *)b; + + if (ea.wired > eb.wired) + return -1; + else if (ea.wired < eb.wired) + return 1; + else if (ea.global > eb.global) + return -1; + else if (ea.global < eb.global) + return 1; + else if (ea.vpn < eb.vpn) + return -1; + else if (ea.vpn > eb.vpn) + return 1; + else if (ea.asid < eb.asid) + return -1; + else if (ea.asid > eb.asid) + return 1; + else if (ea.pagesz > eb.pagesz) + return -1; + else if (ea.pagesz < eb.pagesz) + return 1; + else + return 0; +} + +/* + * Fetch all the TLB entries. Mask individual VPN values retrieved with + * the corresponding page mask and ignoring any 1KiB extension as we'll + * be using 4KiB pages for uniquification. + */ +static void __ref r4k_tlb_uniquify_read(struct tlbent *tlb_vpns, int tlbsize) +{ + int start = num_wired_entries(); + unsigned long long vpn_mask; + bool global; + int i; + + vpn_mask = GENMASK(current_cpu_data.vmbits - 1, VPN2_SHIFT); + vpn_mask |= cpu_has_64bits ? 3ULL << 62 : 1 << 31; + + for (i = 0; i < tlbsize; i++) { + unsigned long long entryhi, vpn, mask, asid; + unsigned int pagesz; + + write_c0_index(i); + mtc0_tlbr_hazard(); + tlb_read(); + tlb_read_hazard(); + + global = !!(read_c0_entrylo0() & ENTRYLO_G); + entryhi = read_c0_entryhi_native(); + mask = read_c0_pagemask(); + + asid = entryhi & cpu_asid_mask(¤t_cpu_data); + vpn = (entryhi & vpn_mask & ~mask) >> VPN2_SHIFT; + pagesz = ilog2((mask >> VPN2_SHIFT) + 1); + + tlb_vpns[i].global = global; + tlb_vpns[i].asid = global ? 0 : asid; + tlb_vpns[i].vpn = vpn; + tlb_vpns[i].pagesz = pagesz; + tlb_vpns[i].wired = i < start; + tlb_vpns[i].index = i; + } +} + +/* + * Write unique values to all but the wired TLB entries each, using + * the 4KiB page size. This size might not be supported with R6, but + * EHINV is mandatory for R6, so we won't ever be called in that case. + * + * A sorted table is supplied with any wired entries at the beginning, + * followed by any global entries, and then finally regular entries. + * We start at the VPN and ASID values of zero and only assign user + * addresses, therefore guaranteeing no clash with addresses produced + * by UNIQUE_ENTRYHI. We avoid any VPN values used by wired or global + * entries, by increasing the VPN value beyond the span of such entry. + * + * When a VPN/ASID clash is found with a regular entry we increment the + * ASID instead until no VPN/ASID clash has been found or the ASID space + * has been exhausted, in which case we increase the VPN value beyond + * the span of the largest clashing entry. + * + * We do not need to be concerned about FTLB or MMID configurations as + * those are required to implement the EHINV feature. + */ +static void __ref r4k_tlb_uniquify_write(struct tlbent *tlb_vpns, int tlbsize) +{ + unsigned long long asid, vpn, vpn_size, pagesz; + int widx, gidx, idx, sidx, lidx, i; + + vpn_size = 1ULL << (current_cpu_data.vmbits - VPN2_SHIFT); + pagesz = ilog2((PM_4K >> VPN2_SHIFT) + 1); + + write_c0_pagemask(PM_4K); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + + asid = 0; + vpn = 0; + widx = 0; + gidx = 0; + for (sidx = 0; sidx < tlbsize && tlb_vpns[sidx].wired; sidx++) + ; + for (lidx = sidx; lidx < tlbsize && tlb_vpns[lidx].global; lidx++) + ; + idx = gidx = sidx + 1; + for (i = sidx; i < tlbsize; i++) { + unsigned long long entryhi, vpn_pagesz = 0; + + while (1) { + if (WARN_ON(vpn >= vpn_size)) { + dump_tlb_all(); + /* Pray local_flush_tlb_all() will cope. */ + return; + } + + /* VPN must be below the next wired entry. */ + if (widx < sidx && vpn >= tlb_vpns[widx].vpn) { + vpn = max(vpn, + (tlb_vpns[widx].vpn + + (1ULL << tlb_vpns[widx].pagesz))); + asid = 0; + widx++; + continue; + } + /* VPN must be below the next global entry. */ + if (gidx < lidx && vpn >= tlb_vpns[gidx].vpn) { + vpn = max(vpn, + (tlb_vpns[gidx].vpn + + (1ULL << tlb_vpns[gidx].pagesz))); + asid = 0; + gidx++; + continue; + } + /* Try to find a free ASID so as to conserve VPNs. */ + if (idx < tlbsize && vpn == tlb_vpns[idx].vpn && + asid == tlb_vpns[idx].asid) { + unsigned long long idx_pagesz; + + idx_pagesz = tlb_vpns[idx].pagesz; + vpn_pagesz = max(vpn_pagesz, idx_pagesz); + do + idx++; + while (idx < tlbsize && + vpn == tlb_vpns[idx].vpn && + asid == tlb_vpns[idx].asid); + asid++; + if (asid > cpu_asid_mask(¤t_cpu_data)) { + vpn += vpn_pagesz; + asid = 0; + vpn_pagesz = 0; + } + continue; + } + /* VPN mustn't be above the next regular entry. */ + if (idx < tlbsize && vpn > tlb_vpns[idx].vpn) { + vpn = max(vpn, + (tlb_vpns[idx].vpn + + (1ULL << tlb_vpns[idx].pagesz))); + asid = 0; + idx++; + continue; + } + break; + } + + entryhi = (vpn << VPN2_SHIFT) | asid; + write_c0_entryhi_native(entryhi); + write_c0_index(tlb_vpns[i].index); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + + tlb_vpns[i].asid = asid; + tlb_vpns[i].vpn = vpn; + tlb_vpns[i].pagesz = pagesz; + + asid++; + if (asid > cpu_asid_mask(¤t_cpu_data)) { + vpn += 1ULL << pagesz; + asid = 0; + } + } } /* @@ -527,14 +746,8 @@ static void __ref r4k_tlb_uniquify(void) { int tlbsize = current_cpu_data.tlbsize; bool use_slab = slab_is_available(); - int start = num_wired_entries(); phys_addr_t tlb_vpn_size; - unsigned long *tlb_vpns; - unsigned long vpn_mask; - int cnt, ent, idx, i; - - vpn_mask = GENMASK(cpu_vmbits - 1, 13); - vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31; + struct tlbent *tlb_vpns; tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); tlb_vpns = (use_slab ? @@ -545,52 +758,13 @@ static void __ref r4k_tlb_uniquify(void) htw_stop(); - for (i = start, cnt = 0; i < tlbsize; i++, cnt++) { - unsigned long vpn; + r4k_tlb_uniquify_read(tlb_vpns, tlbsize); - write_c0_index(i); - mtc0_tlbr_hazard(); - tlb_read(); - tlb_read_hazard(); - vpn = read_c0_entryhi(); - vpn &= vpn_mask & PAGE_MASK; - tlb_vpns[cnt] = vpn; + sort(tlb_vpns, tlbsize, sizeof(*tlb_vpns), r4k_entry_cmp, NULL); - /* Prevent any large pages from overlapping regular ones. */ - write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK); - mtc0_tlbw_hazard(); - tlb_write_indexed(); - tlbw_use_hazard(); - } - - sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL); + r4k_tlb_uniquify_write(tlb_vpns, tlbsize); write_c0_pagemask(PM_DEFAULT_MASK); - write_c0_entrylo0(0); - write_c0_entrylo1(0); - - idx = 0; - ent = tlbsize; - for (i = start; i < tlbsize; i++) - while (1) { - unsigned long entryhi, vpn; - - entryhi = UNIQUE_ENTRYHI(ent); - vpn = entryhi & vpn_mask & PAGE_MASK; - - if (idx >= cnt || vpn < tlb_vpns[idx]) { - write_c0_entryhi(entryhi); - write_c0_index(i); - mtc0_tlbw_hazard(); - tlb_write_indexed(); - ent++; - break; - } else if (vpn == tlb_vpns[idx]) { - ent++; - } else { - idx++; - } - } tlbw_use_hazard(); htw_start(); From 01cc50ea5167bb14117257ec084637abe9e5f691 Mon Sep 17 00:00:00 2001 From: Stefan Wiehler Date: Tue, 10 Mar 2026 11:40:24 +0100 Subject: [PATCH 617/712] mips: mm: Allocate tlb_vpn array atomically Found by DEBUG_ATOMIC_SLEEP: BUG: sleeping function called from invalid context at /include/linux/sched/mm.h:306 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/1 preempt_count: 1, expected: 0 RCU nest depth: 0, expected: 0 no locks held by swapper/1/0. irq event stamp: 0 hardirqs last enabled at (0): [<0000000000000000>] 0x0 hardirqs last disabled at (0): [] copy_process+0x75c/0x1b68 softirqs last enabled at (0): [] copy_process+0x75c/0x1b68 softirqs last disabled at (0): [<0000000000000000>] 0x0 CPU: 1 PID: 0 Comm: swapper/1 Not tainted 6.6.119-d79e757675ec-fct #1 Stack : 800000000290bad8 0000000000000000 0000000000000008 800000000290bae8 800000000290bae8 800000000290bc78 0000000000000000 0000000000000000 ffffffff80c80000 0000000000000001 ffffffff80d8dee8 ffffffff810d09c0 784bb2a7ec10647d 0000000000000010 ffffffff80a6fd60 8000000001d8a9c0 0000000000000000 0000000000000000 ffffffff80d90000 0000000000000000 ffffffff80c9e0e8 0000000007ffffff 0000000000000cc0 0000000000000400 ffffffffffffffff 0000000000000001 0000000000000002 ffffffffc0149ed8 fffffffffffffffe 8000000002908000 800000000290bae0 ffffffff80a81b74 ffffffff80129fb0 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 ffffffff80129fd0 0000000000000000 ... Call Trace: [] show_stack+0x60/0x158 [] dump_stack_lvl+0x88/0xbc [] __might_resched+0x268/0x288 [] __kmem_cache_alloc_node+0x2e0/0x330 [] __kmalloc+0x58/0xd0 [] r4k_tlb_uniquify+0x7c/0x428 [] tlb_init+0x7c/0x110 [] per_cpu_trap_init+0x16c/0x1d0 [] start_secondary+0x28/0x128 Fixes: 231ac951faba ("MIPS: mm: kmalloc tlb_vpn array to avoid stack overflow") Signed-off-by: Stefan Wiehler Cc: stable@vger.kernel.org Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/tlb-r4k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 08c3e65bf0f2..24fe85fa169d 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -751,7 +751,7 @@ static void __ref r4k_tlb_uniquify(void) tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); tlb_vpns = (use_slab ? - kmalloc(tlb_vpn_size, GFP_KERNEL) : + kmalloc(tlb_vpn_size, GFP_ATOMIC) : memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns))); if (WARN_ON(!tlb_vpns)) return; /* Pray local_flush_tlb_all() is good enough. */ From a834a0b66ec6fb743377201a0f4229bb2503f4ce Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 25 Mar 2026 21:07:46 +0200 Subject: [PATCH 618/712] Bluetooth: hci_sync: call destroy in hci_cmd_sync_run if immediate hci_cmd_sync_run() may run the work immediately if called from existing sync work (otherwise it queues a new sync work). In this case it fails to call the destroy() function. On immediate run, make it behave same way as if item was queued successfully: call destroy, and return 0. The only callsite is hci_abort_conn() via hci_cmd_sync_run_once(), and this changes its return value. However, its return value is not used except as the return value for hci_disconnect(), and nothing uses the return value of hci_disconnect(). Hence there should be no behavior change anywhere. Fixes: c898f6d7b093b ("Bluetooth: hci_sync: Introduce hci_cmd_sync_run/hci_cmd_sync_run_once") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 45d16639874a..6283a4df78b0 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -801,8 +801,15 @@ int hci_cmd_sync_run(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, return -ENETDOWN; /* If on cmd_sync_work then run immediately otherwise queue */ - if (current_work() == &hdev->cmd_sync_work) - return func(hdev, data); + if (current_work() == &hdev->cmd_sync_work) { + int err; + + err = func(hdev, data); + if (destroy) + destroy(hdev, data, err); + + return 0; + } return hci_cmd_sync_submit(hdev, func, data, destroy); } From 8a5b0135d4a5d9683203a3d9a12a711ccec5936b Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 26 Mar 2026 23:16:45 +0800 Subject: [PATCH 619/712] Bluetooth: SCO: fix race conditions in sco_sock_connect() sco_sock_connect() checks sk_state and sk_type without holding the socket lock. Two concurrent connect() syscalls on the same socket can both pass the check and enter sco_connect(), leading to use-after-free. The buggy scenario involves three participants and was confirmed with additional logging instrumentation: Thread A (connect): HCI disconnect: Thread B (connect): sco_sock_connect(sk) sco_sock_connect(sk) sk_state==BT_OPEN sk_state==BT_OPEN (pass, no lock) (pass, no lock) sco_connect(sk): sco_connect(sk): hci_dev_lock hci_dev_lock hci_connect_sco <- blocked -> hcon1 sco_conn_add->conn1 lock_sock(sk) sco_chan_add: conn1->sk = sk sk->conn = conn1 sk_state=BT_CONNECT release_sock hci_dev_unlock hci_dev_lock sco_conn_del: lock_sock(sk) sco_chan_del: sk->conn=NULL conn1->sk=NULL sk_state= BT_CLOSED SOCK_ZAPPED release_sock hci_dev_unlock (unblocked) hci_connect_sco -> hcon2 sco_conn_add -> conn2 lock_sock(sk) sco_chan_add: sk->conn=conn2 sk_state= BT_CONNECT // zombie sk! release_sock hci_dev_unlock Thread B revives a BT_CLOSED + SOCK_ZAPPED socket back to BT_CONNECT. Subsequent cleanup triggers double sock_put() and use-after-free. Meanwhile conn1 is leaked as it was orphaned when sco_conn_del() cleared the association. Fix this by: - Moving lock_sock() before the sk_state/sk_type checks in sco_sock_connect() to serialize concurrent connect attempts - Fixing the sk_type != SOCK_SEQPACKET check to actually return the error instead of just assigning it - Adding a state re-check in sco_connect() after lock_sock() to catch state changes during the window between the locks - Adding sco_pi(sk)->conn check in sco_chan_add() to prevent double-attach of a socket to multiple connections - Adding hci_conn_drop() on sco_chan_add failure to prevent HCI connection leaks Fixes: 9a8ec9e8ebb5 ("Bluetooth: SCO: Fix possible circular locking dependency on sco_connect_cfm") Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/sco.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 584e059de20a..b84587811ef4 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -298,7 +298,7 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk, int err = 0; sco_conn_lock(conn); - if (conn->sk) + if (conn->sk || sco_pi(sk)->conn) err = -EBUSY; else __sco_chan_add(conn, sk, parent); @@ -353,9 +353,20 @@ static int sco_connect(struct sock *sk) lock_sock(sk); + /* Recheck state after reacquiring the socket lock, as another + * thread may have changed it (e.g., closed the socket). + */ + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { + release_sock(sk); + hci_conn_drop(hcon); + err = -EBADFD; + goto unlock; + } + err = sco_chan_add(conn, sk, NULL); if (err) { release_sock(sk); + hci_conn_drop(hcon); goto unlock; } @@ -656,13 +667,18 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr_unsized *addr, addr->sa_family != AF_BLUETOOTH) return -EINVAL; - if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) - return -EBADFD; - - if (sk->sk_type != SOCK_SEQPACKET) - err = -EINVAL; - lock_sock(sk); + + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { + release_sock(sk); + return -EBADFD; + } + + if (sk->sk_type != SOCK_SEQPACKET) { + release_sock(sk); + return -EINVAL; + } + /* Set destination address and psm */ bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr); release_sock(sk); From 2b2bf47cd75518c36fa2d41380e4a40641cc89cd Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Thu, 26 Mar 2026 17:31:24 +0000 Subject: [PATCH 620/712] Bluetooth: hci_event: move wake reason storage into validated event handlers hci_store_wake_reason() is called from hci_event_packet() immediately after stripping the HCI event header but before hci_event_func() enforces the per-event minimum payload length from hci_ev_table. This means a short HCI event frame can reach bacpy() before any bounds check runs. Rather than duplicating skb parsing and per-event length checks inside hci_store_wake_reason(), move wake-address storage into the individual event handlers after their existing event-length validation has succeeded. Convert hci_store_wake_reason() into a small helper that only stores an already-validated bdaddr while the caller holds hci_dev_lock(). Use the same helper after hci_event_func() with a NULL address to preserve the existing unexpected-wake fallback semantics when no validated event handler records a wake address. Annotate the helper with __must_hold(&hdev->lock) and add lockdep_assert_held(&hdev->lock) so future call paths keep the lock contract explicit. Call the helper from hci_conn_request_evt(), hci_conn_complete_evt(), hci_sync_conn_complete_evt(), le_conn_complete_evt(), hci_le_adv_report_evt(), hci_le_ext_adv_report_evt(), hci_le_direct_adv_report_evt(), hci_le_pa_sync_established_evt(), and hci_le_past_received_evt(). Fixes: 2f20216c1d6f ("Bluetooth: Emit controller suspend and resume events") Cc: stable@vger.kernel.org Signed-off-by: Oleh Konko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 94 +++++++++++++++------------------------ 1 file changed, 35 insertions(+), 59 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 286529d2e554..81d2f9a3eec9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -80,6 +80,10 @@ static void *hci_le_ev_skb_pull(struct hci_dev *hdev, struct sk_buff *skb, return data; } +static void hci_store_wake_reason(struct hci_dev *hdev, + const bdaddr_t *bdaddr, u8 addr_type) + __must_hold(&hdev->lock); + static u8 hci_cc_inquiry_cancel(struct hci_dev *hdev, void *data, struct sk_buff *skb) { @@ -3111,6 +3115,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR); /* Check for existing connection: * @@ -3274,6 +3279,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "bdaddr %pMR type 0x%x", &ev->bdaddr, ev->link_type); + hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR); + hci_dev_unlock(hdev); + /* Reject incoming connection from device with same BD ADDR against * CVE-2020-26555 */ @@ -5021,6 +5030,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { @@ -5713,6 +5723,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, int err; hci_dev_lock(hdev); + hci_store_wake_reason(hdev, bdaddr, bdaddr_type); /* All controllers implicitly stop advertising in the event of a * connection, so ensure that the state bit is cleared. @@ -6005,6 +6016,7 @@ static void hci_le_past_received_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type); hci_dev_clear_flag(hdev, HCI_PA_SYNC); @@ -6403,6 +6415,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data, info->length + 1)) break; + hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type); + if (info->length <= max_adv_len(hdev)) { rssi = info->data[info->length]; process_adv_report(hdev, info->type, &info->bdaddr, @@ -6491,6 +6505,8 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data, info->length)) break; + hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type); + evt_type = __le16_to_cpu(info->type) & LE_EXT_ADV_EVT_TYPE_MASK; legacy_evt_type = ext_evt_type_to_legacy(hdev, evt_type); @@ -6536,6 +6552,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type); hci_dev_clear_flag(hdev, HCI_PA_SYNC); @@ -6834,6 +6851,8 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data, for (i = 0; i < ev->num; i++) { struct hci_ev_le_direct_adv_info *info = &ev->info[i]; + hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type); + process_adv_report(hdev, info->type, &info->bdaddr, info->bdaddr_type, &info->direct_addr, info->direct_addr_type, HCI_ADV_PHY_1M, 0, @@ -7517,73 +7536,29 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, return true; } -static void hci_store_wake_reason(struct hci_dev *hdev, u8 event, - struct sk_buff *skb) +static void hci_store_wake_reason(struct hci_dev *hdev, + const bdaddr_t *bdaddr, u8 addr_type) + __must_hold(&hdev->lock) { - struct hci_ev_le_advertising_info *adv; - struct hci_ev_le_direct_adv_info *direct_adv; - struct hci_ev_le_ext_adv_info *ext_adv; - const struct hci_ev_conn_complete *conn_complete = (void *)skb->data; - const struct hci_ev_conn_request *conn_request = (void *)skb->data; - - hci_dev_lock(hdev); + lockdep_assert_held(&hdev->lock); /* If we are currently suspended and this is the first BT event seen, * save the wake reason associated with the event. */ if (!hdev->suspended || hdev->wake_reason) - goto unlock; + return; + + if (!bdaddr) { + hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED; + return; + } /* Default to remote wake. Values for wake_reason are documented in the * Bluez mgmt api docs. */ hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE; - - /* Once configured for remote wakeup, we should only wake up for - * reconnections. It's useful to see which device is waking us up so - * keep track of the bdaddr of the connection event that woke us up. - */ - if (event == HCI_EV_CONN_REQUEST) { - bacpy(&hdev->wake_addr, &conn_request->bdaddr); - hdev->wake_addr_type = BDADDR_BREDR; - } else if (event == HCI_EV_CONN_COMPLETE) { - bacpy(&hdev->wake_addr, &conn_complete->bdaddr); - hdev->wake_addr_type = BDADDR_BREDR; - } else if (event == HCI_EV_LE_META) { - struct hci_ev_le_meta *le_ev = (void *)skb->data; - u8 subevent = le_ev->subevent; - u8 *ptr = &skb->data[sizeof(*le_ev)]; - u8 num_reports = *ptr; - - if ((subevent == HCI_EV_LE_ADVERTISING_REPORT || - subevent == HCI_EV_LE_DIRECT_ADV_REPORT || - subevent == HCI_EV_LE_EXT_ADV_REPORT) && - num_reports) { - adv = (void *)(ptr + 1); - direct_adv = (void *)(ptr + 1); - ext_adv = (void *)(ptr + 1); - - switch (subevent) { - case HCI_EV_LE_ADVERTISING_REPORT: - bacpy(&hdev->wake_addr, &adv->bdaddr); - hdev->wake_addr_type = adv->bdaddr_type; - break; - case HCI_EV_LE_DIRECT_ADV_REPORT: - bacpy(&hdev->wake_addr, &direct_adv->bdaddr); - hdev->wake_addr_type = direct_adv->bdaddr_type; - break; - case HCI_EV_LE_EXT_ADV_REPORT: - bacpy(&hdev->wake_addr, &ext_adv->bdaddr); - hdev->wake_addr_type = ext_adv->bdaddr_type; - break; - } - } - } else { - hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED; - } - -unlock: - hci_dev_unlock(hdev); + bacpy(&hdev->wake_addr, bdaddr); + hdev->wake_addr_type = addr_type; } #define HCI_EV_VL(_op, _func, _min_len, _max_len) \ @@ -7830,14 +7805,15 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) skb_pull(skb, HCI_EVENT_HDR_SIZE); - /* Store wake reason if we're suspended */ - hci_store_wake_reason(hdev, event, skb); - bt_dev_dbg(hdev, "event 0x%2.2x", event); hci_event_func(hdev, event, skb, &opcode, &status, &req_complete, &req_complete_skb); + hci_dev_lock(hdev); + hci_store_wake_reason(hdev, NULL, 0); + hci_dev_unlock(hdev); + if (req_complete) { req_complete(hdev, status, opcode); } else if (req_complete_skb) { From 2969554bcfccb5c609f6b6cd4a014933f3a66dd0 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 25 Mar 2026 21:07:43 +0200 Subject: [PATCH 621/712] Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists hci_cmd_sync_queue_once() needs to indicate whether a queue item was added, so caller can know if callbacks are called, so it can avoid leaking resources. Change the function to return -EEXIST if queue item already exists. Modify all callsites to handle that. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 53 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 6283a4df78b0..97745710e3ce 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -780,7 +780,7 @@ int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, void *data, hci_cmd_sync_work_destroy_t destroy) { if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy)) - return 0; + return -EEXIST; return hci_cmd_sync_queue(hdev, func, data, destroy); } @@ -3262,6 +3262,8 @@ static int update_passive_scan_sync(struct hci_dev *hdev, void *data) int hci_update_passive_scan(struct hci_dev *hdev) { + int err; + /* Only queue if it would have any effect */ if (!test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || @@ -3271,8 +3273,9 @@ int hci_update_passive_scan(struct hci_dev *hdev) hci_dev_test_flag(hdev, HCI_UNREGISTER)) return 0; - return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL, - NULL); + err = hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL, + NULL); + return (err == -EEXIST) ? 0 : err; } int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val) @@ -6965,8 +6968,11 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn, - NULL); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn, + NULL); + return (err == -EEXIST) ? 0 : err; } static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) @@ -7002,8 +7008,11 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn, - create_le_conn_complete); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn, + create_le_conn_complete); + return (err == -EEXIST) ? 0 : err; } int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) @@ -7210,8 +7219,11 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data) int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn, - create_pa_complete); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn, + create_pa_complete); + return (err == -EEXIST) ? 0 : err; } static void create_big_complete(struct hci_dev *hdev, void *data, int err) @@ -7273,8 +7285,11 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data) int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn, - create_big_complete); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn, + create_big_complete); + return (err == -EEXIST) ? 0 : err; } struct past_data { @@ -7366,7 +7381,7 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le) if (err) kfree(data); - return err; + return (err == -EEXIST) ? 0 : err; } static void le_read_features_complete(struct hci_dev *hdev, void *data, int err) @@ -7453,7 +7468,7 @@ int hci_le_read_remote_features(struct hci_conn *conn) else err = -EOPNOTSUPP; - return err; + return (err == -EEXIST) ? 0 : err; } static void pkt_type_changed(struct hci_dev *hdev, void *data, int err) @@ -7479,6 +7494,7 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) { struct hci_dev *hdev = conn->hdev; struct hci_cp_change_conn_ptype *cp; + int err; cp = kmalloc_obj(*cp); if (!cp) @@ -7487,8 +7503,9 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) cp->handle = cpu_to_le16(conn->handle); cp->pkt_type = cpu_to_le16(pkt_type); - return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, - pkt_type_changed); + err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, + pkt_type_changed); + return (err == -EEXIST) ? 0 : err; } static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err) @@ -7514,6 +7531,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) { struct hci_dev *hdev = conn->hdev; struct hci_cp_le_set_phy *cp; + int err; cp = kmalloc_obj(*cp); if (!cp) @@ -7524,6 +7542,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) cp->tx_phys = tx_phys; cp->rx_phys = rx_phys; - return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, - le_phy_update_complete); + err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, + le_phy_update_complete); + return (err == -EEXIST) ? 0 : err; } From aca377208e7f7322bf4e107cdec6e7d7e8aa7a88 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 25 Mar 2026 21:07:44 +0200 Subject: [PATCH 622/712] Bluetooth: hci_sync: fix leaks when hci_cmd_sync_queue_once fails When hci_cmd_sync_queue_once() returns with error, the destroy callback will not be called. Fix leaking references / memory on these failures. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 97745710e3ce..8cbbba50e77e 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7460,13 +7460,16 @@ int hci_le_read_remote_features(struct hci_conn *conn) * role is possible. Otherwise just transition into the * connected state without requesting the remote features. */ - if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) + if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) { err = hci_cmd_sync_queue_once(hdev, hci_le_read_remote_features_sync, hci_conn_hold(conn), le_read_features_complete); - else + if (err) + hci_conn_drop(conn); + } else { err = -EOPNOTSUPP; + } return (err == -EEXIST) ? 0 : err; } @@ -7505,6 +7508,9 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, pkt_type_changed); + if (err) + kfree(cp); + return (err == -EEXIST) ? 0 : err; } @@ -7544,5 +7550,8 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, le_phy_update_complete); + if (err) + kfree(cp); + return (err == -EEXIST) ? 0 : err; } From 035c25007c9e698bef3826070ee34bb6d778020c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 25 Mar 2026 11:11:46 -0400 Subject: [PATCH 623/712] Bluetooth: hci_sync: Fix UAF in le_read_features_complete This fixes the following backtrace caused by hci_conn being freed before le_read_features_complete but after hci_le_read_remote_features_sync so hci_conn_del -> hci_cmd_sync_dequeue is not able to prevent it: ================================================================== BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:96 [inline] BUG: KASAN: slab-use-after-free in atomic_dec_and_test include/linux/atomic/atomic-instrumented.h:1383 [inline] BUG: KASAN: slab-use-after-free in hci_conn_drop include/net/bluetooth/hci_core.h:1688 [inline] BUG: KASAN: slab-use-after-free in le_read_features_complete+0x5b/0x340 net/bluetooth/hci_sync.c:7344 Write of size 4 at addr ffff8880796b0010 by task kworker/u9:0/52 CPU: 0 UID: 0 PID: 52 Comm: kworker/u9:0 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025 Workqueue: hci0 hci_cmd_sync_work Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xcd/0x630 mm/kasan/report.c:482 kasan_report+0xe0/0x110 mm/kasan/report.c:595 check_region_inline mm/kasan/generic.c:194 [inline] kasan_check_range+0x100/0x1b0 mm/kasan/generic.c:200 instrument_atomic_read_write include/linux/instrumented.h:96 [inline] atomic_dec_and_test include/linux/atomic/atomic-instrumented.h:1383 [inline] hci_conn_drop include/net/bluetooth/hci_core.h:1688 [inline] le_read_features_complete+0x5b/0x340 net/bluetooth/hci_sync.c:7344 hci_cmd_sync_work+0x1ff/0x430 net/bluetooth/hci_sync.c:334 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257 process_scheduled_works kernel/workqueue.c:3340 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421 kthread+0x3c5/0x780 kernel/kthread.c:463 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 Allocated by task 5932: kasan_save_stack+0x33/0x60 mm/kasan/common.c:56 kasan_save_track+0x14/0x30 mm/kasan/common.c:77 poison_kmalloc_redzone mm/kasan/common.c:400 [inline] __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:417 kmalloc_noprof include/linux/slab.h:957 [inline] kzalloc_noprof include/linux/slab.h:1094 [inline] __hci_conn_add+0xf8/0x1c70 net/bluetooth/hci_conn.c:963 hci_conn_add_unset+0x76/0x100 net/bluetooth/hci_conn.c:1084 le_conn_complete_evt+0x639/0x1f20 net/bluetooth/hci_event.c:5714 hci_le_enh_conn_complete_evt+0x23d/0x380 net/bluetooth/hci_event.c:5861 hci_le_meta_evt+0x357/0x5e0 net/bluetooth/hci_event.c:7408 hci_event_func net/bluetooth/hci_event.c:7716 [inline] hci_event_packet+0x685/0x11c0 net/bluetooth/hci_event.c:7773 hci_rx_work+0x2c9/0xeb0 net/bluetooth/hci_core.c:4076 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257 process_scheduled_works kernel/workqueue.c:3340 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421 kthread+0x3c5/0x780 kernel/kthread.c:463 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 Freed by task 5932: kasan_save_stack+0x33/0x60 mm/kasan/common.c:56 kasan_save_track+0x14/0x30 mm/kasan/common.c:77 __kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:587 kasan_save_free_info mm/kasan/kasan.h:406 [inline] poison_slab_object mm/kasan/common.c:252 [inline] __kasan_slab_free+0x5f/0x80 mm/kasan/common.c:284 kasan_slab_free include/linux/kasan.h:234 [inline] slab_free_hook mm/slub.c:2540 [inline] slab_free mm/slub.c:6663 [inline] kfree+0x2f8/0x6e0 mm/slub.c:6871 device_release+0xa4/0x240 drivers/base/core.c:2565 kobject_cleanup lib/kobject.c:689 [inline] kobject_release lib/kobject.c:720 [inline] kref_put include/linux/kref.h:65 [inline] kobject_put+0x1e7/0x590 lib/kobject.c:737 put_device drivers/base/core.c:3797 [inline] device_unregister+0x2f/0xc0 drivers/base/core.c:3920 hci_conn_del_sysfs+0xb4/0x180 net/bluetooth/hci_sysfs.c:79 hci_conn_cleanup net/bluetooth/hci_conn.c:173 [inline] hci_conn_del+0x657/0x1180 net/bluetooth/hci_conn.c:1234 hci_disconn_complete_evt+0x410/0xa00 net/bluetooth/hci_event.c:3451 hci_event_func net/bluetooth/hci_event.c:7719 [inline] hci_event_packet+0xa10/0x11c0 net/bluetooth/hci_event.c:7773 hci_rx_work+0x2c9/0xeb0 net/bluetooth/hci_core.c:4076 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257 process_scheduled_works kernel/workqueue.c:3340 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421 kthread+0x3c5/0x780 kernel/kthread.c:463 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 The buggy address belongs to the object at ffff8880796b0000 which belongs to the cache kmalloc-8k of size 8192 The buggy address is located 16 bytes inside of freed 8192-byte region [ffff8880796b0000, ffff8880796b2000) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x796b0 head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 anon flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff) page_type: f5(slab) raw: 00fff00000000040 ffff88813ff27280 0000000000000000 0000000000000001 raw: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000 head: 00fff00000000040 ffff88813ff27280 0000000000000000 0000000000000001 head: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000 head: 00fff00000000003 ffffea0001e5ac01 00000000ffffffff 00000000ffffffff head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd2040(__GFP_IO|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5657, tgid 5657 (dhcpcd-run-hook), ts 79819636908, free_ts 79814310558 set_page_owner include/linux/page_owner.h:32 [inline] post_alloc_hook+0x1af/0x220 mm/page_alloc.c:1845 prep_new_page mm/page_alloc.c:1853 [inline] get_page_from_freelist+0xd0b/0x31a0 mm/page_alloc.c:3879 __alloc_frozen_pages_noprof+0x25f/0x2440 mm/page_alloc.c:5183 alloc_pages_mpol+0x1fb/0x550 mm/mempolicy.c:2416 alloc_slab_page mm/slub.c:3075 [inline] allocate_slab mm/slub.c:3248 [inline] new_slab+0x2c3/0x430 mm/slub.c:3302 ___slab_alloc+0xe18/0x1c90 mm/slub.c:4651 __slab_alloc.constprop.0+0x63/0x110 mm/slub.c:4774 __slab_alloc_node mm/slub.c:4850 [inline] slab_alloc_node mm/slub.c:5246 [inline] __kmalloc_cache_noprof+0x477/0x800 mm/slub.c:5766 kmalloc_noprof include/linux/slab.h:957 [inline] kzalloc_noprof include/linux/slab.h:1094 [inline] tomoyo_print_bprm security/tomoyo/audit.c:26 [inline] tomoyo_init_log+0xc8a/0x2140 security/tomoyo/audit.c:264 tomoyo_supervisor+0x302/0x13b0 security/tomoyo/common.c:2198 tomoyo_audit_env_log security/tomoyo/environ.c:36 [inline] tomoyo_env_perm+0x191/0x200 security/tomoyo/environ.c:63 tomoyo_environ security/tomoyo/domain.c:672 [inline] tomoyo_find_next_domain+0xec1/0x20b0 security/tomoyo/domain.c:888 tomoyo_bprm_check_security security/tomoyo/tomoyo.c:102 [inline] tomoyo_bprm_check_security+0x12d/0x1d0 security/tomoyo/tomoyo.c:92 security_bprm_check+0x1b9/0x1e0 security/security.c:794 search_binary_handler fs/exec.c:1659 [inline] exec_binprm fs/exec.c:1701 [inline] bprm_execve fs/exec.c:1753 [inline] bprm_execve+0x81e/0x1620 fs/exec.c:1729 do_execveat_common.isra.0+0x4a5/0x610 fs/exec.c:1859 page last free pid 5657 tgid 5657 stack trace: reset_page_owner include/linux/page_owner.h:25 [inline] free_pages_prepare mm/page_alloc.c:1394 [inline] __free_frozen_pages+0x7df/0x1160 mm/page_alloc.c:2901 discard_slab mm/slub.c:3346 [inline] __put_partials+0x130/0x170 mm/slub.c:3886 qlink_free mm/kasan/quarantine.c:163 [inline] qlist_free_all+0x4c/0xf0 mm/kasan/quarantine.c:179 kasan_quarantine_reduce+0x195/0x1e0 mm/kasan/quarantine.c:286 __kasan_slab_alloc+0x69/0x90 mm/kasan/common.c:352 kasan_slab_alloc include/linux/kasan.h:252 [inline] slab_post_alloc_hook mm/slub.c:4948 [inline] slab_alloc_node mm/slub.c:5258 [inline] __kmalloc_cache_noprof+0x274/0x800 mm/slub.c:5766 kmalloc_noprof include/linux/slab.h:957 [inline] tomoyo_print_header security/tomoyo/audit.c:156 [inline] tomoyo_init_log+0x197/0x2140 security/tomoyo/audit.c:255 tomoyo_supervisor+0x302/0x13b0 security/tomoyo/common.c:2198 tomoyo_audit_env_log security/tomoyo/environ.c:36 [inline] tomoyo_env_perm+0x191/0x200 security/tomoyo/environ.c:63 tomoyo_environ security/tomoyo/domain.c:672 [inline] tomoyo_find_next_domain+0xec1/0x20b0 security/tomoyo/domain.c:888 tomoyo_bprm_check_security security/tomoyo/tomoyo.c:102 [inline] tomoyo_bprm_check_security+0x12d/0x1d0 security/tomoyo/tomoyo.c:92 security_bprm_check+0x1b9/0x1e0 security/security.c:794 search_binary_handler fs/exec.c:1659 [inline] exec_binprm fs/exec.c:1701 [inline] bprm_execve fs/exec.c:1753 [inline] bprm_execve+0x81e/0x1620 fs/exec.c:1729 do_execveat_common.isra.0+0x4a5/0x610 fs/exec.c:1859 do_execve fs/exec.c:1933 [inline] __do_sys_execve fs/exec.c:2009 [inline] __se_sys_execve fs/exec.c:2004 [inline] __x64_sys_execve+0x8e/0xb0 fs/exec.c:2004 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xcd/0xf80 arch/x86/entry/syscall_64.c:94 Memory state around the buggy address: ffff8880796aff00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8880796aff80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff8880796b0000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff8880796b0080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8880796b0100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Fixes: a106e50be74b ("Bluetooth: HCI: Add support for LL Extended Feature Set") Reported-by: syzbot+87badbb9094e008e0685@syzkaller.appspotmail.com Tested-by: syzbot+87badbb9094e008e0685@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=87badbb9094e008e0685 Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Pauli Virtanen --- net/bluetooth/hci_sync.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 8cbbba50e77e..ffb0ceda6f7b 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7390,10 +7390,8 @@ static void le_read_features_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "err %d", err); - if (err == -ECANCELED) - return; - hci_conn_drop(conn); + hci_conn_put(conn); } static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev, @@ -7463,10 +7461,12 @@ int hci_le_read_remote_features(struct hci_conn *conn) if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) { err = hci_cmd_sync_queue_once(hdev, hci_le_read_remote_features_sync, - hci_conn_hold(conn), + hci_conn_hold(hci_conn_get(conn)), le_read_features_complete); - if (err) + if (err) { hci_conn_drop(conn); + hci_conn_put(conn); + } } else { err = -EOPNOTSUPP; } From 0ffac654e95c1bdfe2d4edf28fb18d6ba1f103e6 Mon Sep 17 00:00:00 2001 From: Jonathan Rissanen Date: Fri, 27 Mar 2026 11:47:21 +0100 Subject: [PATCH 624/712] Bluetooth: hci_h4: Fix race during initialization Commit 5df5dafc171b ("Bluetooth: hci_uart: Fix another race during initialization") fixed a race for hci commands sent during initialization. However, there is still a race that happens if an hci event from one of these commands is received before HCI_UART_REGISTERED has been set at the end of hci_uart_register_dev(). The event will be ignored which causes the command to fail with a timeout in the log: "Bluetooth: hci0: command 0x1003 tx timeout" This is because the hci event receive path (hci_uart_tty_receive -> h4_recv) requires HCI_UART_REGISTERED to be set in h4_recv(), while the hci command transmit path (hci_uart_send_frame -> h4_enqueue) only requires HCI_UART_PROTO_INIT to be set in hci_uart_send_frame(). The check for HCI_UART_REGISTERED was originally added in commit c2578202919a ("Bluetooth: Fix H4 crash from incoming UART packets") to fix a crash caused by hu->hdev being null dereferenced. That can no longer happen: once HCI_UART_PROTO_INIT is set in hci_uart_register_dev() all pointers (hu, hu->priv and hu->hdev) are valid, and hci_uart_tty_receive() already calls h4_recv() on HCI_UART_PROTO_INIT or HCI_UART_PROTO_READY. Remove the check for HCI_UART_REGISTERED in h4_recv() to fix the race condition. Fixes: 5df5dafc171b ("Bluetooth: hci_uart: Fix another race during initialization") Signed-off-by: Jonathan Rissanen Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_h4.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 07cc2a79a77b..a889a66a326f 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -109,9 +109,6 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count) { struct h4_struct *h4 = hu->priv; - if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) - return -EUNATCH; - h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count, h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); if (IS_ERR(h4->rx_skb)) { From b8dbe9648d69059cfe3a28917bfbf7e61efd7f15 Mon Sep 17 00:00:00 2001 From: Keenan Dong Date: Sat, 28 Mar 2026 16:46:47 +0800 Subject: [PATCH 625/712] Bluetooth: MGMT: validate LTK enc_size on load Load Long Term Keys stores the user-provided enc_size and later uses it to size fixed-size stack operations when replying to LE LTK requests. An enc_size larger than the 16-byte key buffer can therefore overflow the reply stack buffer. Reject oversized enc_size values while validating the management LTK record so invalid keys never reach the stored key state. Fixes: 346af67b8d11 ("Bluetooth: Add MGMT handlers for dealing with SMP LTK's") Reported-by: Keenan Dong Signed-off-by: Keenan Dong Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e5f9287fb826..adcd86c15b4e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7248,6 +7248,9 @@ static bool ltk_is_valid(struct mgmt_ltk_info *key) if (key->initiator != 0x00 && key->initiator != 0x01) return false; + if (key->enc_size > sizeof(key->val)) + return false; + switch (key->addr.type) { case BDADDR_LE_PUBLIC: return true; From a2639a7f0f5bf7d73f337f8f077c19415c62ed2c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 29 Mar 2026 16:43:01 +0300 Subject: [PATCH 626/712] Bluetooth: hci_conn: fix potential UAF in set_cig_params_sync hci_conn lookup and field access must be covered by hdev lock in set_cig_params_sync, otherwise it's possible it is freed concurrently. Take hdev lock to prevent hci_conn from being deleted or modified concurrently. Just RCU lock is not suitable here, as we also want to avoid "tearing" in the configuration. Fixes: a091289218202 ("Bluetooth: hci_conn: Fix hci_le_set_cig_params") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e6393f17576b..11d3ad8d2551 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1843,9 +1843,13 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data) u8 aux_num_cis = 0; u8 cis_id; + hci_dev_lock(hdev); + conn = hci_conn_hash_lookup_cig(hdev, cig_id); - if (!conn) + if (!conn) { + hci_dev_unlock(hdev); return 0; + } qos = &conn->iso_qos; pdu->cig_id = cig_id; @@ -1884,6 +1888,8 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data) } pdu->num_cis = aux_num_cis; + hci_dev_unlock(hdev); + if (!pdu->num_cis) return 0; From b255531b27da336571411248c2a72a350662bd09 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 29 Mar 2026 16:43:02 +0300 Subject: [PATCH 627/712] Bluetooth: hci_event: fix potential UAF in hci_le_remote_conn_param_req_evt hci_conn lookup and field access must be covered by hdev lock in hci_le_remote_conn_param_req_evt, otherwise it's possible it is freed concurrently. Extend the hci_dev_lock critical section to cover all conn usage. Fixes: 95118dd4edfec ("Bluetooth: hci_event: Use of a function table to handle LE subevents") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 81d2f9a3eec9..3ebc5e6d45d9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6784,25 +6784,31 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data, latency = le16_to_cpu(ev->latency); timeout = le16_to_cpu(ev->timeout); + hci_dev_lock(hdev); + hcon = hci_conn_hash_lookup_handle(hdev, handle); - if (!hcon || hcon->state != BT_CONNECTED) - return send_conn_param_neg_reply(hdev, handle, - HCI_ERROR_UNKNOWN_CONN_ID); + if (!hcon || hcon->state != BT_CONNECTED) { + send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_UNKNOWN_CONN_ID); + goto unlock; + } - if (max > hcon->le_conn_max_interval) - return send_conn_param_neg_reply(hdev, handle, - HCI_ERROR_INVALID_LL_PARAMS); + if (max > hcon->le_conn_max_interval) { + send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_INVALID_LL_PARAMS); + goto unlock; + } - if (hci_check_conn_params(min, max, latency, timeout)) - return send_conn_param_neg_reply(hdev, handle, - HCI_ERROR_INVALID_LL_PARAMS); + if (hci_check_conn_params(min, max, latency, timeout)) { + send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_INVALID_LL_PARAMS); + goto unlock; + } if (hcon->role == HCI_ROLE_MASTER) { struct hci_conn_params *params; u8 store_hint; - hci_dev_lock(hdev); - params = hci_conn_params_lookup(hdev, &hcon->dst, hcon->dst_type); if (params) { @@ -6815,8 +6821,6 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data, store_hint = 0x00; } - hci_dev_unlock(hdev); - mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, store_hint, min, max, latency, timeout); } @@ -6830,6 +6834,9 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data, cp.max_ce_len = 0; hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp); + +unlock: + hci_dev_unlock(hdev); } static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data, From bda93eec78cdbfe5cda00785cefebd443e56b88b Mon Sep 17 00:00:00 2001 From: Keenan Dong Date: Wed, 1 Apr 2026 22:25:26 +0800 Subject: [PATCH 628/712] Bluetooth: MGMT: validate mesh send advertising payload length mesh_send() currently bounds MGMT_OP_MESH_SEND by total command length, but it never verifies that the bytes supplied for the flexible adv_data[] array actually match the embedded adv_data_len field. MGMT_MESH_SEND_SIZE only covers the fixed header, so a truncated command can still pass the existing 20..50 byte range check and later drive the async mesh send path past the end of the queued command buffer. Keep rejecting zero-length and oversized advertising payloads, but validate adv_data_len explicitly and require the command length to exactly match the flexible array size before queueing the request. Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh") Reported-by: Keenan Dong Signed-off-by: Keenan Dong Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index adcd86c15b4e..b05bb380e5f8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2478,6 +2478,7 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) struct mgmt_mesh_tx *mesh_tx; struct mgmt_cp_mesh_send *send = data; struct mgmt_rp_mesh_read_features rp; + u16 expected_len; bool sending; int err = 0; @@ -2485,12 +2486,19 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) !hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, MGMT_STATUS_NOT_SUPPORTED); - if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) || - len <= MGMT_MESH_SEND_SIZE || - len > (MGMT_MESH_SEND_SIZE + 31)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, MGMT_STATUS_REJECTED); + if (!send->adv_data_len || send->adv_data_len > 31) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, + MGMT_STATUS_REJECTED); + + expected_len = struct_size(send, adv_data, send->adv_data_len); + if (expected_len != len) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); memset(&rp, 0, sizeof(rp)); From d05111bfe37bfd8bd4d2dfe6675d6bdeef43f7c7 Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Tue, 31 Mar 2026 11:52:12 +0000 Subject: [PATCH 629/712] Bluetooth: SMP: force responder MITM requirements before building the pairing response smp_cmd_pairing_req() currently builds the pairing response from the initiator auth_req before enforcing the local BT_SECURITY_HIGH requirement. If the initiator omits SMP_AUTH_MITM, the response can also omit it even though the local side still requires MITM. tk_request() then sees an auth value without SMP_AUTH_MITM and may select JUST_CFM, making method selection inconsistent with the pairing policy the responder already enforces. When the local side requires HIGH security, first verify that MITM can be achieved from the IO capabilities and then force SMP_AUTH_MITM in the response in both rsp.auth_req and auth. This keeps the responder auth bits and later method selection aligned. Fixes: 2b64d153a0cc ("Bluetooth: Add MITM mechanism to LE-SMP") Cc: stable@vger.kernel.org Suggested-by: Luiz Augusto von Dentz Signed-off-by: Oleh Konko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/smp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 485e3468bd26..deb8dd244b77 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1826,7 +1826,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (sec_level > conn->hcon->pending_sec_level) conn->hcon->pending_sec_level = sec_level; - /* If we need MITM check that it can be achieved */ + /* If we need MITM check that it can be achieved. */ if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { u8 method; @@ -1834,6 +1834,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) req->io_capability); if (method == JUST_WORKS || method == JUST_CFM) return SMP_AUTH_REQUIREMENTS; + + /* Force MITM bit if it isn't set by the initiator. */ + auth |= SMP_AUTH_MITM; + rsp.auth_req |= SMP_AUTH_MITM; } key_size = min(req->max_key_size, rsp.max_key_size); From 20756fec2f0108cb88e815941f1ffff88dc286fe Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Tue, 31 Mar 2026 11:52:13 +0000 Subject: [PATCH 630/712] Bluetooth: SMP: derive legacy responder STK authentication from MITM state The legacy responder path in smp_random() currently labels the stored STK as authenticated whenever pending_sec_level is BT_SECURITY_HIGH. That reflects what the local service requested, not what the pairing flow actually achieved. For Just Works/Confirm legacy pairing, SMP_FLAG_MITM_AUTH stays clear and the resulting STK should remain unauthenticated even if the local side requested HIGH security. Use the established MITM state when storing the responder STK so the key metadata matches the pairing result. This also keeps the legacy path aligned with the Secure Connections code, which already treats JUST_WORKS/JUST_CFM as unauthenticated. Fixes: fff3490f4781 ("Bluetooth: Fix setting correct authentication information for SMP STK") Cc: stable@vger.kernel.org Signed-off-by: Oleh Konko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/smp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index deb8dd244b77..98f1da4f5f55 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1018,10 +1018,7 @@ static u8 smp_random(struct smp_chan *smp) smp_s1(smp->tk, smp->prnd, smp->rrnd, stk); - if (hcon->pending_sec_level == BT_SECURITY_HIGH) - auth = 1; - else - auth = 0; + auth = test_bit(SMP_FLAG_MITM_AUTH, &smp->flags) ? 1 : 0; /* Even though there's no _RESPONDER suffix this is the * responder STK we're adding for later lookup (the initiator From bc39a094730ce062fa034a529c93147c096cb488 Mon Sep 17 00:00:00 2001 From: hkbinbin Date: Tue, 31 Mar 2026 05:39:16 +0000 Subject: [PATCH 631/712] Bluetooth: hci_sync: fix stack buffer overflow in hci_le_big_create_sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hci_le_big_create_sync() uses DEFINE_FLEX to allocate a struct hci_cp_le_big_create_sync on the stack with room for 0x11 (17) BIS entries. However, conn->num_bis can hold up to HCI_MAX_ISO_BIS (31) entries — validated against ISO_MAX_NUM_BIS (0x1f) in the caller hci_conn_big_create_sync(). When conn->num_bis is between 18 and 31, the memcpy that copies conn->bis into cp->bis writes up to 14 bytes past the stack buffer, corrupting adjacent stack memory. This is trivially reproducible: binding an ISO socket with bc_num_bis = ISO_MAX_NUM_BIS (31) and calling listen() will eventually trigger hci_le_big_create_sync() from the HCI command sync worker, causing a KASAN-detectable stack-out-of-bounds write: BUG: KASAN: stack-out-of-bounds in hci_le_big_create_sync+0x256/0x3b0 Write of size 31 at addr ffffc90000487b48 by task kworker/u9:0/71 Fix this by changing the DEFINE_FLEX count from the incorrect 0x11 to HCI_MAX_ISO_BIS, which matches the maximum number of BIS entries that conn->bis can actually carry. Fixes: 42ecf1947135 ("Bluetooth: ISO: Do not emit LE BIG Create Sync if previous is pending") Cc: stable@vger.kernel.org Signed-off-by: hkbinbin Reviewed-by: Paul Menzel Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index ffb0ceda6f7b..919ec275dd23 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7241,7 +7241,8 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) static int hci_le_big_create_sync(struct hci_dev *hdev, void *data) { - DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis, 0x11); + DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis, + HCI_MAX_ISO_BIS); struct hci_conn *conn = data; struct bt_iso_qos *qos = &conn->iso_qos; int err; From ffb5a4843c5bde702ed17cbcdbda98b37f7a6dad Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Tue, 31 Mar 2026 12:17:18 +0800 Subject: [PATCH 632/712] ipv6: fix data race in fib6_metric_set() using cmpxchg fib6_metric_set() may be called concurrently from softirq context without holding the FIB table lock. A typical path is: ndisc_router_discovery() spin_unlock_bh(&table->tb6_lock) <- lock released fib6_metric_set(rt, RTAX_HOPLIMIT, ...) <- lockless call When two CPUs process Router Advertisement packets for the same router simultaneously, they can both arrive at fib6_metric_set() with the same fib6_info pointer whose fib6_metrics still points to dst_default_metrics. if (f6i->fib6_metrics == &dst_default_metrics) { /* both CPUs: true */ struct dst_metrics *p = kzalloc_obj(*p, GFP_ATOMIC); refcount_set(&p->refcnt, 1); f6i->fib6_metrics = p; /* CPU1 overwrites CPU0's p -> p0 leaked */ } The dst_metrics allocated by the losing CPU has refcnt=1 but no pointer to it anywhere in memory, producing a kmemleak report: unreferenced object 0xff1100025aca1400 (size 96): comm "softirq", pid 0, jiffies 4299271239 backtrace: kmalloc_trace+0x28a/0x380 fib6_metric_set+0xcd/0x180 ndisc_router_discovery+0x12dc/0x24b0 icmpv6_rcv+0xc16/0x1360 Fix this by: - Set val for p->metrics before published via cmpxchg() so the metrics value is ready before the pointer becomes visible to other CPUs. - Replace the plain pointer store with cmpxchg() and free the allocation safely when competition failed. - Add READ_ONCE()/WRITE_ONCE() for metrics[] setting in the non-default metrics path to prevent compiler-based data races. Fixes: d4ead6b34b67 ("net/ipv6: move metrics from dst to rt6_info") Reported-by: Fei Liu Reviewed-by: Jiayuan Chen Signed-off-by: Hangbin Liu Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260331-b4-fib6_metric_set-kmemleak-v3-1-88d27f4d8825@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_fib.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index dd26657b6a4a..45ef4d65dcbc 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -727,20 +727,28 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) void fib6_metric_set(struct fib6_info *f6i, int metric, u32 val) { + struct dst_metrics *m; + if (!f6i) return; - if (f6i->fib6_metrics == &dst_default_metrics) { + if (READ_ONCE(f6i->fib6_metrics) == &dst_default_metrics) { + struct dst_metrics *dflt = (struct dst_metrics *)&dst_default_metrics; struct dst_metrics *p = kzalloc_obj(*p, GFP_ATOMIC); if (!p) return; + p->metrics[metric - 1] = val; refcount_set(&p->refcnt, 1); - f6i->fib6_metrics = p; + if (cmpxchg(&f6i->fib6_metrics, dflt, p) != dflt) + kfree(p); + else + return; } - f6i->fib6_metrics->metrics[metric - 1] = val; + m = READ_ONCE(f6i->fib6_metrics); + WRITE_ONCE(m->metrics[metric - 1], val); } /* From a54ecccfae62c5c85259ae5ea5d9c20009519049 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Tue, 31 Mar 2026 00:32:38 +0800 Subject: [PATCH 633/712] rds: ib: reject FRMR registration before IB connection is established rds_ib_get_mr() extracts the rds_ib_connection from conn->c_transport_data and passes it to rds_ib_reg_frmr() for FRWR memory registration. On a fresh outgoing connection, ic is allocated in rds_ib_conn_alloc() with i_cm_id = NULL because the connection worker has not yet called rds_ib_conn_path_connect() to create the rdma_cm_id. When sendmsg() with RDS_CMSG_RDMA_MAP is called on such a connection, the sendmsg path parses the control message before any connection establishment, allowing rds_ib_post_reg_frmr() to dereference ic->i_cm_id->qp and crash the kernel. The existing guard in rds_ib_reg_frmr() only checks for !ic (added in commit 9e630bcb7701), which does not catch this case since ic is allocated early and is always non-NULL once the connection object exists. KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:rds_ib_post_reg_frmr+0x50e/0x920 Call Trace: rds_ib_post_reg_frmr (net/rds/ib_frmr.c:167) rds_ib_map_frmr (net/rds/ib_frmr.c:252) rds_ib_reg_frmr (net/rds/ib_frmr.c:430) rds_ib_get_mr (net/rds/ib_rdma.c:615) __rds_rdma_map (net/rds/rdma.c:295) rds_cmsg_rdma_map (net/rds/rdma.c:860) rds_sendmsg (net/rds/send.c:1363) ____sys_sendmsg do_syscall_64 Add a check in rds_ib_get_mr() that verifies ic, i_cm_id, and qp are all non-NULL before proceeding with FRMR registration, mirroring the guard already present in rds_ib_post_inv(). Return -ENODEV when the connection is not ready, which the existing error handling in rds_cmsg_send() converts to -EAGAIN for userspace retry and triggers rds_conn_connect_if_down() to start the connection worker. Fixes: 1659185fb4d0 ("RDS: IB: Support Fastreg MR (FRMR) memory registration mode") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260330163237.2752440-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/rds/ib_rdma.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 077f7041df15..2cfec252eeac 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -604,8 +604,13 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents, return ibmr; } - if (conn) + if (conn) { ic = conn->c_transport_data; + if (!ic || !ic->i_cm_id || !ic->i_cm_id->qp) { + ret = -ENODEV; + goto out; + } + } if (!rds_ibdev->mr_8k_pool || !rds_ibdev->mr_1m_pool) { ret = -ENODEV; From ad8391d37f334ee73ba91926f8b4e4cf6d31ea04 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 1 Apr 2026 00:54:15 +0000 Subject: [PATCH 634/712] bpf: sockmap: Fix use-after-free of sk->sk_socket in sk_psock_verdict_data_ready(). syzbot reported use-after-free of AF_UNIX socket's sk->sk_socket in sk_psock_verdict_data_ready(). [0] In unix_stream_sendmsg(), the peer socket's ->sk_data_ready() is called after dropping its unix_state_lock(). Although the sender socket holds the peer's refcount, it does not prevent the peer's sock_orphan(), and the peer's sk_socket might be freed after one RCU grace period. Let's fetch the peer's sk->sk_socket and sk->sk_socket->ops under RCU in sk_psock_verdict_data_ready(). [0]: BUG: KASAN: slab-use-after-free in sk_psock_verdict_data_ready+0xec/0x590 net/core/skmsg.c:1278 Read of size 8 at addr ffff8880594da860 by task syz.4.1842/11013 CPU: 1 UID: 0 PID: 11013 Comm: syz.4.1842 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026 Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xba/0x230 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 sk_psock_verdict_data_ready+0xec/0x590 net/core/skmsg.c:1278 unix_stream_sendmsg+0x8a3/0xe80 net/unix/af_unix.c:2482 sock_sendmsg_nosec net/socket.c:721 [inline] __sock_sendmsg net/socket.c:736 [inline] ____sys_sendmsg+0x972/0x9f0 net/socket.c:2585 ___sys_sendmsg+0x2a5/0x360 net/socket.c:2639 __sys_sendmsg net/socket.c:2671 [inline] __do_sys_sendmsg net/socket.c:2676 [inline] __se_sys_sendmsg net/socket.c:2674 [inline] __x64_sys_sendmsg+0x1bd/0x2a0 net/socket.c:2674 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7facf899c819 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007facf9827028 EFLAGS: 00000246 ORIG_RAX: 000000000000002e RAX: ffffffffffffffda RBX: 00007facf8c15fa0 RCX: 00007facf899c819 RDX: 0000000000000000 RSI: 0000200000000500 RDI: 0000000000000004 RBP: 00007facf8a32c91 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007facf8c16038 R14: 00007facf8c15fa0 R15: 00007ffd41b01c78 Allocated by task 11013: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 unpoison_slab_object mm/kasan/common.c:340 [inline] __kasan_slab_alloc+0x6c/0x80 mm/kasan/common.c:366 kasan_slab_alloc include/linux/kasan.h:253 [inline] slab_post_alloc_hook mm/slub.c:4538 [inline] slab_alloc_node mm/slub.c:4866 [inline] kmem_cache_alloc_lru_noprof+0x2b8/0x640 mm/slub.c:4885 sock_alloc_inode+0x28/0xc0 net/socket.c:316 alloc_inode+0x6a/0x1b0 fs/inode.c:347 new_inode_pseudo include/linux/fs.h:3003 [inline] sock_alloc net/socket.c:631 [inline] __sock_create+0x12d/0x9d0 net/socket.c:1562 sock_create net/socket.c:1656 [inline] __sys_socketpair+0x1c4/0x560 net/socket.c:1803 __do_sys_socketpair net/socket.c:1856 [inline] __se_sys_socketpair net/socket.c:1853 [inline] __x64_sys_socketpair+0x9b/0xb0 net/socket.c:1853 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 15: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2685 [inline] slab_free mm/slub.c:6165 [inline] kmem_cache_free+0x187/0x630 mm/slub.c:6295 rcu_do_batch kernel/rcu/tree.c:2617 [inline] rcu_core+0x7cd/0x1070 kernel/rcu/tree.c:2869 handle_softirqs+0x22a/0x870 kernel/softirq.c:622 run_ksoftirqd+0x36/0x60 kernel/softirq.c:1063 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x51e/0xb90 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fixes: c63829182c37 ("af_unix: Implement ->psock_update_sk_prot()") Closes: https://lore.kernel.org/bpf/69cc6b9f.a70a0220.128fd0.004b.GAE@google.com/ Reported-by: syzbot+2184232f07e3677fbaef@syzkaller.appspotmail.com Signed-off-by: Kuniyuki Iwashima Signed-off-by: Martin KaFai Lau Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260401005418.2452999-1-kuniyu@google.com --- net/core/skmsg.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 3261793abe83..6187a83bd741 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1267,17 +1267,20 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) static void sk_psock_verdict_data_ready(struct sock *sk) { - struct socket *sock = sk->sk_socket; - const struct proto_ops *ops; + const struct proto_ops *ops = NULL; + struct socket *sock; int copied; trace_sk_data_ready(sk); - if (unlikely(!sock)) - return; - ops = READ_ONCE(sock->ops); + rcu_read_lock(); + sock = READ_ONCE(sk->sk_socket); + if (likely(sock)) + ops = READ_ONCE(sock->ops); + rcu_read_unlock(); if (!ops || !ops->read_skb) return; + copied = ops->read_skb(sk, sk_psock_verdict_recv); if (copied >= 0) { struct sk_psock *psock; From d64cb81dcbd54927515a7f65e5e24affdc73c14b Mon Sep 17 00:00:00 2001 From: Yucheng Lu Date: Tue, 31 Mar 2026 16:00:21 +0800 Subject: [PATCH 635/712] net/sched: sch_netem: fix out-of-bounds access in packet corruption In netem_enqueue(), the packet corruption logic uses get_random_u32_below(skb_headlen(skb)) to select an index for modifying skb->data. When an AF_PACKET TX_RING sends fully non-linear packets over an IPIP tunnel, skb_headlen(skb) evaluates to 0. Passing 0 to get_random_u32_below() takes the variable-ceil slow path which returns an unconstrained 32-bit random integer. Using this unconstrained value as an offset into skb->data results in an out-of-bounds memory access. Fix this by verifying skb_headlen(skb) is non-zero before attempting to corrupt the linear data area. Fully non-linear packets will silently bypass the corruption logic. Fixes: c865e5d99e25 ("[PKT_SCHED] netem: packet corruption option") Reported-by: Yifan Wu Reported-by: Juefei Pu Signed-off-by: Yuan Tan Signed-off-by: Xin Liu Signed-off-by: Yuhang Zheng Signed-off-by: Yucheng Lu Reviewed-by: Stephen Hemminger Link: https://patch.msgid.link/45435c0935df877853a81e6d06205ac738ec65fa.1774941614.git.kanolyc@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/sch_netem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 5de1c932944a..20df1c08b1e9 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -519,8 +519,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, goto finish_segs; } - skb->data[get_random_u32_below(skb_headlen(skb))] ^= - 1<data[get_random_u32_below(skb_headlen(skb))] ^= + 1 << get_random_u32_below(8); } if (unlikely(q->t_len >= sch->limit)) { From b4e5f04c58a29c499faa85d12952ca9a4faf1cb9 Mon Sep 17 00:00:00 2001 From: Srujana Challa Date: Thu, 26 Mar 2026 19:53:44 +0530 Subject: [PATCH 636/712] virtio_net: clamp rss_max_key_size to NETDEV_RSS_KEY_LEN rss_max_key_size in the virtio spec is the maximum key size supported by the device, not a mandatory size the driver must use. Also the value 40 is a spec minimum, not a spec maximum. The current code rejects RSS and can fail probe when the device reports a larger rss_max_key_size than the driver buffer limit. Instead, clamp the effective key length to min(device rss_max_key_size, NETDEV_RSS_KEY_LEN) and keep RSS enabled. This keeps probe working on devices that advertise larger maximum key sizes while respecting the netdev RSS key buffer size limit. Fixes: 3f7d9c1964fc ("virtio_net: Add hash_key_length check") Cc: stable@vger.kernel.org Signed-off-by: Srujana Challa Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260326142344.1171317-1-schalla@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/virtio_net.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ab2108ee206a..c0b9bc5574e2 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -381,8 +381,6 @@ struct receive_queue { struct xdp_buff **xsk_buffs; }; -#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40 - /* Control VQ buffers: protected by the rtnl lock */ struct control_buf { struct virtio_net_ctrl_hdr hdr; @@ -486,7 +484,7 @@ struct virtnet_info { /* Must be last as it ends in a flexible-array member. */ TRAILING_OVERLAP(struct virtio_net_rss_config_trailer, rss_trailer, hash_key_data, - u8 rss_hash_key_data[VIRTIO_NET_RSS_MAX_KEY_SIZE]; + u8 rss_hash_key_data[NETDEV_RSS_KEY_LEN]; ); }; static_assert(offsetof(struct virtnet_info, rss_trailer.hash_key_data) == @@ -6708,6 +6706,7 @@ static int virtnet_probe(struct virtio_device *vdev) struct virtnet_info *vi; u16 max_queue_pairs; int mtu = 0; + u16 key_sz; /* Find if host supports multiqueue/rss virtio_net device */ max_queue_pairs = 1; @@ -6842,14 +6841,13 @@ static int virtnet_probe(struct virtio_device *vdev) } if (vi->has_rss || vi->has_rss_hash_report) { - vi->rss_key_size = - virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size)); - if (vi->rss_key_size > VIRTIO_NET_RSS_MAX_KEY_SIZE) { - dev_err(&vdev->dev, "rss_max_key_size=%u exceeds the limit %u.\n", - vi->rss_key_size, VIRTIO_NET_RSS_MAX_KEY_SIZE); - err = -EINVAL; - goto free; - } + key_sz = virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size)); + + vi->rss_key_size = min_t(u16, key_sz, NETDEV_RSS_KEY_LEN); + if (key_sz > vi->rss_key_size) + dev_warn(&vdev->dev, + "rss_max_key_size=%u exceeds driver limit %u, clamping\n", + key_sz, vi->rss_key_size); vi->rss_hash_types_supported = virtio_cread32(vdev, offsetof(struct virtio_net_config, supported_hash_types)); From ce8fe5287b87e24e225c342f3b0ec04f0b3680fe Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Mon, 30 Mar 2026 21:45:40 +0300 Subject: [PATCH 637/712] net: macb: fix clk handling on PCI glue driver removal platform_device_unregister() may still want to use the registered clks during runtime resume callback. Note that there is a commit d82d5303c4c5 ("net: macb: fix use after free on rmmod") that addressed the similar problem of clk vs platform device unregistration but just moved the bug to another place. Save the pointers to clks into local variables for reuse after platform device is unregistered. BUG: KASAN: use-after-free in clk_prepare+0x5a/0x60 Read of size 8 at addr ffff888104f85e00 by task modprobe/597 CPU: 2 PID: 597 Comm: modprobe Not tainted 6.1.164+ #114 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.1-0-g3208b098f51a-prebuilt.qemu.org 04/01/2014 Call Trace: dump_stack_lvl+0x8d/0xba print_report+0x17f/0x496 kasan_report+0xd9/0x180 clk_prepare+0x5a/0x60 macb_runtime_resume+0x13d/0x410 [macb] pm_generic_runtime_resume+0x97/0xd0 __rpm_callback+0xc8/0x4d0 rpm_callback+0xf6/0x230 rpm_resume+0xeeb/0x1a70 __pm_runtime_resume+0xb4/0x170 bus_remove_device+0x2e3/0x4b0 device_del+0x5b3/0xdc0 platform_device_del+0x4e/0x280 platform_device_unregister+0x11/0x50 pci_device_remove+0xae/0x210 device_remove+0xcb/0x180 device_release_driver_internal+0x529/0x770 driver_detach+0xd4/0x1a0 bus_remove_driver+0x135/0x260 driver_unregister+0x72/0xb0 pci_unregister_driver+0x26/0x220 __do_sys_delete_module+0x32e/0x550 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Allocated by task 519: kasan_save_stack+0x2c/0x50 kasan_set_track+0x21/0x30 __kasan_kmalloc+0x8e/0x90 __clk_register+0x458/0x2890 clk_hw_register+0x1a/0x60 __clk_hw_register_fixed_rate+0x255/0x410 clk_register_fixed_rate+0x3c/0xa0 macb_probe+0x1d8/0x42e [macb_pci] local_pci_probe+0xd7/0x190 pci_device_probe+0x252/0x600 really_probe+0x255/0x7f0 __driver_probe_device+0x1ee/0x330 driver_probe_device+0x4c/0x1f0 __driver_attach+0x1df/0x4e0 bus_for_each_dev+0x15d/0x1f0 bus_add_driver+0x486/0x5e0 driver_register+0x23a/0x3d0 do_one_initcall+0xfd/0x4d0 do_init_module+0x18b/0x5a0 load_module+0x5663/0x7950 __do_sys_finit_module+0x101/0x180 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Freed by task 597: kasan_save_stack+0x2c/0x50 kasan_set_track+0x21/0x30 kasan_save_free_info+0x2a/0x50 __kasan_slab_free+0x106/0x180 __kmem_cache_free+0xbc/0x320 clk_unregister+0x6de/0x8d0 macb_remove+0x73/0xc0 [macb_pci] pci_device_remove+0xae/0x210 device_remove+0xcb/0x180 device_release_driver_internal+0x529/0x770 driver_detach+0xd4/0x1a0 bus_remove_driver+0x135/0x260 driver_unregister+0x72/0xb0 pci_unregister_driver+0x26/0x220 __do_sys_delete_module+0x32e/0x550 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Fixes: d82d5303c4c5 ("net: macb: fix use after free on rmmod") Signed-off-by: Fedor Pchelkin Link: https://patch.msgid.link/20260330184542.626619-1-pchelkin@ispras.ru Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index fc4f5aee6ab3..0ce5b736ea43 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -109,10 +109,12 @@ static void macb_remove(struct pci_dev *pdev) { struct platform_device *plat_dev = pci_get_drvdata(pdev); struct macb_platform_data *plat_data = dev_get_platdata(&plat_dev->dev); + struct clk *pclk = plat_data->pclk; + struct clk *hclk = plat_data->hclk; - clk_unregister(plat_data->pclk); - clk_unregister(plat_data->hclk); platform_device_unregister(plat_dev); + clk_unregister(pclk); + clk_unregister(hclk); } static const struct pci_device_id dev_id_table[] = { From f0f367a4f459cc8118aadc43c6bba53c60d93f8d Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Mon, 30 Mar 2026 21:45:41 +0300 Subject: [PATCH 638/712] net: macb: properly unregister fixed rate clocks The additional resources allocated with clk_register_fixed_rate() need to be released with clk_unregister_fixed_rate(), otherwise they are lost. Fixes: 83a77e9ec415 ("net: macb: Added PCI wrapper for Platform Driver.") Signed-off-by: Fedor Pchelkin Link: https://patch.msgid.link/20260330184542.626619-2-pchelkin@ispras.ru Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index 0ce5b736ea43..b79dec17e6b0 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -96,10 +96,10 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_plat_dev_register: - clk_unregister(plat_data.hclk); + clk_unregister_fixed_rate(plat_data.hclk); err_hclk_register: - clk_unregister(plat_data.pclk); + clk_unregister_fixed_rate(plat_data.pclk); err_pclk_register: return err; @@ -113,8 +113,8 @@ static void macb_remove(struct pci_dev *pdev) struct clk *hclk = plat_data->hclk; platform_device_unregister(plat_dev); - clk_unregister(pclk); - clk_unregister(hclk); + clk_unregister_fixed_rate(pclk); + clk_unregister_fixed_rate(hclk); } static const struct pci_device_id dev_id_table[] = { From bf16bca6653679d8a514d6c1c5a2c67065033f14 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Mon, 30 Mar 2026 22:40:13 +0300 Subject: [PATCH 639/712] net/mlx5: lag: Check for LAG device before creating debugfs __mlx5_lag_dev_add_mdev() may return 0 (success) even when an error occurs that is handled gracefully. Consequently, the initialization flow proceeds to call mlx5_ldev_add_debugfs() even when there is no valid LAG context. mlx5_ldev_add_debugfs() blindly created the debugfs directory and attributes. This exposed interfaces (like the members file) that rely on a valid ldev pointer, leading to potential NULL pointer dereferences if accessed when ldev is NULL. Add a check to verify that mlx5_lag_dev(dev) returns a valid pointer before attempting to create the debugfs entries. Fixes: 7f46a0b7327a ("net/mlx5: Lag, add debugfs to query hardware lag state") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260330194015.53585-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c index 62b6faa4276a..b8d5f6a44d26 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c @@ -160,8 +160,11 @@ DEFINE_SHOW_ATTRIBUTE(members); void mlx5_ldev_add_debugfs(struct mlx5_core_dev *dev) { + struct mlx5_lag *ldev = mlx5_lag_dev(dev); struct dentry *dbg; + if (!ldev) + return; dbg = debugfs_create_dir("lag", mlx5_debugfs_get_dev_root(dev)); dev->priv.dbg.lag_debugfs = dbg; From 10dc35f6a443d488f219d1a1e3fb8f8dac422070 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 30 Mar 2026 22:40:14 +0300 Subject: [PATCH 640/712] net/mlx5: Avoid "No data available" when FW version queries fail Avoid printing the misleading "kernel answers: No data available" devlink output when querying firmware or pending firmware version fails (e.g. MLX5 fw state errors / flash failures). FW can fail on loading the pending flash image and get its version due to various reasons, examples: mlxfw: Firmware flash failed: key not applicable, err (7) mlx5_fw_image_pending: can't read pending fw version while fw state is 1 and the resulting: $ devlink dev info kernel answers: No data available Instead, just report 0 or 0xfff.. versions in case of failure to indicate a problem, and let other information be shown. after the fix: $ devlink dev info pci/0000:00:06.0: driver mlx5_core serial_number xxx... board.serial_number MT2225300179 versions: fixed: fw.psid MT_0000000436 running: fw.version 22.41.0188 fw 22.41.0188 stored: fw.version 255.255.65535 fw 255.255.65535 Fixes: 9c86b07e3069 ("net/mlx5: Added fw version query command") Signed-off-by: Saeed Mahameed Reviewed-by: Moshe Shemesh Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260330194015.53585-3-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/devlink.c | 4 +- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 49 ++++++++++++------- .../ethernet/mellanox/mlx5/core/mlx5_core.h | 4 +- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 6698ac55a4bf..73cf0321bb86 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -107,9 +107,7 @@ mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, if (err) return err; - err = mlx5_fw_version_query(dev, &running_fw, &stored_fw); - if (err) - return err; + mlx5_fw_version_query(dev, &running_fw, &stored_fw); snprintf(version_str, sizeof(version_str), "%d.%d.%04d", mlx5_fw_ver_major(running_fw), mlx5_fw_ver_minor(running_fw), diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index eeb4437975f2..c1f220e5fe18 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -822,48 +822,63 @@ mlx5_fw_image_pending(struct mlx5_core_dev *dev, return 0; } -int mlx5_fw_version_query(struct mlx5_core_dev *dev, - u32 *running_ver, u32 *pending_ver) +void mlx5_fw_version_query(struct mlx5_core_dev *dev, + u32 *running_ver, u32 *pending_ver) { u32 reg_mcqi_version[MLX5_ST_SZ_DW(mcqi_version)] = {}; bool pending_version_exists; int component_index; int err; + *running_ver = 0; + *pending_ver = 0; + if (!MLX5_CAP_GEN(dev, mcam_reg) || !MLX5_CAP_MCAM_REG(dev, mcqi) || !MLX5_CAP_MCAM_REG(dev, mcqs)) { mlx5_core_warn(dev, "fw query isn't supported by the FW\n"); - return -EOPNOTSUPP; + return; } component_index = mlx5_get_boot_img_component_index(dev); - if (component_index < 0) - return component_index; + if (component_index < 0) { + mlx5_core_warn(dev, "fw query failed to find boot img component index, err %d\n", + component_index); + return; + } + *running_ver = U32_MAX; /* indicate failure */ err = mlx5_reg_mcqi_version_query(dev, component_index, MCQI_FW_RUNNING_VERSION, reg_mcqi_version); - if (err) - return err; - - *running_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version); + if (!err) + *running_ver = MLX5_GET(mcqi_version, reg_mcqi_version, + version); + else + mlx5_core_warn(dev, "failed to query running version, err %d\n", + err); + *pending_ver = U32_MAX; /* indicate failure */ err = mlx5_fw_image_pending(dev, component_index, &pending_version_exists); - if (err) - return err; + if (err) { + mlx5_core_warn(dev, "failed to query pending image, err %d\n", + err); + return; + } if (!pending_version_exists) { *pending_ver = 0; - return 0; + return; } err = mlx5_reg_mcqi_version_query(dev, component_index, MCQI_FW_STORED_VERSION, reg_mcqi_version); - if (err) - return err; + if (!err) + *pending_ver = MLX5_GET(mcqi_version, reg_mcqi_version, + version); + else + mlx5_core_warn(dev, "failed to query pending version, err %d\n", + err); - *pending_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version); - - return 0; + return; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index b635b423d972..1507e881d962 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -393,8 +393,8 @@ int mlx5_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); int mlx5_firmware_flash(struct mlx5_core_dev *dev, const struct firmware *fw, struct netlink_ext_ack *extack); -int mlx5_fw_version_query(struct mlx5_core_dev *dev, - u32 *running_ver, u32 *stored_ver); +void mlx5_fw_version_query(struct mlx5_core_dev *dev, u32 *running_ver, + u32 *stored_ver); #ifdef CONFIG_MLX5_CORE_EN int mlx5e_init(void); From 403186400a1a6166efe7031edc549c15fee4723f Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 30 Mar 2026 22:40:15 +0300 Subject: [PATCH 641/712] net/mlx5: Fix switchdev mode rollback in case of failure If for some internal reason switchdev mode fails, we rollback to legacy mode, before this patch, rollback will unregister the uplink netdev and leave it unregistered causing the below kernel bug. To fix this, we need to avoid netdev unregister by setting the proper rollback flag 'MLX5_PRIV_FLAGS_SWITCH_LEGACY' to indicate legacy mode. devlink (431) used greatest stack depth: 11048 bytes left mlx5_core 0000:00:03.0: E-Switch: Disable: mode(LEGACY), nvfs(0), \ necvfs(0), active vports(0) mlx5_core 0000:00:03.0: E-Switch: Supported tc chains and prios offload mlx5_core 0000:00:03.0: Loading uplink representor for vport 65535 mlx5_core 0000:00:03.0: mlx5_cmd_out_err:816:(pid 456): \ QUERY_HCA_CAP(0x100) op_mod(0x0) failed, \ status bad parameter(0x3), syndrome (0x3a3846), err(-22) mlx5_core 0000:00:03.0 enp0s3np0 (unregistered): Unloading uplink \ representor for vport 65535 ------------[ cut here ]------------ kernel BUG at net/core/dev.c:12070! Oops: invalid opcode: 0000 [#1] SMP NOPTI CPU: 2 UID: 0 PID: 456 Comm: devlink Not tainted 6.16.0-rc3+ \ #9 PREEMPT(voluntary) RIP: 0010:unregister_netdevice_many_notify+0x123/0xae0 ... Call Trace: [ 90.923094] unregister_netdevice_queue+0xad/0xf0 [ 90.923323] unregister_netdev+0x1c/0x40 [ 90.923522] mlx5e_vport_rep_unload+0x61/0xc6 [ 90.923736] esw_offloads_enable+0x8e6/0x920 [ 90.923947] mlx5_eswitch_enable_locked+0x349/0x430 [ 90.924182] ? is_mp_supported+0x57/0xb0 [ 90.924376] mlx5_devlink_eswitch_mode_set+0x167/0x350 [ 90.924628] devlink_nl_eswitch_set_doit+0x6f/0xf0 [ 90.924862] genl_family_rcv_msg_doit+0xe8/0x140 [ 90.925088] genl_rcv_msg+0x18b/0x290 [ 90.925269] ? __pfx_devlink_nl_pre_doit+0x10/0x10 [ 90.925506] ? __pfx_devlink_nl_eswitch_set_doit+0x10/0x10 [ 90.925766] ? __pfx_devlink_nl_post_doit+0x10/0x10 [ 90.926001] ? __pfx_genl_rcv_msg+0x10/0x10 [ 90.926206] netlink_rcv_skb+0x52/0x100 [ 90.926393] genl_rcv+0x28/0x40 [ 90.926557] netlink_unicast+0x27d/0x3d0 [ 90.926749] netlink_sendmsg+0x1f7/0x430 [ 90.926942] __sys_sendto+0x213/0x220 [ 90.927127] ? __sys_recvmsg+0x6a/0xd0 [ 90.927312] __x64_sys_sendto+0x24/0x30 [ 90.927504] do_syscall_64+0x50/0x1c0 [ 90.927687] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 90.927929] RIP: 0033:0x7f7d0363e047 Fixes: 2a4f56fbcc47 ("net/mlx5e: Keep netdev when leave switchdev for devlink set legacy only") Signed-off-by: Saeed Mahameed Reviewed-by: Jianbo Liu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260330194015.53585-4-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 7a9ee36b8dca..01f6aecc4fcc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3761,6 +3761,8 @@ int esw_offloads_enable(struct mlx5_eswitch *esw) return 0; err_vports: + /* rollback to legacy, indicates don't unregister the uplink netdev */ + esw->dev->priv.flags |= MLX5_PRIV_FLAGS_SWITCH_LEGACY; mlx5_esw_offloads_rep_unload(esw, MLX5_VPORT_UPLINK); err_uplink: esw_offloads_steering_cleanup(esw); From ceee35e5674aa84cf9e504c2a9dae4587511556c Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 30 Mar 2026 23:51:36 -0700 Subject: [PATCH 642/712] bnxt_en: Refactor some basic ring setup and adjustment logic Refactor out the basic code that trims the default rings, sets up and adjusts XDP TX rings and CP rings. There is no change in behavior. This is to prepare for the next bug fix patch. Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Reviewed-by: Andy Gospodarek Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260331065138.948205-2-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 53 +++++++++++++------ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 5 +- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 5 +- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7ed805713fbb..173f962fc2ab 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -12992,6 +12992,21 @@ static int bnxt_tx_nr_rings_per_tc(struct bnxt *bp) return bp->num_tc ? bp->tx_nr_rings / bp->num_tc : bp->tx_nr_rings; } +static void bnxt_set_xdp_tx_rings(struct bnxt *bp) +{ + bp->tx_nr_rings_xdp = bp->tx_nr_rings_per_tc; + bp->tx_nr_rings += bp->tx_nr_rings_xdp; +} + +static void bnxt_adj_tx_rings(struct bnxt *bp) +{ + /* Make adjustments if reserved TX rings are less than requested */ + bp->tx_nr_rings -= bp->tx_nr_rings_xdp; + bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + if (bp->tx_nr_rings_xdp) + bnxt_set_xdp_tx_rings(bp); +} + static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) { int rc = 0; @@ -13009,13 +13024,7 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) if (rc) return rc; - /* Make adjustments if reserved TX rings are less than requested */ - bp->tx_nr_rings -= bp->tx_nr_rings_xdp; - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); - if (bp->tx_nr_rings_xdp) { - bp->tx_nr_rings_xdp = bp->tx_nr_rings_per_tc; - bp->tx_nr_rings += bp->tx_nr_rings_xdp; - } + bnxt_adj_tx_rings(bp); rc = bnxt_alloc_mem(bp, irq_re_init); if (rc) { netdev_err(bp->dev, "bnxt_alloc_mem err: %x\n", rc); @@ -15436,11 +15445,19 @@ static int bnxt_change_mtu(struct net_device *dev, int new_mtu) return 0; } +void bnxt_set_cp_rings(struct bnxt *bp, bool sh) +{ + int tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); + + bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) : + tx_cp + bp->rx_nr_rings; +} + int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) { struct bnxt *bp = netdev_priv(dev); bool sh = false; - int rc, tx_cp; + int rc; if (tc > bp->max_tc) { netdev_err(dev, "Too many traffic classes requested: %d. Max supported is %d.\n", @@ -15473,9 +15490,7 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) bp->num_tc = 0; } bp->tx_nr_rings += bp->tx_nr_rings_xdp; - tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); - bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) : - tx_cp + bp->rx_nr_rings; + bnxt_set_cp_rings(bp, sh); if (netif_running(bp->dev)) return bnxt_open_nic(bp, true, false); @@ -16525,6 +16540,15 @@ static void bnxt_trim_dflt_sh_rings(struct bnxt *bp) bp->tx_nr_rings = bnxt_tx_nr_rings(bp); } +static void bnxt_adj_dflt_rings(struct bnxt *bp, bool sh) +{ + if (sh) + bnxt_trim_dflt_sh_rings(bp); + else + bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings; + bp->tx_nr_rings = bnxt_tx_nr_rings(bp); +} + static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) { int dflt_rings, max_rx_rings, max_tx_rings, rc; @@ -16550,11 +16574,8 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) return rc; bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings); bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings); - if (sh) - bnxt_trim_dflt_sh_rings(bp); - else - bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings; - bp->tx_nr_rings = bnxt_tx_nr_rings(bp); + + bnxt_adj_dflt_rings(bp, sh); avail_msix = bnxt_get_max_func_irqs(bp) - bp->cp_nr_rings; if (avail_msix >= BNXT_MIN_ROCE_CP_RINGS) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index a97d651130df..4bc7f7aeaab3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2985,6 +2985,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, int tx_xdp); int bnxt_fw_init_one(struct bnxt *bp); bool bnxt_hwrm_reset_permitted(struct bnxt *bp); +void bnxt_set_cp_rings(struct bnxt *bp, bool sh); int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); struct bnxt_ntuple_filter *bnxt_lookup_ntp_filter_from_idx(struct bnxt *bp, struct bnxt_ntuple_filter *fltr, u32 idx); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 28d0ece2e7b1..0407aa1b3190 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -945,7 +945,6 @@ static int bnxt_set_channels(struct net_device *dev, bool sh = false; int tx_xdp = 0; int rc = 0; - int tx_cp; if (channel->other_count) return -EINVAL; @@ -1013,9 +1012,7 @@ static int bnxt_set_channels(struct net_device *dev, if (tcs > 1) bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs + tx_xdp; - tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); - bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) : - tx_cp + bp->rx_nr_rings; + bnxt_set_cp_rings(bp, sh); /* After changing number of rx channels, update NTUPLE feature. */ netdev_update_features(dev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 85cbeb35681c..bebe37e139c9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -384,7 +384,7 @@ int bnxt_xdp_xmit(struct net_device *dev, int num_frames, static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) { struct net_device *dev = bp->dev; - int tx_xdp = 0, tx_cp, rc, tc; + int tx_xdp = 0, rc, tc; struct bpf_prog *old; netdev_assert_locked(dev); @@ -431,8 +431,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) } bp->tx_nr_rings_xdp = tx_xdp; bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp; - tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); - bp->cp_nr_rings = max_t(int, tx_cp, bp->rx_nr_rings); + bnxt_set_cp_rings(bp, true); bnxt_set_tpa_flags(bp); bnxt_set_ring_params(bp); From e4bf81dcad0a6fff2bbe5331d2c7fb30d45a788c Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 30 Mar 2026 23:51:37 -0700 Subject: [PATCH 643/712] bnxt_en: Don't assume XDP is never enabled in bnxt_init_dflt_ring_mode() The original code made the assumption that when we set up the initial default ring mode, we must be just loading the driver and XDP cannot be enabled yet. This is not true when the FW goes through a resource or capability change. Resource reservations will be cancelled and reinitialized with XDP already enabled. devlink reload with XDP enabled will also have the same issue. This scenario will cause the ring arithmetic to be all wrong in the bnxt_init_dflt_ring_mode() path causing failure: bnxt_en 0000:a1:00.0 ens2f0np0: bnxt_setup_int_mode err: ffffffea bnxt_en 0000:a1:00.0 ens2f0np0: bnxt_request_irq err: ffffffea bnxt_en 0000:a1:00.0 ens2f0np0: nic open fail (rc: ffffffea) Fix it by properly accounting for XDP in the bnxt_init_dflt_ring_mode() path by using the refactored helper functions in the previous patch. Reviewed-by: Andy Gospodarek Reviewed-by: Pavan Chebbi Reviewed-by: Kalesh AP Fixes: ec5d31e3c15d ("bnxt_en: Handle firmware reset status during IF_UP.") Fixes: 228ea8c187d8 ("bnxt_en: implement devlink dev reload driver_reinit") Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260331065138.948205-3-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 173f962fc2ab..f11f3a704da5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -16547,6 +16547,10 @@ static void bnxt_adj_dflt_rings(struct bnxt *bp, bool sh) else bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings; bp->tx_nr_rings = bnxt_tx_nr_rings(bp); + if (sh && READ_ONCE(bp->xdp_prog)) { + bnxt_set_xdp_tx_rings(bp); + bnxt_set_cp_rings(bp, true); + } } static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) @@ -16588,16 +16592,17 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) rc = __bnxt_reserve_rings(bp); if (rc && rc != -ENODEV) netdev_warn(bp->dev, "Unable to reserve tx rings\n"); - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + + bnxt_adj_tx_rings(bp); if (sh) - bnxt_trim_dflt_sh_rings(bp); + bnxt_adj_dflt_rings(bp, true); /* Rings may have been trimmed, re-reserve the trimmed rings. */ if (bnxt_need_reserve_rings(bp)) { rc = __bnxt_reserve_rings(bp); if (rc && rc != -ENODEV) netdev_warn(bp->dev, "2nd rings reservation failed.\n"); - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + bnxt_adj_tx_rings(bp); } if (BNXT_CHIP_TYPE_NITRO_A0(bp)) { bp->rx_nr_rings++; @@ -16631,7 +16636,7 @@ static int bnxt_init_dflt_ring_mode(struct bnxt *bp) if (rc) goto init_dflt_ring_err; - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + bnxt_adj_tx_rings(bp); bnxt_set_dflt_rfs(bp); From 071dbfa304e85a6b04a593e950d18fa170997288 Mon Sep 17 00:00:00 2001 From: Pavan Chebbi Date: Mon, 30 Mar 2026 23:51:38 -0700 Subject: [PATCH 644/712] bnxt_en: Restore default stat ctxs for ULP when resource is available During resource reservation, if the L2 driver does not have enough MSIX vectors to provide to the RoCE driver, it sets the stat ctxs for ULP also to 0 so that we don't have to reserve it unnecessarily. However, subsequently the user may reduce L2 rings thereby freeing up some resources that the L2 driver can now earmark for RoCE. In this case, the driver should restore the default ULP stat ctxs to make sure that all RoCE resources are ready for use. The RoCE driver may fail to initialize in this scenario without this fix. Fixes: d630624ebd70 ("bnxt_en: Utilize ulp client resources if RoCE is not registered") Reviewed-by: Kalesh AP Signed-off-by: Pavan Chebbi Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260331065138.948205-4-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index f11f3a704da5..3f775196ef81 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8045,6 +8045,8 @@ static int __bnxt_reserve_rings(struct bnxt *bp) ulp_msix = bnxt_get_avail_msix(bp, bp->ulp_num_msix_want); if (!ulp_msix) bnxt_set_ulp_stat_ctxs(bp, 0); + else + bnxt_set_dflt_ulp_stat_ctxs(bp); if (ulp_msix > bp->ulp_num_msix_want) ulp_msix = bp->ulp_num_msix_want; From f8995c2df519f382525ca4bc90553ad2ec611067 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2026 17:42:51 +0100 Subject: [PATCH 645/712] drm/ioc32: stop speculation on the drm_compat_ioctl path The drm compat ioctl path takes a user controlled pointer, and then dereferences it into a table of function pointers, the signature method of spectre problems. Fix this up by calling array_index_nospec() on the index to the function pointer list. Fixes: 505b5240329b ("drm/ioctl: Fix Spectre v1 vulnerabilities") Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: stable Assisted-by: gkh_clanker_2000 Signed-off-by: Greg Kroah-Hartman Acked-by: Thomas Zimmermann Acked-by: Maxime Ripard Reviewed-by: Simona Vetter Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/2026032451-playing-rummage-8fa2@gregkh --- drivers/gpu/drm/drm_ioc32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index e6b5b06de148..f3e40d1e6098 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -28,6 +28,7 @@ * IN THE SOFTWARE. */ #include +#include #include #include @@ -374,6 +375,7 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (nr >= ARRAY_SIZE(drm_compat_ioctls)) return drm_ioctl(filp, cmd, arg); + nr = array_index_nospec(nr, ARRAY_SIZE(drm_compat_ioctls)); fn = drm_compat_ioctls[nr].fn; if (!fn) return drm_ioctl(filp, cmd, arg); From 0179c6da0793ae03607002c284b53b6d584172d0 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 30 Mar 2026 17:02:42 +0200 Subject: [PATCH 646/712] usb: core: phy: avoid double use of 'usb3-phy' Commit 53a2d95df836 ("usb: core: add phy notify connect and disconnect") causes double use of the 'usb3-phy' in certain cases. Since that commit, if a generic PHY named 'usb3-phy' is specified in the device tree, that is getting added to the 'phy_roothub' list of the secondary HCD by the usb_phy_roothub_alloc_usb3_phy() function. However, that PHY is getting added also to the primary HCD's 'phy_roothub' list by usb_phy_roothub_alloc() if there is no generic PHY specified with 'usb2-phy' name. This causes that the usb_add_hcd() function executes each phy operations twice on the 'usb3-phy'. Once when the primary HCD is added, then once again when the secondary HCD is added. The issue affects the Marvell Armada 3700 platform at least, where a custom name is used for the USB2 PHY: $ git grep 'phy-names.*usb3' arch/arm64/boot/dts/marvell/armada-37xx.dtsi | tr '\t' ' ' arch/arm64/boot/dts/marvell/armada-37xx.dtsi: phy-names = "usb3-phy", "usb2-utmi-otg-phy"; Extend the usb_phy_roothub_alloc_usb3_phy() function to skip adding the 'usb3-phy' to the 'phy_roothub' list of the secondary HCD when 'usb2-phy' is not specified in the device tree to avoid the double use. Fixes: 53a2d95df836 ("usb: core: add phy notify connect and disconnect") Cc: stable Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20260330-usb-avoid-usb3-phy-double-use-v1-1-d2113aecb535@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/phy.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c index 4bba1c275740..4d966cc9cdc9 100644 --- a/drivers/usb/core/phy.c +++ b/drivers/usb/core/phy.c @@ -114,7 +114,7 @@ EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc); struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) { struct usb_phy_roothub *phy_roothub; - int num_phys; + int num_phys, usb2_phy_index; if (!IS_ENABLED(CONFIG_GENERIC_PHY)) return NULL; @@ -124,6 +124,16 @@ struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) if (num_phys <= 0) return NULL; + /* + * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added + * all PHYs to the primary HCD's phy_roothub already, so skip + * adding 'usb3-phy' here to avoid double use of that. + */ + usb2_phy_index = of_property_match_string(dev->of_node, "phy-names", + "usb2-phy"); + if (usb2_phy_index < 0) + return NULL; + phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL); if (!phy_roothub) return ERR_PTR(-ENOMEM); From 7f6f127b9bc34bed35f56faf7ecb1561d6b39000 Mon Sep 17 00:00:00 2001 From: Yongchao Wu Date: Tue, 31 Mar 2026 08:04:07 +0800 Subject: [PATCH 647/712] usb: cdns3: gadget: fix NULL pointer dereference in ep_queue When the gadget endpoint is disabled or not yet configured, the ep->desc pointer can be NULL. This leads to a NULL pointer dereference when __cdns3_gadget_ep_queue() is called, causing a kernel crash. Add a check to return -ESHUTDOWN if ep->desc is NULL, which is the standard return code for unconfigured endpoints. This prevents potential crashes when ep_queue is called on endpoints that are not ready. Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Cc: stable Signed-off-by: Yongchao Wu Acked-by: Peter Chen Link: https://patch.msgid.link/20260331000407.613298-1-yongchao.wu@autochips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index d59a60a16ec7..96d2a4c38b3f 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2589,6 +2589,9 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, struct cdns3_request *priv_req; int ret = 0; + if (!ep->desc) + return -ESHUTDOWN; + request->actual = 0; request->status = -EINPROGRESS; priv_req = to_cdns3_request(request); From 8b7a42ecdcdeb55580d9345412f7f8fc5aca3f6c Mon Sep 17 00:00:00 2001 From: JP Hein Date: Mon, 30 Mar 2026 17:38:04 -0700 Subject: [PATCH 648/712] USB: core: add NO_LPM quirk for Razer Kiyo Pro webcam The Razer Kiyo Pro (1532:0e05) is a USB 3.0 UVC webcam whose firmware does not handle USB Link Power Management transitions reliably. When LPM is active, the device can enter a state where it fails to respond to control transfers, producing EPIPE (-32) errors on UVC probe control SET_CUR requests. In the worst case, the stalled endpoint triggers an xHCI stop-endpoint command that times out, causing the host controller to be declared dead and every USB device on the bus to be disconnected. This has been reported as Ubuntu Launchpad Bug #2061177. The failure mode is: 1. UVC probe control SET_CUR returns -32 (EPIPE) 2. xHCI host not responding to stop endpoint command 3. xHCI host controller not responding, assume dead 4. All USB devices on the affected xHCI controller disconnect Disabling LPM prevents the firmware from entering the problematic low- power states that precede the stall. This is the same approach used for other webcams with similar firmware issues (e.g., Logitech HD Webcam C270). Cc: stable Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2061177 Signed-off-by: JP Hein Link: https://patch.msgid.link/20260331003806.212565-2-jp@jphein.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 26fed25da26e..0ffdaefba508 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -493,6 +493,8 @@ static const struct usb_device_id usb_quirk_list[] = { /* Razer - Razer Blade Keyboard */ { USB_DEVICE(0x1532, 0x0116), .driver_info = USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* Razer - Razer Kiyo Pro Webcam */ + { USB_DEVICE(0x1532, 0x0e05), .driver_info = USB_QUIRK_NO_LPM }, /* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */ { USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME }, From 33cfe0709b6bf1a7f1a16d5e8d65d003a71b6a21 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 31 Mar 2026 20:05:08 +0800 Subject: [PATCH 649/712] usb: misc: usbio: Fix URB memory leak on submit failure When usb_submit_urb() fails in usbio_probe(), the previously allocated URB is never freed, causing a memory leak. Fix this by jumping to err_free_urb label to properly release the URB on the error path. Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Signed-off-by: Felix Gu Reviewed-by: Oliver Neukum Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260331-usbio-v2-1-d8c48dad9463@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 2e68d48a2cc0..02d1e0760f0c 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -614,8 +614,10 @@ static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *i usb_fill_bulk_urb(usbio->urb, udev, usbio->rx_pipe, usbio->rxbuf, usbio->rxbuf_len, usbio_bulk_recv, usbio); ret = usb_submit_urb(usbio->urb, GFP_KERNEL); - if (ret) - return dev_err_probe(dev, ret, "Submitting usb urb\n"); + if (ret) { + dev_err_probe(dev, ret, "Submitting usb urb\n"); + goto err_free_urb; + } mutex_lock(&usbio->ctrl_mutex); @@ -663,6 +665,7 @@ static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *i err_unlock: mutex_unlock(&usbio->ctrl_mutex); usb_kill_urb(usbio->urb); +err_free_urb: usb_free_urb(usbio->urb); return ret; From 01af542392b5d41fd659d487015a71f627accce3 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 1 Apr 2026 10:51:42 +0800 Subject: [PATCH 650/712] usb: ulpi: fix double free in ulpi_register_interface() error path When device_register() fails, ulpi_register() calls put_device() on ulpi->dev. The device release callback ulpi_dev_release() drops the OF node reference and frees ulpi, but the current error path in ulpi_register_interface() then calls kfree(ulpi) again, causing a double free. Let put_device() handle the cleanup through ulpi_dev_release() and avoid freeing ulpi again in ulpi_register_interface(). Fixes: 289fcff4bcdb1 ("usb: add bus type for USB ULPI") Cc: stable Signed-off-by: Guangshuo Li Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260401025142.1398996-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/ulpi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 56c9bfaf2ea3..b34fb65813c4 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -331,10 +331,9 @@ struct ulpi *ulpi_register_interface(struct device *dev, ulpi->ops = ops; ret = ulpi_register(dev, ulpi); - if (ret) { - kfree(ulpi); + if (ret) return ERR_PTR(ret); - } + return ulpi; } From 6e0e34d85cd46ceb37d16054e97a373a32770f6c Mon Sep 17 00:00:00 2001 From: Taegu Ha Date: Thu, 2 Apr 2026 04:13:11 +0900 Subject: [PATCH 651/712] usb: gadget: f_uac1_legacy: validate control request size f_audio_complete() copies req->length bytes into a 4-byte stack variable: u32 data = 0; memcpy(&data, req->buf, req->length); req->length is derived from the host-controlled USB request path, which can lead to a stack out-of-bounds write. Validate req->actual against the expected payload size for the supported control selectors and decode only the expected amount of data. This avoids copying a host-influenced length into a fixed-size stack object. Signed-off-by: Taegu Ha Cc: stable Link: https://patch.msgid.link/20260401191311.3604898-1-hataegu0826@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uac1_legacy.c | 47 ++++++++++++++++----- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index a0c953a99727..5d201a2e30e7 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) { struct f_audio *audio = req->context; - int status = req->status; - u32 data = 0; struct usb_ep *out_ep = audio->out_ep; - switch (status) { - - case 0: /* normal completion? */ - if (ep == out_ep) + switch (req->status) { + case 0: + if (ep == out_ep) { f_audio_out_ep_complete(ep, req); - else if (audio->set_con) { - memcpy(&data, req->buf, req->length); - audio->set_con->set(audio->set_con, audio->set_cmd, - le16_to_cpu(data)); + } else if (audio->set_con) { + struct usb_audio_control *con = audio->set_con; + u8 type = con->type; + u32 data; + bool valid_request = false; + + switch (type) { + case UAC_FU_MUTE: { + u8 value; + + if (req->actual == sizeof(value)) { + memcpy(&value, req->buf, sizeof(value)); + data = value; + valid_request = true; + } + break; + } + case UAC_FU_VOLUME: { + __le16 value; + + if (req->actual == sizeof(value)) { + memcpy(&value, req->buf, sizeof(value)); + data = le16_to_cpu(value); + valid_request = true; + } + break; + } + } + + if (valid_request) + con->set(con, audio->set_cmd, data); + else + usb_ep_set_halt(ep); + audio->set_con = NULL; } break; From 541288339b8cb8bb62be5ec0eab6c65e9dfc6055 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Wed, 1 Apr 2026 21:49:38 +0800 Subject: [PATCH 652/712] usb: dwc3: imx8mp: fix memory leak on probe failure path When platform_get_drvdata() returns NULL and probe defers, the error path jumps to the 'depopulate' label, skipping put_device() for the reference acquired by of_find_device_by_node(). This extra reference prevents the child platform device from being freed when of_platform_depopulate() is called, resulting in memory leaks reported by kmemleak: unreferenced object 0xffff0000c92c1480 (size 64): comm "kworker/u16:2", pid 50, jiffies 4294895789 backtrace (crc 49d507d0): kmemleak_alloc+0x34/0x40 __kmalloc_noprof+0x430/0x670 of_device_alloc+0xec/0x26c of_platform_device_create_pdata+0x60/0x1f0 of_platform_bus_create+0x290/0x610 of_platform_populate+0x74/0x118 dwc3_imx8mp_probe+0x228/0x734 Fixes: 86767625f525 ("usb: dwc3: imx8mp: disable auto suspend for host role") Signed-off-by: Xiaolei Wang Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260401134938.686748-1-xiaolei.wang@windriver.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-imx8mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index b3d7252bd910..1cf96540b66e 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -263,7 +263,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev); if (!dwc3) { err = dev_err_probe(dev, -EPROBE_DEFER, "failed to get dwc3 platform data\n"); - goto depopulate; + goto put_dwc3; } dwc3->glue_ops = &dwc3_imx_glue_ops; From c32f8748d70c8fc77676ad92ed76cede17bf2c48 Mon Sep 17 00:00:00 2001 From: Yongchao Wu Date: Wed, 1 Apr 2026 08:10:00 +0800 Subject: [PATCH 653/712] usb: cdns3: gadget: fix state inconsistency on gadget init failure When cdns3_gadget_start() fails, the DRD hardware is left in gadget mode while software state remains INACTIVE, creating hardware/software state inconsistency. When switching to host mode via sysfs: echo host > /sys/class/usb_role/13180000.usb-role-switch/role The role state is not set to CDNS_ROLE_STATE_ACTIVE due to the error, so cdns_role_stop() skips cleanup because state is still INACTIVE. This violates the DRD controller design specification (Figure22), which requires returning to idle state before switching roles. This leads to a synchronous external abort in xhci_gen_setup() when setting up the host controller: [ 516.440698] configfs-gadget 13180000.usb: failed to start g1: -19 [ 516.442035] cdns-usb3 13180000.usb: Failed to add gadget [ 516.443278] cdns-usb3 13180000.usb: set role 2 has failed ... [ 1301.375722] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller [ 1301.377716] Internal error: synchronous external abort: 96000010 [#1] PREEMPT SMP [ 1301.382485] pc : xhci_gen_setup+0xa4/0x408 [ 1301.393391] backtrace: ... xhci_gen_setup+0xa4/0x408 <-- CRASH xhci_plat_setup+0x44/0x58 usb_add_hcd+0x284/0x678 ... cdns_role_set+0x9c/0xbc <-- Role switch Fix by calling cdns_drd_gadget_off() in the error path to properly clean up the DRD gadget state. Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Cc: stable Signed-off-by: Yongchao Wu Acked-by: Peter Chen Link: https://patch.msgid.link/20260401001000.5761-1-yongchao.wu@autochips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 96d2a4c38b3f..8382231af357 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3431,6 +3431,7 @@ static int __cdns3_gadget_init(struct cdns *cdns) ret = cdns3_gadget_start(cdns); if (ret) { pm_runtime_put_sync(cdns->dev); + cdns_drd_gadget_off(cdns); return ret; } From bd3d245b0fef571f93504904df62b8865b1c0d34 Mon Sep 17 00:00:00 2001 From: Guan-Yu Lin Date: Wed, 1 Apr 2026 12:32:17 +0000 Subject: [PATCH 654/712] usb: core: use dedicated spinlock for offload state Replace the coarse USB device lock with a dedicated offload_lock spinlock to reduce contention during offload operations. Use offload_pm_locked to synchronize with PM transitions and replace the legacy offload_at_suspend flag. Optimize usb_offload_get/put by switching from auto-resume/suspend to pm_runtime_get_if_active(). This ensures offload state is only modified when the device is already active, avoiding unnecessary power transitions. Cc: stable Fixes: ef82a4803aab ("xhci: sideband: add api to trace sideband usage") Signed-off-by: Guan-Yu Lin Tested-by: Hailong Liu Acked-by: Mathias Nyman Link: https://patch.msgid.link/20260401123238.3790062-2-guanyulin@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 23 ++++--- drivers/usb/core/offload.c | 102 ++++++++++++++++++------------- drivers/usb/core/usb.c | 1 + drivers/usb/host/xhci-sideband.c | 4 +- include/linux/usb.h | 10 ++- 5 files changed, 84 insertions(+), 56 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2574e65bc640..f63004417058 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1415,14 +1415,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i = 0, n = 0; struct usb_interface *intf; + bool offload_active = false; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) goto done; + usb_offload_set_pm_locked(udev, true); if (msg.event == PM_EVENT_SUSPEND && usb_offload_check(udev)) { dev_dbg(&udev->dev, "device offloaded, skip suspend.\n"); - udev->offload_at_suspend = 1; + offload_active = true; } /* Suspend all the interfaces and then udev itself */ @@ -1436,8 +1438,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * interrupt urbs, allowing interrupt events to be * handled during system suspend. */ - if (udev->offload_at_suspend && - intf->needs_remote_wakeup) { + if (offload_active && intf->needs_remote_wakeup) { dev_dbg(&intf->dev, "device offloaded, skip suspend.\n"); continue; @@ -1452,7 +1453,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } } if (status == 0) { - if (!udev->offload_at_suspend) + if (!offload_active) status = usb_suspend_device(udev, msg); /* @@ -1498,7 +1499,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) */ } else { udev->can_submit = 0; - if (!udev->offload_at_suspend) { + if (!offload_active) { for (i = 0; i < 16; ++i) { usb_hcd_flush_endpoint(udev, udev->ep_out[i]); usb_hcd_flush_endpoint(udev, udev->ep_in[i]); @@ -1507,6 +1508,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } done: + if (status != 0) + usb_offload_set_pm_locked(udev, false); dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); return status; } @@ -1536,16 +1539,19 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i; struct usb_interface *intf; + bool offload_active = false; if (udev->state == USB_STATE_NOTATTACHED) { status = -ENODEV; goto done; } udev->can_submit = 1; + if (msg.event == PM_EVENT_RESUME) + offload_active = usb_offload_check(udev); /* Resume the device */ if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) { - if (!udev->offload_at_suspend) + if (!offload_active) status = usb_resume_device(udev, msg); else dev_dbg(&udev->dev, @@ -1562,8 +1568,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) * pending interrupt urbs, allowing interrupt events * to be handled during system suspend. */ - if (udev->offload_at_suspend && - intf->needs_remote_wakeup) { + if (offload_active && intf->needs_remote_wakeup) { dev_dbg(&intf->dev, "device offloaded, skip resume.\n"); continue; @@ -1572,11 +1577,11 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) udev->reset_resume); } } - udev->offload_at_suspend = 0; usb_mark_last_busy(udev); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); + usb_offload_set_pm_locked(udev, false); if (!status) udev->reset_resume = 0; return status; diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c index 7c699f1b8d2b..9db3cfedd29c 100644 --- a/drivers/usb/core/offload.c +++ b/drivers/usb/core/offload.c @@ -25,33 +25,30 @@ */ int usb_offload_get(struct usb_device *udev) { - int ret; + int ret = 0; - usb_lock_device(udev); - if (udev->state == USB_STATE_NOTATTACHED) { - usb_unlock_device(udev); + if (!usb_get_dev(udev)) return -ENODEV; + + if (pm_runtime_get_if_active(&udev->dev) != 1) { + ret = -EBUSY; + goto err_rpm; } - if (udev->state == USB_STATE_SUSPENDED || - udev->offload_at_suspend) { - usb_unlock_device(udev); - return -EBUSY; - } + spin_lock(&udev->offload_lock); - /* - * offload_usage could only be modified when the device is active, since - * it will alter the suspend flow of the device. - */ - ret = usb_autoresume_device(udev); - if (ret < 0) { - usb_unlock_device(udev); - return ret; + if (udev->offload_pm_locked) { + ret = -EAGAIN; + goto err; } udev->offload_usage++; - usb_autosuspend_device(udev); - usb_unlock_device(udev); + +err: + spin_unlock(&udev->offload_lock); + pm_runtime_put_autosuspend(&udev->dev); +err_rpm: + usb_put_dev(udev); return ret; } @@ -69,35 +66,32 @@ EXPORT_SYMBOL_GPL(usb_offload_get); */ int usb_offload_put(struct usb_device *udev) { - int ret; + int ret = 0; - usb_lock_device(udev); - if (udev->state == USB_STATE_NOTATTACHED) { - usb_unlock_device(udev); + if (!usb_get_dev(udev)) return -ENODEV; + + if (pm_runtime_get_if_active(&udev->dev) != 1) { + ret = -EBUSY; + goto err_rpm; } - if (udev->state == USB_STATE_SUSPENDED || - udev->offload_at_suspend) { - usb_unlock_device(udev); - return -EBUSY; - } + spin_lock(&udev->offload_lock); - /* - * offload_usage could only be modified when the device is active, since - * it will alter the suspend flow of the device. - */ - ret = usb_autoresume_device(udev); - if (ret < 0) { - usb_unlock_device(udev); - return ret; + if (udev->offload_pm_locked) { + ret = -EAGAIN; + goto err; } /* Drop the count when it wasn't 0, ignore the operation otherwise. */ if (udev->offload_usage) udev->offload_usage--; - usb_autosuspend_device(udev); - usb_unlock_device(udev); + +err: + spin_unlock(&udev->offload_lock); + pm_runtime_put_autosuspend(&udev->dev); +err_rpm: + usb_put_dev(udev); return ret; } @@ -112,25 +106,47 @@ EXPORT_SYMBOL_GPL(usb_offload_put); * management. * * The caller must hold @udev's device lock. In addition, the caller should - * ensure downstream usb devices are all either suspended or marked as - * "offload_at_suspend" to ensure the correctness of the return value. + * ensure the device itself and the downstream usb devices are all marked as + * "offload_pm_locked" to ensure the correctness of the return value. * * Returns true on any offload activity, false otherwise. */ bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex) { struct usb_device *child; - bool active; + bool active = false; int port1; + if (udev->offload_usage) + return true; + usb_hub_for_each_child(udev, port1, child) { usb_lock_device(child); active = usb_offload_check(child); usb_unlock_device(child); + if (active) - return true; + break; } - return !!udev->offload_usage; + return active; } EXPORT_SYMBOL_GPL(usb_offload_check); + +/** + * usb_offload_set_pm_locked - set the PM lock state of a USB device + * @udev: the USB device to modify + * @locked: the new lock state + * + * Setting @locked to true prevents offload_usage from being modified. This + * ensures that offload activities cannot be started or stopped during critical + * power management transitions, maintaining a stable state for the duration + * of the transition. + */ +void usb_offload_set_pm_locked(struct usb_device *udev, bool locked) +{ + spin_lock(&udev->offload_lock); + udev->offload_pm_locked = locked; + spin_unlock(&udev->offload_lock); +} +EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e9a10a33534c..df166cafe106 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -671,6 +671,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, set_dev_node(&dev->dev, dev_to_node(bus->sysdev)); dev->state = USB_STATE_ATTACHED; dev->lpm_disable_count = 1; + spin_lock_init(&dev->offload_lock); dev->offload_usage = 0; atomic_set(&dev->urbnum, 0); diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index abbcc0e44f1b..54284d29d201 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -291,8 +291,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer); * Allow other drivers, such as usb controller driver, to check if there are * any sideband activity on the host controller. This information could be used * for power management or other forms of resource management. The caller should - * ensure downstream usb devices are all either suspended or marked as - * "offload_at_suspend" to ensure the correctness of the return value. + * ensure downstream usb devices are all marked as "offload_pm_locked" to ensure + * the correctness of the return value. * * Returns true on any active sideband existence, false otherwise. */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 04277af4bb9d..4aab20015851 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -21,6 +21,7 @@ #include /* for struct completion */ #include /* for current && schedule_timeout */ #include /* for struct mutex */ +#include /* for spinlock_t */ #include /* for runtime PM */ struct usb_device; @@ -636,8 +637,9 @@ struct usb3_lpm_parameters { * @do_remote_wakeup: remote wakeup should be enabled * @reset_resume: needs reset instead of resume * @port_is_suspended: the upstream port is suspended (L2 or U3) - * @offload_at_suspend: offload activities during suspend is enabled. + * @offload_pm_locked: prevents offload_usage changes during PM transitions. * @offload_usage: number of offload activities happening on this usb device. + * @offload_lock: protects offload_usage and offload_pm_locked * @slot_id: Slot ID assigned by xHCI * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout. * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout. @@ -726,8 +728,9 @@ struct usb_device { unsigned do_remote_wakeup:1; unsigned reset_resume:1; unsigned port_is_suspended:1; - unsigned offload_at_suspend:1; + unsigned offload_pm_locked:1; int offload_usage; + spinlock_t offload_lock; enum usb_link_tunnel_mode tunnel_mode; struct device_link *usb4_link; @@ -849,6 +852,7 @@ static inline void usb_mark_last_busy(struct usb_device *udev) int usb_offload_get(struct usb_device *udev); int usb_offload_put(struct usb_device *udev); bool usb_offload_check(struct usb_device *udev); +void usb_offload_set_pm_locked(struct usb_device *udev, bool locked); #else static inline int usb_offload_get(struct usb_device *udev) @@ -857,6 +861,8 @@ static inline int usb_offload_put(struct usb_device *udev) { return 0; } static inline bool usb_offload_check(struct usb_device *udev) { return false; } +static inline void usb_offload_set_pm_locked(struct usb_device *udev, bool locked) +{ } #endif extern int usb_disable_lpm(struct usb_device *udev); From 5abbe6ecc6203355c770bf232ade88e29c960049 Mon Sep 17 00:00:00 2001 From: Guan-Yu Lin Date: Wed, 1 Apr 2026 12:32:18 +0000 Subject: [PATCH 655/712] usb: host: xhci-sideband: delegate offload_usage tracking to class drivers Remove usb_offload_get() and usb_offload_put() from the xHCI sideband interrupter creation and removal paths. The responsibility of manipulating offload_usage now lies entirely with the USB class drivers. They have the precise context of when an offload data stream actually starts and stops, ensuring a much more accurate representation of offload activity for power management. Cc: stable Fixes: ef82a4803aab ("xhci: sideband: add api to trace sideband usage") Signed-off-by: Guan-Yu Lin Tested-by: Hailong Liu Tested-by: hailong.liu@oppo.com Acked-by: Mathias Nyman Link: https://patch.msgid.link/20260401123238.3790062-3-guanyulin@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-sideband.c | 14 +------------- sound/usb/qcom/qc_audio_offload.c | 10 +++++++++- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index 54284d29d201..23153e136d4b 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -93,8 +93,6 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e static void __xhci_sideband_remove_interrupter(struct xhci_sideband *sb) { - struct usb_device *udev; - lockdep_assert_held(&sb->mutex); if (!sb->ir) @@ -102,10 +100,6 @@ __xhci_sideband_remove_interrupter(struct xhci_sideband *sb) xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir); sb->ir = NULL; - udev = sb->vdev->udev; - - if (udev->state != USB_STATE_NOTATTACHED) - usb_offload_put(udev); } /* sideband api functions */ @@ -328,9 +322,6 @@ int xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg, bool ip_autoclear, u32 imod_interval, int intr_num) { - int ret = 0; - struct usb_device *udev; - if (!sb || !sb->xhci) return -ENODEV; @@ -348,12 +339,9 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg, if (!sb->ir) return -ENOMEM; - udev = sb->vdev->udev; - ret = usb_offload_get(udev); - sb->ir->ip_autoclear = ip_autoclear; - return ret; + return 0; } EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter); diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 510b68cced33..a8d313fa254a 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -699,6 +699,7 @@ static void uaudio_event_ring_cleanup_free(struct uaudio_dev *dev) uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE); xhci_sideband_remove_interrupter(uadev[dev->chip->card->number].sb); + usb_offload_put(dev->udev); } } @@ -1182,12 +1183,16 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev); er_pa = 0; + ret = usb_offload_get(subs->dev); + if (ret < 0) + goto exit; + /* event ring */ ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false, 0, uaudio_qdev->data->intr_num); if (ret < 0) { dev_err(&subs->dev->dev, "failed to fetch interrupter\n"); - goto exit; + goto put_offload; } sgt = xhci_sideband_get_event_buffer(uadev[card_num].sb); @@ -1219,6 +1224,8 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, mem_info->dma = 0; remove_interrupter: xhci_sideband_remove_interrupter(uadev[card_num].sb); +put_offload: + usb_offload_put(subs->dev); exit: return ret; } @@ -1482,6 +1489,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE); free_sec_ring: xhci_sideband_remove_interrupter(uadev[card_num].sb); + usb_offload_put(subs->dev); drop_sync_ep: if (subs->sync_endpoint) { uaudio_iommu_unmap(MEM_XFER_RING, From 4e0a88254ad59f6c53a34bf5fa241884ec09e8b2 Mon Sep 17 00:00:00 2001 From: Michael Zimmermann Date: Tue, 31 Mar 2026 20:48:44 +0200 Subject: [PATCH 656/712] usb: gadget: f_hid: move list and spinlock inits from bind to alloc There was an issue when you did the following: - setup and bind an hid gadget - open /dev/hidg0 - use the resulting fd in EPOLL_CTL_ADD - unbind the UDC - bind the UDC - use the fd in EPOLL_CTL_DEL When CONFIG_DEBUG_LIST was enabled, a list_del corruption was reported within remove_wait_queue (via ep_remove_wait_queue). After some debugging I found out that the queues, which f_hid registers via poll_wait were the problem. These were initialized using init_waitqueue_head inside hidg_bind. So effectively, the bind function re-initialized the queues while there were still items in them. The solution is to move the initialization from hidg_bind to hidg_alloc to extend their lifetimes to the lifetime of the function instance. Additionally, I found many other possibly problematic init calls in the bind function, which I moved as well. Signed-off-by: Michael Zimmermann Cc: stable Link: https://patch.msgid.link/20260331184844.2388761-1-sigmaepsilon92@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_hid.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 8812ebf33d14..e5ccaec7750c 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1262,17 +1262,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail; - spin_lock_init(&hidg->write_spinlock); hidg->write_pending = 1; hidg->req = NULL; - spin_lock_init(&hidg->read_spinlock); - spin_lock_init(&hidg->get_report_spinlock); - init_waitqueue_head(&hidg->write_queue); - init_waitqueue_head(&hidg->read_queue); - init_waitqueue_head(&hidg->get_queue); - init_waitqueue_head(&hidg->get_id_queue); - INIT_LIST_HEAD(&hidg->completed_out_req); - INIT_LIST_HEAD(&hidg->report_list); INIT_WORK(&hidg->work, get_report_workqueue_handler); hidg->workqueue = alloc_workqueue("report_work", @@ -1608,6 +1599,16 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) mutex_lock(&opts->lock); + spin_lock_init(&hidg->write_spinlock); + spin_lock_init(&hidg->read_spinlock); + spin_lock_init(&hidg->get_report_spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); + init_waitqueue_head(&hidg->get_queue); + init_waitqueue_head(&hidg->get_id_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); + INIT_LIST_HEAD(&hidg->report_list); + device_initialize(&hidg->dev); hidg->dev.release = hidg_release; hidg->dev.class = &hidg_class; From 9e07e3b81807edd356e1f794cffa00a428eff443 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 1 Apr 2026 16:33:53 +0200 Subject: [PATCH 657/712] thermal: core: Fix thermal zone device registration error path If thermal_zone_device_register_with_trips() fails after registering a thermal zone device, it needs to wait for the tz->removal completion like thermal_zone_device_unregister(), in case user space has managed to take a reference to the thermal zone device's kobject, in which case thermal_release() may not be called by the error path itself and tz may be freed prematurely. Add the missing wait_for_completion() call to the thermal zone device registration error path. Fixes: 04e6ccfc93c5 ("thermal: core: Fix NULL pointer dereference in zone registration error path") Signed-off-by: Rafael J. Wysocki Cc: All applicable Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba Link: https://patch.msgid.link/2849815.mvXUDI8C0e@rafael.j.wysocki --- drivers/thermal/thermal_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 5337612e484d..d1beee9e15f8 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1642,6 +1642,7 @@ thermal_zone_device_register_with_trips(const char *type, device_del(&tz->device); release_device: put_device(&tz->device); + wait_for_completion(&tz->removal); remove_id: ida_free(&thermal_tz_ida, id); free_tzp: From d10a26aa4d072320530e6968ef945c8c575edf61 Mon Sep 17 00:00:00 2001 From: Martin Schiller Date: Tue, 31 Mar 2026 09:43:17 +0200 Subject: [PATCH 658/712] net/x25: Fix potential double free of skb When alloc_skb fails in x25_queue_rx_frame it calls kfree_skb(skb) at line 48 and returns 1 (error). This error propagates back through the call chain: x25_queue_rx_frame returns 1 | v x25_state3_machine receives the return value 1 and takes the else branch at line 278, setting queued=0 and returning 0 | v x25_process_rx_frame returns queued=0 | v x25_backlog_rcv at line 452 sees queued=0 and calls kfree_skb(skb) again This would free the same skb twice. Looking at x25_backlog_rcv: net/x25/x25_in.c:x25_backlog_rcv() { ... queued = x25_process_rx_frame(sk, skb); ... if (!queued) kfree_skb(skb); } Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Martin Schiller Link: https://patch.msgid.link/20260331-x25_fraglen-v4-1-3e69f18464b4@dev.tdt.de Signed-off-by: Paolo Abeni --- net/x25/x25_in.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index b981a4828d08..0dbc73efab1c 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -44,10 +44,9 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) if (x25->fraglen > 0) { /* End of fragment */ int len = x25->fraglen + skb->len; - if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL){ - kfree_skb(skb); + skbn = alloc_skb(len, GFP_ATOMIC); + if (!skbn) return 1; - } skb_queue_tail(&x25->fragment_queue, skb); From a1822cb524e89b4cd2cf0b82e484a2335496a6d9 Mon Sep 17 00:00:00 2001 From: Martin Schiller Date: Tue, 31 Mar 2026 09:43:18 +0200 Subject: [PATCH 659/712] net/x25: Fix overflow when accumulating packets Add a check to ensure that `x25_sock.fraglen` does not overflow. The `fraglen` also needs to be resetted when purging `fragment_queue` in `x25_clear_queues()`. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Suggested-by: Yiming Qian Signed-off-by: Martin Schiller Link: https://patch.msgid.link/20260331-x25_fraglen-v4-2-3e69f18464b4@dev.tdt.de Signed-off-by: Paolo Abeni --- net/x25/x25_in.c | 4 ++++ net/x25/x25_subr.c | 1 + 2 files changed, 5 insertions(+) diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index 0dbc73efab1c..e47ebd8acd21 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -34,6 +34,10 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) struct sk_buff *skbo, *skbn = skb; struct x25_sock *x25 = x25_sk(sk); + /* make sure we don't overflow */ + if (x25->fraglen + skb->len > USHRT_MAX) + return 1; + if (more) { x25->fraglen += skb->len; skb_queue_tail(&x25->fragment_queue, skb); diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c index 0285aaa1e93c..159708d9ad20 100644 --- a/net/x25/x25_subr.c +++ b/net/x25/x25_subr.c @@ -40,6 +40,7 @@ void x25_clear_queues(struct sock *sk) skb_queue_purge(&x25->interrupt_in_queue); skb_queue_purge(&x25->interrupt_out_queue); skb_queue_purge(&x25->fragment_queue); + x25->fraglen = 0; } From 1319ea57529e131822bab56bf417c8edc2db9ae8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 1 Apr 2026 15:20:20 +0200 Subject: [PATCH 660/712] sched/fair: Fix zero_vruntime tracking fix John reported that stress-ng-yield could make his machine unhappy and managed to bisect it to commit b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking"). The combination of yield and that commit was specific enough to hypothesize the following scenario: Suppose we have 2 runnable tasks, both doing yield. Then one will be eligible and one will not be, because the average position must be in between these two entities. Therefore, the runnable task will be eligible, and be promoted a full slice (all the tasks do is yield after all). This causes it to jump over the other task and now the other task is eligible and current is no longer. So we schedule. Since we are runnable, there is no {de,en}queue. All we have is the __{en,de}queue_entity() from {put_prev,set_next}_task(). But per the fingered commit, those two no longer move zero_vruntime. All that moves zero_vruntime are tick and full {de,en}queue. This means, that if the two tasks playing leapfrog can reach the critical speed to reach the overflow point inside one tick's worth of time, we're up a creek. Additionally, when multiple cgroups are involved, there is no guarantee the tick will in fact hit every cgroup in a timely manner. Statistically speaking it will, but that same statistics does not rule out the possibility of one cgroup not getting a tick for a significant amount of time -- however unlikely. Therefore, just like with the yield() case, force an update at the end of every slice. This ensures the update is never more than a single slice behind and the whole thing is within 2 lag bounds as per the comment on entity_key(). Fixes: b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking") Reported-by: John Stultz Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Vincent Guittot Tested-by: K Prateek Nayak Tested-by: John Stultz Link: https://patch.msgid.link/20260401132355.081530332@infradead.org --- kernel/sched/fair.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index bf948db905ed..ab4114712be7 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -707,7 +707,7 @@ void update_zero_vruntime(struct cfs_rq *cfs_rq, s64 delta) * Called in: * - place_entity() -- before enqueue * - update_entity_lag() -- before dequeue - * - entity_tick() + * - update_deadline() -- slice expiration * * This means it is one entry 'behind' but that puts it close enough to where * the bound on entity_key() is at most two lag bounds. @@ -1131,6 +1131,7 @@ static bool update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se) * EEVDF: vd_i = ve_i + r_i / w_i */ se->deadline = se->vruntime + calc_delta_fair(se->slice, se); + avg_vruntime(cfs_rq); /* * The task has consumed its request, reschedule. @@ -5593,11 +5594,6 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) update_load_avg(cfs_rq, curr, UPDATE_TG); update_cfs_group(curr); - /* - * Pulls along cfs_rq::zero_vruntime. - */ - avg_vruntime(cfs_rq); - #ifdef CONFIG_SCHED_HRTICK /* * queued ticks are scheduled to match the slice, so don't bother @@ -9128,7 +9124,7 @@ static void yield_task_fair(struct rq *rq) */ if (entity_eligible(cfs_rq, se)) { se->vruntime = se->deadline; - se->deadline += calc_delta_fair(se->slice, se); + update_deadline(cfs_rq, se); } } From e08d007f9d813616ce7093600bc4fdb9c9d81d89 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 1 Apr 2026 15:20:21 +0200 Subject: [PATCH 661/712] sched/debug: Fix avg_vruntime() usage John reported that stress-ng-yield could make his machine unhappy and managed to bisect it to commit b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking"). The commit in question changes avg_vruntime() from a function that is a pure reader, to a function that updates variables. This turns an unlocked sched/debug usage of this function from a minor mistake into a data corruptor. Fixes: af4cf40470c2 ("sched/fair: Add cfs_rq::avg_vruntime") Fixes: b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking") Reported-by: John Stultz Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Vincent Guittot Tested-by: K Prateek Nayak Tested-by: John Stultz Link: https://patch.msgid.link/20260401132355.196370805@infradead.org --- kernel/sched/debug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index b24f40f05019..15bf45b6f912 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -902,6 +902,7 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu) void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) { s64 left_vruntime = -1, zero_vruntime, right_vruntime = -1, left_deadline = -1, spread; + u64 avruntime; struct sched_entity *last, *first, *root; struct rq *rq = cpu_rq(cpu); unsigned long flags; @@ -925,6 +926,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) if (last) right_vruntime = last->vruntime; zero_vruntime = cfs_rq->zero_vruntime; + avruntime = avg_vruntime(cfs_rq); raw_spin_rq_unlock_irqrestore(rq, flags); SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "left_deadline", @@ -934,7 +936,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "zero_vruntime", SPLIT_NS(zero_vruntime)); SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "avg_vruntime", - SPLIT_NS(avg_vruntime(cfs_rq))); + SPLIT_NS(avruntime)); SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "right_vruntime", SPLIT_NS(right_vruntime)); spread = right_vruntime - left_vruntime; From dbde07f06226438cd2cf1179745fa1bec5d8914a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 12 Mar 2026 12:43:05 -0700 Subject: [PATCH 662/712] perf/x86: Fix potential bad container_of in intel_pmu_hw_config Auto counter reload may have a group of events with software events present within it. The software event PMU isn't the x86_hybrid_pmu and a container_of operation in intel_pmu_set_acr_caused_constr (via the hybrid helper) could cause out of bound memory reads. Avoid this by guarding the call to intel_pmu_set_acr_caused_constr with an is_x86_event check. Fixes: ec980e4facef ("perf/x86/intel: Support auto counter reload") Signed-off-by: Ian Rogers Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Thomas Falcon Link: https://patch.msgid.link/20260312194305.1834035-1-irogers@google.com --- arch/x86/events/intel/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 36c68210d4d2..793335c3ce78 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -4855,8 +4855,10 @@ static int intel_pmu_hw_config(struct perf_event *event) intel_pmu_set_acr_caused_constr(leader, idx++, cause_mask); if (leader->nr_siblings) { - for_each_sibling_event(sibling, leader) - intel_pmu_set_acr_caused_constr(sibling, idx++, cause_mask); + for_each_sibling_event(sibling, leader) { + if (is_x86_event(sibling)) + intel_pmu_set_acr_caused_constr(sibling, idx++, cause_mask); + } } if (leader != event) From 579af7204d762587f9cce0d6236a710a771f1f6f Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Mon, 2 Feb 2026 10:47:55 +0100 Subject: [PATCH 663/712] gpib: Fix fluke driver s390 compile issue The following errors were reported for a s390 randconfig build of the fluke gpib driver: >> drivers/gpib/eastwood/fluke_gpib.c:1002:23: error: call to undeclared function 'ioremap'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 1002 | nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, | ^ >> drivers/gpib/eastwood/fluke_gpib.c:1002:21: error: incompatible integer to pointer conversion assigning to 'void *' from 'int' [-Wint-conversion] 1002 | nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1003 | resource_size(e_priv->gpib_iomem_res)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/gpib/eastwood/fluke_gpib.c:1036:33: error: incompatible integer to pointer conversion assigning to 'void *' from 'int' [-Wint-conversion] 1036 | e_priv->write_transfer_counter = ioremap(e_priv->write_transfer_counter_res->start, | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1037 | resource_size(e_priv->write_transfer_counter_res)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add HAS_IOMEM dependency to Kconfig for fluke driver option Suggested-by: Arnd Bergmann Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601221748.AFAqHieJ-lkp@intel.com/ Fixes: baf8855c9160 ("staging: gpib: fix address space mixup") Cc: stable Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260202094755.4259-1-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpib/Kconfig b/drivers/gpib/Kconfig index eeb50956ce85..d43a28c62ed7 100644 --- a/drivers/gpib/Kconfig +++ b/drivers/gpib/Kconfig @@ -122,6 +122,7 @@ config GPIB_FLUKE depends on OF select GPIB_COMMON select GPIB_NEC7210 + depends on HAS_IOMEM help GPIB driver for Fluke based cda devices. From 5cefb52c1af6f69ea719e42788f6ec6a087eb74c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Mar 2026 11:51:27 +0100 Subject: [PATCH 664/712] gpib: lpvo_usb: fix memory leak on disconnect The driver iterates over the registered USB interfaces during GPIB attach and takes a reference to their USB devices until a match is found. These references are never released which leads to a memory leak when devices are disconnected. Fix the leak by dropping the unnecessary references. Fixes: fce79512a96a ("staging: gpib: Add LPVO DIY USB GPIB driver") Cc: stable # 6.13 Cc: Dave Penkler Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260310105127.17538-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index ee781d2f0b8e..0f9d385bc50b 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -406,7 +406,7 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con for (j = 0 ; j < MAX_DEV ; j++) { if ((assigned_usb_minors & 1 << j) == 0) continue; - udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + udev = interface_to_usbdev(lpvo_usb_interfaces[j]); device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL); match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev, config->device_path); @@ -421,7 +421,7 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con for (j = 0 ; j < MAX_DEV ; j++) { if ((assigned_usb_minors & 1 << j) == 0) continue; - udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + udev = interface_to_usbdev(lpvo_usb_interfaces[j]); DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j, udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot); if (config->pci_bus == udev->bus->busnum && From d1857f8296dceb75d00ab857fc3c61bc00c7f5c6 Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Tue, 17 Mar 2026 19:25:28 +0700 Subject: [PATCH 665/712] gpib: fix use-after-free in IO ioctl handlers The IBRD, IBWRT, IBCMD, and IBWAIT ioctl handlers use a gpib_descriptor pointer after board->big_gpib_mutex has been released. A concurrent IBCLOSEDEV ioctl can free the descriptor via close_dev_ioctl() during this window, causing a use-after-free. The IO handlers (read_ioctl, write_ioctl, command_ioctl) explicitly release big_gpib_mutex before calling their handler. wait_ioctl() is called with big_gpib_mutex held, but ibwait() releases it internally when wait_mask is non-zero. In all four cases, the descriptor pointer obtained from handle_to_descriptor() becomes unprotected. Fix this by introducing a kernel-only descriptor_busy reference count in struct gpib_descriptor. Each handler atomically increments descriptor_busy under file_priv->descriptors_mutex before releasing the lock, and decrements it when done. close_dev_ioctl() checks descriptor_busy under the same lock and rejects the close with -EBUSY if the count is non-zero. A reference count rather than a simple flag is necessary because multiple handlers can operate on the same descriptor concurrently (e.g. IBRD and IBWAIT on the same handle from different threads). A separate counter is needed because io_in_progress can be cleared from unprivileged userspace via the IBWAIT ioctl (through general_ibstatus() with set_mask containing CMPL), which would allow an attacker to bypass a check based solely on io_in_progress. The new descriptor_busy counter is only modified by the kernel IO paths. The lock ordering is consistent (big_gpib_mutex -> descriptors_mutex) and the handlers only hold descriptors_mutex briefly during the lookup, so there is no deadlock risk and no impact on IO throughput. Signed-off-by: Adam Crosser Cc: stable Reviewed-by: Dave Penkler Tested-by: Dave Penkler Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/common/gpib_os.c | 96 +++++++++++++++++++++++-------- drivers/gpib/include/gpib_types.h | 8 +++ 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index be757db993a5..97c98f0a7a43 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -888,10 +888,6 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count) return -EINVAL; - desc = handle_to_descriptor(file_priv, read_cmd.handle); - if (!desc) - return -EINVAL; - if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr))) return -EFAULT; @@ -904,6 +900,17 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo if (!access_ok(userbuf, remain)) return -EFAULT; + /* Lock descriptors to prevent concurrent close from freeing descriptor */ + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + desc = handle_to_descriptor(file_priv, read_cmd.handle); + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); + atomic_set(&desc->io_in_progress, 1); /* Read buffer loads till we fill the user supplied buffer */ @@ -937,6 +944,7 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd)); atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); if (retval) @@ -964,10 +972,6 @@ static int command_ioctl(struct gpib_file_private *file_priv, if (cmd.completed_transfer_count > cmd.requested_transfer_count) return -EINVAL; - desc = handle_to_descriptor(file_priv, cmd.handle); - if (!desc) - return -EINVAL; - userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr; userbuf += cmd.completed_transfer_count; @@ -980,6 +984,17 @@ static int command_ioctl(struct gpib_file_private *file_priv, if (!access_ok(userbuf, remain)) return -EFAULT; + /* Lock descriptors to prevent concurrent close from freeing descriptor */ + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); + /* * Write buffer loads till we empty the user supplied buffer. * Call drivers at least once, even if remain is zero, in @@ -1003,6 +1018,7 @@ static int command_ioctl(struct gpib_file_private *file_priv, userbuf += bytes_written; if (retval < 0) { atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); break; @@ -1022,6 +1038,7 @@ static int command_ioctl(struct gpib_file_private *file_priv, */ if (!no_clear_io_in_prog || fault) atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); if (fault) @@ -1047,10 +1064,6 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count) return -EINVAL; - desc = handle_to_descriptor(file_priv, write_cmd.handle); - if (!desc) - return -EINVAL; - userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr; userbuf += write_cmd.completed_transfer_count; @@ -1060,6 +1073,17 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b if (!access_ok(userbuf, remain)) return -EFAULT; + /* Lock descriptors to prevent concurrent close from freeing descriptor */ + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + desc = handle_to_descriptor(file_priv, write_cmd.handle); + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); + atomic_set(&desc->io_in_progress, 1); /* Write buffer loads till we empty the user supplied buffer */ @@ -1094,6 +1118,7 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd)); atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); if (fault) @@ -1276,6 +1301,9 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne { struct gpib_close_dev_ioctl cmd; struct gpib_file_private *file_priv = filep->private_data; + struct gpib_descriptor *desc; + unsigned int pad; + int sad; int retval; retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); @@ -1284,19 +1312,27 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS) return -EINVAL; - if (!file_priv->descriptors[cmd.handle]) + + mutex_lock(&file_priv->descriptors_mutex); + desc = file_priv->descriptors[cmd.handle]; + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); return -EINVAL; - - retval = decrement_open_device_count(board, &board->device_list, - file_priv->descriptors[cmd.handle]->pad, - file_priv->descriptors[cmd.handle]->sad); - if (retval < 0) - return retval; - - kfree(file_priv->descriptors[cmd.handle]); + } + if (atomic_read(&desc->descriptor_busy)) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EBUSY; + } + /* Remove from table while holding lock to prevent new IO from starting */ file_priv->descriptors[cmd.handle] = NULL; + pad = desc->pad; + sad = desc->sad; + mutex_unlock(&file_priv->descriptors_mutex); - return 0; + retval = decrement_open_device_count(board, &board->device_list, pad, sad); + + kfree(desc); + return retval; } static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg) @@ -1331,12 +1367,25 @@ static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo if (retval) return -EFAULT; + /* + * Lock descriptors to prevent concurrent close from freeing + * descriptor. ibwait() releases big_gpib_mutex when wait_mask + * is non-zero, so desc must be pinned with descriptor_busy. + */ + mutex_lock(&file_priv->descriptors_mutex); desc = handle_to_descriptor(file_priv, wait_cmd.handle); - if (!desc) + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask, wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc); + + atomic_dec(&desc->descriptor_busy); + if (retval < 0) return retval; @@ -2035,6 +2084,7 @@ void init_gpib_descriptor(struct gpib_descriptor *desc) desc->is_board = 0; desc->autopoll_enabled = 0; atomic_set(&desc->io_in_progress, 0); + atomic_set(&desc->descriptor_busy, 0); } int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module) diff --git a/drivers/gpib/include/gpib_types.h b/drivers/gpib/include/gpib_types.h index 5a0978ae27e7..28b73157ffb7 100644 --- a/drivers/gpib/include/gpib_types.h +++ b/drivers/gpib/include/gpib_types.h @@ -364,6 +364,14 @@ struct gpib_descriptor { unsigned int pad; /* primary gpib address */ int sad; /* secondary gpib address (negative means disabled) */ atomic_t io_in_progress; + /* + * Kernel-only reference count to prevent descriptor from being + * freed while IO handlers hold a pointer to it. Incremented + * before each IO operation, decremented when done. Unlike + * io_in_progress, this cannot be modified from userspace via + * general_ibstatus(). + */ + atomic_t descriptor_busy; unsigned is_board : 1; unsigned autopoll_enabled : 1; }; From 101ab946b79ad83b36d5cfd47de587492a80acf0 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 28 Jan 2026 15:00:10 +0000 Subject: [PATCH 666/712] comedi: ni_atmio16d: Fix invalid clean-up after failed attach If the driver's COMEDI "attach" handler function (`atmio16d_attach()`) returns an error, the COMEDI core will call the driver's "detach" handler function (`atmio16d_detach()`) to clean up. This calls `reset_atmio16d()` unconditionally, but depending on where the error occurred in the attach handler, the device may not have been sufficiently initialized to call `reset_atmio16d()`. It uses `dev->iobase` as the I/O port base address and `dev->private` as the pointer to the COMEDI device's private data structure. `dev->iobase` may still be set to its initial value of 0, which would result in undesired writes to low I/O port addresses. `dev->private` may still be `NULL`, which would result in null pointer dereferences. Fix `atmio16d_detach()` by checking that `dev->private` is valid (non-null) before calling `reset_atmio16d()`. This implies that `dev->iobase` was set correctly since that is set up before `dev->private`. Fixes: 2323b276308a ("Staging: comedi: add ni_at_atmio16d driver") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260128150011.5006-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_atmio16d.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index e5e7cc423c87..b057b3b3582e 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -698,7 +698,8 @@ static int atmio16d_attach(struct comedi_device *dev, static void atmio16d_detach(struct comedi_device *dev) { - reset_atmio16d(dev); + if (dev->private) + reset_atmio16d(dev); comedi_legacy_detach(dev); } From 3fb43a7a5b44713f892c58ead2e5f3a1bc9f4ee7 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 5 Feb 2026 13:39:49 +0000 Subject: [PATCH 667/712] comedi: me4000: Fix potential overrun of firmware buffer `me4000_xilinx_download()` loads the firmware that was requested by `request_firmware()`. It is possible for it to overrun the source buffer because it blindly trusts the file format. It reads a data stream length from the first 4 bytes into variable `file_length` and reads the data stream contents of length `file_length` from offset 16 onwards. Add a test to ensure that the supplied firmware is long enough to contain the header and the data stream. On failure, log an error and return `-EINVAL`. Note: The firmware loading was totally broken before commit ac584af59945 ("staging: comedi: me4000: fix firmware downloading"), but that is the most sensible target for this fix. Fixes: ac584af59945 ("staging: comedi: me4000: fix firmware downloading") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260205133949.71722-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/me4000.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/comedi/drivers/me4000.c b/drivers/comedi/drivers/me4000.c index 7dd3a0071863..effe9fdbbafe 100644 --- a/drivers/comedi/drivers/me4000.c +++ b/drivers/comedi/drivers/me4000.c @@ -315,6 +315,18 @@ static int me4000_xilinx_download(struct comedi_device *dev, unsigned int val; unsigned int i; + /* Get data stream length from header. */ + if (size >= 4) { + file_length = (((unsigned int)data[0] & 0xff) << 24) + + (((unsigned int)data[1] & 0xff) << 16) + + (((unsigned int)data[2] & 0xff) << 8) + + ((unsigned int)data[3] & 0xff); + } + if (size < 16 || file_length > size - 16) { + dev_err(dev->class_dev, "Firmware length inconsistency\n"); + return -EINVAL; + } + if (!xilinx_iobase) return -ENODEV; @@ -346,10 +358,6 @@ static int me4000_xilinx_download(struct comedi_device *dev, outl(val, devpriv->plx_regbase + PLX9052_CNTRL); /* Download Xilinx firmware */ - file_length = (((unsigned int)data[0] & 0xff) << 24) + - (((unsigned int)data[1] & 0xff) << 16) + - (((unsigned int)data[2] & 0xff) << 8) + - ((unsigned int)data[3] & 0xff); usleep_range(10, 1000); for (i = 0; i < file_length; i++) { From cc797d4821c754c701d9714b58bea947e31dbbe0 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 5 Feb 2026 14:01:30 +0000 Subject: [PATCH 668/712] comedi: me_daq: Fix potential overrun of firmware buffer `me2600_xilinx_download()` loads the firmware that was requested by `request_firmware()`. It is possible for it to overrun the source buffer because it blindly trusts the file format. It reads a data stream length from the first 4 bytes into variable `file_length` and reads the data stream contents of length `file_length` from offset 16 onwards. Although it checks that the supplied firmware is at least 16 bytes long, it does not check that it is long enough to contain the data stream. Add a test to ensure that the supplied firmware is long enough to contain the header and the data stream. On failure, log an error and return `-EINVAL`. Fixes: 85acac61096f9 ("Staging: comedi: add me_daq driver") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260205140130.76697-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/me_daq.c | 35 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/comedi/drivers/me_daq.c b/drivers/comedi/drivers/me_daq.c index 076b15097afd..2f2ea029cffc 100644 --- a/drivers/comedi/drivers/me_daq.c +++ b/drivers/comedi/drivers/me_daq.c @@ -344,6 +344,25 @@ static int me2600_xilinx_download(struct comedi_device *dev, unsigned int file_length; unsigned int i; + /* + * Format of the firmware + * Build longs from the byte-wise coded header + * Byte 1-3: length of the array + * Byte 4-7: version + * Byte 8-11: date + * Byte 12-15: reserved + */ + if (size >= 4) { + file_length = (((unsigned int)data[0] & 0xff) << 24) + + (((unsigned int)data[1] & 0xff) << 16) + + (((unsigned int)data[2] & 0xff) << 8) + + ((unsigned int)data[3] & 0xff); + } + if (size < 16 || file_length > size - 16) { + dev_err(dev->class_dev, "Firmware length inconsistency\n"); + return -EINVAL; + } + /* disable irq's on PLX */ writel(0x00, devpriv->plx_regbase + PLX9052_INTCSR); @@ -357,22 +376,6 @@ static int me2600_xilinx_download(struct comedi_device *dev, writeb(0x00, dev->mmio + 0x0); sleep(1); - /* - * Format of the firmware - * Build longs from the byte-wise coded header - * Byte 1-3: length of the array - * Byte 4-7: version - * Byte 8-11: date - * Byte 12-15: reserved - */ - if (size < 16) - return -EINVAL; - - file_length = (((unsigned int)data[0] & 0xff) << 24) + - (((unsigned int)data[1] & 0xff) << 16) + - (((unsigned int)data[2] & 0xff) << 8) + - ((unsigned int)data[3] & 0xff); - /* * Loop for writing firmware byte by byte to xilinx * Firmware data start at offset 16 From 4b9a9a6d71e3e252032f959fb3895a33acb5865c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 25 Feb 2026 13:24:27 +0000 Subject: [PATCH 669/712] comedi: Reinit dev->spinlock between attachments to low-level drivers `struct comedi_device` is the main controlling structure for a COMEDI device created by the COMEDI subsystem. It contains a member `spinlock` containing a spin-lock that is initialized by the COMEDI subsystem, but is reserved for use by a low-level driver attached to the COMEDI device (at least since commit 25436dc9d84f ("Staging: comedi: remove RT code")). Some COMEDI devices (those created on initialization of the COMEDI subsystem when the "comedi.comedi_num_legacy_minors" parameter is non-zero) can be attached to different low-level drivers over their lifetime using the `COMEDI_DEVCONFIG` ioctl command. This can result in inconsistent lock states being reported when there is a mismatch in the spin-lock locking levels used by each low-level driver to which the COMEDI device has been attached. Fix it by reinitializing `dev->spinlock` before calling the low-level driver's `attach` function pointer if `CONFIG_LOCKDEP` is enabled. Reported-by: syzbot+cc9f7f4a7df09f53c4a4@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=cc9f7f4a7df09f53c4a4 Fixes: ed9eccbe8970 ("Staging: add comedi core") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260225132427.86578-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c index db225a3bf012..5ab96b5eefd1 100644 --- a/drivers/comedi/drivers.c +++ b/drivers/comedi/drivers.c @@ -1063,6 +1063,14 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) ret = -EIO; goto out; } + if (IS_ENABLED(CONFIG_LOCKDEP)) { + /* + * dev->spinlock is for private use by the attached low-level + * driver. Reinitialize it to stop lock-dependency tracking + * between attachments to different low-level drivers. + */ + spin_lock_init(&dev->spinlock); + } dev->driver = driv; dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr : dev->driver->driver_name; From 29f644f14b89e6c4965e3c89251929e451190a66 Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 10 Mar 2026 11:11:04 +0000 Subject: [PATCH 670/712] comedi: runflags cannot determine whether to reclaim chanlist syzbot reported a memory leak [1], because commit 4e1da516debb ("comedi: Add reference counting for Comedi command handling") did not consider the exceptional exit case in do_cmd_ioctl() where runflags is not set. This caused chanlist not to be properly freed by do_become_nonbusy(), as it only frees chanlist when runflags is correctly set. Added a check in do_become_nonbusy() for the case where runflags is not set, to properly free the chanlist memory. [1] BUG: memory leak backtrace (crc 844a0efa): __comedi_get_user_chanlist drivers/comedi/comedi_fops.c:1815 [inline] do_cmd_ioctl.part.0+0x112/0x350 drivers/comedi/comedi_fops.c:1890 do_cmd_ioctl drivers/comedi/comedi_fops.c:1858 [inline] Fixes: 4e1da516debb ("comedi: Add reference counting for Comedi command handling") Reported-by: syzbot+f238baf6ded841b5a82e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f238baf6ded841b5a82e Signed-off-by: Edward Adam Davis Reviewed-by: Ian Abbott Cc: stable # 6.19 Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260310111104.70959-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/comedi_fops.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index 48a8a607a84c..0df9f4636fb6 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -793,13 +793,15 @@ static void do_become_nonbusy(struct comedi_device *dev, __comedi_clear_subdevice_runflags(s, COMEDI_SRF_RUNNING | COMEDI_SRF_BUSY); spin_unlock_irqrestore(&s->spin_lock, flags); - if (comedi_is_runflags_busy(runflags)) { + if (async) { /* * "Run active" counter was set to 1 when setting up the * command. Decrement it and wait for it to become 0. */ - comedi_put_is_subdevice_running(s); - wait_for_completion(&async->run_complete); + if (comedi_is_runflags_busy(runflags)) { + comedi_put_is_subdevice_running(s); + wait_for_completion(&async->run_complete); + } comedi_buf_reset(s); async->inttrig = NULL; kfree(async->cmd.chanlist); From 93853512f565e625df2397f0d8050d6aafd7c3ad Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Mon, 9 Mar 2026 16:18:59 +0530 Subject: [PATCH 671/712] comedi: dt2815: add hardware detection to prevent crash The dt2815 driver crashes when attached to I/O ports without actual hardware present. This occurs because syzkaller or users can attach the driver to arbitrary I/O addresses via COMEDI_DEVCONFIG ioctl. When no hardware exists at the specified port, inb() operations return 0xff (floating bus), but outb() operations can trigger page faults due to undefined behavior, especially under race conditions: BUG: unable to handle page fault for address: 000000007fffff90 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page RIP: 0010:dt2815_attach+0x6e0/0x1110 Add hardware detection by reading the status register before attempting any write operations. If the read returns 0xff, assume no hardware is present and fail the attach with -ENODEV. This prevents crashes from outb() operations on non-existent hardware. Reported-by: syzbot+72f94b474d6e50b71ffc@syzkaller.appspotmail.com Cc: stable Closes: https://syzkaller.appspot.com/bug?extid=72f94b474d6e50b71ffc Tested-by: syzbot+72f94b474d6e50b71ffc@syzkaller.appspotmail.com Reviewed-by: Ian Abbott Signed-off-by: Deepanshu Kartikey Link: [https://lore.kernel.org/all/20260126070458.10974-1-kartikey406@gmail.com/T/] Link: [https://lore.kernel.org/all/20260126070458.10974-1-kartikey406@gmail.com/T/ Link: https://patch.msgid.link/20260309104859.503529-1-kartikey406@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2815.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/comedi/drivers/dt2815.c b/drivers/comedi/drivers/dt2815.c index 03ba2fd18a21..d066dc303520 100644 --- a/drivers/comedi/drivers/dt2815.c +++ b/drivers/comedi/drivers/dt2815.c @@ -175,6 +175,18 @@ static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it) ? current_range_type : voltage_range_type; } + /* + * Check if hardware is present before attempting any I/O operations. + * Reading 0xff from status register typically indicates no hardware + * on the bus (floating bus reads as all 1s). + */ + if (inb(dev->iobase + DT2815_STATUS) == 0xff) { + dev_err(dev->class_dev, + "No hardware detected at I/O base 0x%lx\n", + dev->iobase); + return -ENODEV; + } + /* Init the 2815 */ outb(0x00, dev->iobase + DT2815_STATUS); for (i = 0; i < 100; i++) { From ba2c83167b215da30fa2aae56b140198cf8d8408 Mon Sep 17 00:00:00 2001 From: Xingjing Deng Date: Fri, 30 Jan 2026 07:41:40 +0800 Subject: [PATCH 672/712] misc: fastrpc: possible double-free of cctx->remote_heap fastrpc_init_create_static_process() may free cctx->remote_heap on the err_map path but does not clear the pointer. Later, fastrpc_rpmsg_remove() frees cctx->remote_heap again if it is non-NULL, which can lead to a double-free if the INIT_CREATE_STATIC ioctl hits the error path and the rpmsg device is subsequently removed/unbound. Clear cctx->remote_heap after freeing it in the error path to prevent the later cleanup from freeing it again. This issue was found by an in-house analysis workflow that extracts AST-based information and runs static checks, with LLM assistance for triage, and was confirmed by manual code review. No hardware testing was performed. Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd") Cc: stable@vger.kernel.org # 6.2+ Signed-off-by: Xingjing Deng Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260129234140.410983-1-xjdeng@buaa.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 47356a5d5804..19cfc487df7c 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1401,6 +1401,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, } err_map: fastrpc_buf_free(fl->cctx->remote_heap); + fl->cctx->remote_heap = NULL; err_name: kfree(name); err: From 6a502776f4a4f80fb839b22f12aeaf0267fca344 Mon Sep 17 00:00:00 2001 From: Xingjing Deng Date: Sat, 31 Jan 2026 14:55:39 +0800 Subject: [PATCH 673/712] misc: fastrpc: check qcom_scm_assign_mem() return in rpmsg_probe In the SDSP probe path, qcom_scm_assign_mem() is used to assign the reserved memory to the configured VMIDs, but its return value was not checked. Fail the probe if the SCM call fails to avoid continuing with an unexpected/incorrect memory permission configuration. This issue was found by an in-house analysis workflow that extracts AST-based information and runs static checks, with LLM assistance for triage, and was confirmed by manual code review. No hardware testing was performed. Fixes: c3c0363bc72d4 ("misc: fastrpc: support complete DMA pool access to the DSP") Cc: stable@vger.kernel.org # 6.11-rc1 Signed-off-by: Xingjing Deng Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260131065539.2124047-1-xjdeng@buaa.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 19cfc487df7c..1080f9acf70a 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2390,8 +2390,10 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) if (!err) { src_perms = BIT(QCOM_SCM_VMID_HLOS); - qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, + err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, data->vmperms, data->vmcount); + if (err) + goto err_free_data; } } From faeea8bbf6e958bf3c00cb08263109661975987c Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 30 Mar 2026 22:02:15 -0700 Subject: [PATCH 674/712] net/sched: cls_fw: fix NULL pointer dereference on shared blocks The old-method path in fw_classify() calls tcf_block_q() and dereferences q->handle. Shared blocks leave block->q NULL, causing a NULL deref when an empty cls_fw filter is attached to a shared block and a packet with a nonzero major skb mark is classified. Reject the configuration in fw_change() when the old method (no TCA_OPTIONS) is used on a shared block, since fw_classify()'s old-method path needs block->q which is NULL for shared blocks. The fixed null-ptr-deref calling stack: KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f] RIP: 0010:fw_classify (net/sched/cls_fw.c:81) Call Trace: tcf_classify (./include/net/tc_wrapper.h:197 net/sched/cls_api.c:1764 net/sched/cls_api.c:1860) tc_run (net/core/dev.c:4401) __dev_queue_xmit (net/core/dev.c:4535 net/core/dev.c:4790) Fixes: 1abf272022cf ("net: sched: tcindex, fw, flow: use tcf_block_q helper to get struct Qdisc") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260331050217.504278-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/sched/cls_fw.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index be81c108179d..23884ef8b80c 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -247,8 +247,18 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, struct nlattr *tb[TCA_FW_MAX + 1]; int err; - if (!opt) - return handle ? -EINVAL : 0; /* Succeed if it is old method. */ + if (!opt) { + if (handle) + return -EINVAL; + + if (tcf_block_shared(tp->chain->block)) { + NL_SET_ERR_MSG(extack, + "Must specify mark when attaching fw filter to block"); + return -EINVAL; + } + + return 0; /* Succeed if it is old method. */ + } err = nla_parse_nested_deprecated(tb, TCA_FW_MAX, opt, fw_policy, NULL); From 1a280dd4bd1d616a01d6ffe0de284c907b555504 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 30 Mar 2026 22:02:16 -0700 Subject: [PATCH 675/712] net/sched: cls_flow: fix NULL pointer dereference on shared blocks flow_change() calls tcf_block_q() and dereferences q->handle to derive a default baseclass. Shared blocks leave block->q NULL, causing a NULL deref when a flow filter without a fully qualified baseclass is created on a shared block. Check tcf_block_shared() before accessing block->q and return -EINVAL for shared blocks. This avoids the null-deref shown below: ======================================================================= KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f] RIP: 0010:flow_change (net/sched/cls_flow.c:508) Call Trace: tc_new_tfilter (net/sched/cls_api.c:2432) rtnetlink_rcv_msg (net/core/rtnetlink.c:6980) [...] ======================================================================= Fixes: 1abf272022cf ("net: sched: tcindex, fw, flow: use tcf_block_q helper to get struct Qdisc") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260331050217.504278-2-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/sched/cls_flow.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 339c664beff6..ab364e4e4686 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -503,8 +503,16 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, } if (TC_H_MAJ(baseclass) == 0) { - struct Qdisc *q = tcf_block_q(tp->chain->block); + struct tcf_block *block = tp->chain->block; + struct Qdisc *q; + if (tcf_block_shared(block)) { + NL_SET_ERR_MSG(extack, + "Must specify baseclass when attaching flow filter to block"); + goto err2; + } + + q = tcf_block_q(block); baseclass = TC_H_MAKE(q->handle, baseclass); } if (TC_H_MIN(baseclass) == 0) From 70f73562d278d9f88e7095e327f2a50082a82c65 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 30 Mar 2026 22:02:17 -0700 Subject: [PATCH 676/712] selftests/tc-testing: add tests for cls_fw and cls_flow on shared blocks Regression tests for the shared-block NULL derefs fixed in the previous two patches: - fw: attempt to attach an empty fw filter to a shared block and verify the configuration is rejected with EINVAL. - flow: create a flow filter on a shared block without a baseclass and verify the configuration is rejected with EINVAL. Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260331050217.504278-3-xmei5@asu.edu Signed-off-by: Paolo Abeni --- .../tc-testing/tc-tests/infra/filter.json | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json b/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json index 8d10042b489b..dbce6436ed26 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json @@ -22,5 +22,49 @@ "teardown": [ "$TC qdisc del dev $DUMMY root handle 1: htb default 1" ] + }, + { + "id": "b7e3", + "name": "Empty fw filter on shared block - rejected at config time", + "category": [ + "filter", + "fw" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 egress_block 1 clsact" + ], + "cmdUnderTest": "$TC filter add block 1 protocol ip prio 1 fw", + "expExitCode": "2", + "verifyCmd": "$TC filter show block 1", + "matchPattern": "fw", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] + }, + { + "id": "c8f4", + "name": "Flow filter on shared block without baseclass - rejected at config time", + "category": [ + "filter", + "flow" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress_block 1 clsact" + ], + "cmdUnderTest": "$TC filter add block 1 protocol ip prio 1 handle 1 flow map key dst", + "expExitCode": "2", + "verifyCmd": "$TC filter show block 1", + "matchPattern": "flow", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] } ] From 48b5163c957548f5854f14c90bfdedc33afbea3c Mon Sep 17 00:00:00 2001 From: Christian Eggers Date: Fri, 27 Mar 2026 13:16:44 +0000 Subject: [PATCH 677/712] nvmem: imx: assign nvmem_cell_info::raw_len Avoid getting error messages at startup like the following on i.MX6ULL: nvmem imx-ocotp0: cell mac-addr raw len 6 unaligned to nvmem word size 4 nvmem imx-ocotp0: cell mac-addr raw len 6 unaligned to nvmem word size 4 This shouldn't cause any functional change as this alignment would otherwise be done in nvmem_cell_info_to_nvmem_cell_entry_nodup(). Cc: stable@vger.kernel.org Fixes: 13bcd440f2ff ("nvmem: core: verify cell's raw_len") Signed-off-by: Christian Eggers Signed-off-by: Fabio Estevam Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131645.3025781-2-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp-ele.c | 1 + drivers/nvmem/imx-ocotp.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c index 7cf7e809a8f5..a0d2985c6d03 100644 --- a/drivers/nvmem/imx-ocotp-ele.c +++ b/drivers/nvmem/imx-ocotp-ele.c @@ -131,6 +131,7 @@ static int imx_ocotp_cell_pp(void *context, const char *id, int index, static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, struct nvmem_cell_info *cell) { + cell->raw_len = round_up(cell->bytes, 4); cell->read_post_process = imx_ocotp_cell_pp; } diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 7bf7656d4f96..108d78d7f6cb 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -589,6 +589,7 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, struct nvmem_cell_info *cell) { + cell->raw_len = round_up(cell->bytes, 4); cell->read_post_process = imx_ocotp_cell_pp; } From f9b88613ff402aa6fe8fd020573cb95867ae947e Mon Sep 17 00:00:00 2001 From: Ivan Vera Date: Fri, 27 Mar 2026 13:16:45 +0000 Subject: [PATCH 678/712] nvmem: zynqmp_nvmem: Fix buffer size in DMA and memcpy Buffer size used in dma allocation and memcpy is wrong. It can lead to undersized DMA buffer access and possible memory corruption. use correct buffer size in dma_alloc_coherent and memcpy. Fixes: 737c0c8d07b5 ("nvmem: zynqmp_nvmem: Add support to access efuse") Cc: stable@vger.kernel.org Signed-off-by: Ivan Vera Signed-off-by: Harish Ediga Signed-off-by: Harsh Jain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131645.3025781-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/zynqmp_nvmem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nvmem/zynqmp_nvmem.c b/drivers/nvmem/zynqmp_nvmem.c index 7da717d6c7fa..d297ff150dc0 100644 --- a/drivers/nvmem/zynqmp_nvmem.c +++ b/drivers/nvmem/zynqmp_nvmem.c @@ -66,7 +66,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, dma_addr_t dma_buf; size_t words = bytes / WORD_INBYTES; int ret; - int value; + unsigned int value; char *data; if (bytes % WORD_INBYTES != 0) { @@ -80,7 +80,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, } if (pufflag == 1 && flag == EFUSE_WRITE) { - memcpy(&value, val, bytes); + memcpy(&value, val, sizeof(value)); if ((offset == EFUSE_PUF_START_OFFSET || offset == EFUSE_PUF_MID_OFFSET) && value & P_USER_0_64_UPPER_MASK) { @@ -100,7 +100,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, if (!efuse) return -ENOMEM; - data = dma_alloc_coherent(dev, sizeof(bytes), + data = dma_alloc_coherent(dev, bytes, &dma_buf, GFP_KERNEL); if (!data) { ret = -ENOMEM; @@ -134,7 +134,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, if (flag == EFUSE_READ) memcpy(val, data, bytes); efuse_access_err: - dma_free_coherent(dev, sizeof(bytes), + dma_free_coherent(dev, bytes, data, dma_buf); efuse_data_fail: dma_free_coherent(dev, sizeof(struct xilinx_efuse), From d78ceee1e6205ffcd84ff581ccb40a008d39136f Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Tue, 24 Mar 2026 08:29:28 +0000 Subject: [PATCH 679/712] .get_maintainer.ignore: add myself I don't want get_maintainer.pl to automatically print my email. Signed-off-by: Askar Safin Link: https://patch.msgid.link/20260324082928.3473789-1-safinaskar@gmail.com Signed-off-by: Greg Kroah-Hartman --- .get_maintainer.ignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.get_maintainer.ignore b/.get_maintainer.ignore index e8d2269bad9d..60b6b2a374cd 100644 --- a/.get_maintainer.ignore +++ b/.get_maintainer.ignore @@ -1,6 +1,7 @@ Alan Cox Alan Cox Alyssa Rosenzweig +Askar Safin Christoph Hellwig Jeff Kirsher Marc Gonzalez From b18c833888742ca9de80c250f9d40d0e97caa9f6 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 1 Apr 2026 11:21:53 +0200 Subject: [PATCH 680/712] vsock: initialize child_ns_mode_locked in vsock_net_init() The `child_ns_mode_locked` field lives in `struct net`, which persists across vsock module reloads. When the module is unloaded and reloaded, `vsock_net_init()` resets `mode` and `child_ns_mode` back to their default values, but does not reset `child_ns_mode_locked`. The stale lock from the previous module load causes subsequent writes to `child_ns_mode` to silently fail: `vsock_net_set_child_mode()` sees the old lock, skips updating the actual value, and returns success when the requested mode matches the stale lock. The sysctl handler reports no error, but `child_ns_mode` remains unchanged. Steps to reproduce: $ modprobe vsock $ echo local > /proc/sys/net/vsock/child_ns_mode $ cat /proc/sys/net/vsock/child_ns_mode local $ modprobe -r vsock $ modprobe vsock $ echo local > /proc/sys/net/vsock/child_ns_mode $ cat /proc/sys/net/vsock/child_ns_mode global <--- expected "local" Fix this by initializing `child_ns_mode_locked` to 0 (unlocked) in `vsock_net_init()`, so the write-once mechanism works correctly after module reload. Fixes: 102eab95f025 ("vsock: lock down child_ns_mode as write-once") Reported-by: Jin Liu Signed-off-by: Stefano Garzarella Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/20260401092153.28462-1-sgarzare@redhat.com Signed-off-by: Jakub Kicinski --- net/vmw_vsock/af_vsock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 2f7d94d682cb..d912ed2f012a 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -2928,6 +2928,7 @@ static void vsock_net_init(struct net *net) net->vsock.mode = vsock_net_child_mode(current->nsproxy->net_ns); net->vsock.child_ns_mode = net->vsock.mode; + net->vsock.child_ns_mode_locked = 0; } static __net_init int vsock_sysctl_init_net(struct net *net) From f5df2990c364d1ac596d24b3118dbc56503f7cd4 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Wed, 1 Apr 2026 11:22:42 +0200 Subject: [PATCH 681/712] net: hsr: serialize seq_blocks merge across nodes During node merging, hsr_handle_sup_frame() walks node_curr->seq_blocks to update node_real without holding node_curr->seq_out_lock. This allows concurrent mutations from duplicate registration paths, risking inconsistent state or XArray/bitmap corruption. Fix this by locking both nodes' seq_out_lock during the merge. To prevent ABBA deadlocks, locks are acquired in order of memory address. Reviewed-by: Felix Maurer Fixes: 415e6367512b ("hsr: Implement more robust duplicate discard for PRP") Signed-off-by: Luka Gejak Link: https://patch.msgid.link/20260401092243.52121-2-luka.gejak@linux.dev Signed-off-by: Jakub Kicinski --- net/hsr/hsr_framereg.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 50996f4de7f9..d41863593674 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -123,6 +123,40 @@ static void hsr_free_node_rcu(struct rcu_head *rn) hsr_free_node(node); } +static void hsr_lock_seq_out_pair(struct hsr_node *node_a, + struct hsr_node *node_b) +{ + if (node_a == node_b) { + spin_lock_bh(&node_a->seq_out_lock); + return; + } + + if (node_a < node_b) { + spin_lock_bh(&node_a->seq_out_lock); + spin_lock_nested(&node_b->seq_out_lock, SINGLE_DEPTH_NESTING); + } else { + spin_lock_bh(&node_b->seq_out_lock); + spin_lock_nested(&node_a->seq_out_lock, SINGLE_DEPTH_NESTING); + } +} + +static void hsr_unlock_seq_out_pair(struct hsr_node *node_a, + struct hsr_node *node_b) +{ + if (node_a == node_b) { + spin_unlock_bh(&node_a->seq_out_lock); + return; + } + + if (node_a < node_b) { + spin_unlock(&node_b->seq_out_lock); + spin_unlock_bh(&node_a->seq_out_lock); + } else { + spin_unlock(&node_a->seq_out_lock); + spin_unlock_bh(&node_b->seq_out_lock); + } +} + void hsr_del_nodes(struct list_head *node_db) { struct hsr_node *node; @@ -432,7 +466,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) } ether_addr_copy(node_real->macaddress_B, ethhdr->h_source); - spin_lock_bh(&node_real->seq_out_lock); + hsr_lock_seq_out_pair(node_real, node_curr); for (i = 0; i < HSR_PT_PORTS; i++) { if (!node_curr->time_in_stale[i] && time_after(node_curr->time_in[i], node_real->time_in[i])) { @@ -455,7 +489,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE); } } - spin_unlock_bh(&node_real->seq_out_lock); + hsr_unlock_seq_out_pair(node_real, node_curr); node_real->addr_B_port = port_rcv->type; spin_lock_bh(&hsr->list_lock); From 2e3514e63bfb0e972b1f19668547a455d0129e88 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Wed, 1 Apr 2026 11:22:43 +0200 Subject: [PATCH 682/712] net: hsr: fix VLAN add unwind on slave errors When vlan_vid_add() fails for a secondary slave, the error path calls vlan_vid_del() on the failing port instead of the peer slave that had already succeeded. This results in asymmetric VLAN state across the HSR pair. Fix this by switching to a centralized unwind path that removes the VID from any slave device that was already programmed. Fixes: 1a8a63a5305e ("net: hsr: Add VLAN CTAG filter support") Signed-off-by: Luka Gejak Link: https://patch.msgid.link/20260401092243.52121-3-luka.gejak@linux.dev Signed-off-by: Jakub Kicinski --- net/hsr/hsr_device.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index d1bfc49b5f01..fd2fea25eff0 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -532,8 +532,8 @@ static void hsr_change_rx_flags(struct net_device *dev, int change) static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { - bool is_slave_a_added = false; - bool is_slave_b_added = false; + struct net_device *slave_a_dev = NULL; + struct net_device *slave_b_dev = NULL; struct hsr_port *port; struct hsr_priv *hsr; int ret = 0; @@ -549,33 +549,35 @@ static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev, switch (port->type) { case HSR_PT_SLAVE_A: if (ret) { - /* clean up Slave-B */ netdev_err(dev, "add vid failed for Slave-A\n"); - if (is_slave_b_added) - vlan_vid_del(port->dev, proto, vid); - return ret; + goto unwind; } - - is_slave_a_added = true; + slave_a_dev = port->dev; break; - case HSR_PT_SLAVE_B: if (ret) { - /* clean up Slave-A */ netdev_err(dev, "add vid failed for Slave-B\n"); - if (is_slave_a_added) - vlan_vid_del(port->dev, proto, vid); - return ret; + goto unwind; } - - is_slave_b_added = true; + slave_b_dev = port->dev; break; default: + if (ret) + goto unwind; break; } } return 0; + +unwind: + if (slave_a_dev) + vlan_vid_del(slave_a_dev, proto, vid); + + if (slave_b_dev) + vlan_vid_del(slave_b_dev, proto, vid); + + return ret; } static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev, From 4e453375561fc60820e6b9d8ebeb6b3ee177d42e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 1 Apr 2026 15:47:21 +0000 Subject: [PATCH 683/712] ipv6: avoid overflows in ip6_datagram_send_ctl() Yiming Qian reported : I believe I found a locally triggerable kernel bug in the IPv6 sendmsg ancillary-data path that can panic the kernel via `skb_under_panic()` (local DoS). The core issue is a mismatch between: - a 16-bit length accumulator (`struct ipv6_txoptions::opt_flen`, type `__u16`) and - a pointer to the *last* provided destination-options header (`opt->dst1opt`) when multiple `IPV6_DSTOPTS` control messages (cmsgs) are provided. - `include/net/ipv6.h`: - `struct ipv6_txoptions::opt_flen` is `__u16` (wrap possible). (lines 291-307, especially 298) - `net/ipv6/datagram.c:ip6_datagram_send_ctl()`: - Accepts repeated `IPV6_DSTOPTS` and accumulates into `opt_flen` without rejecting duplicates. (lines 909-933) - `net/ipv6/ip6_output.c:__ip6_append_data()`: - Uses `opt->opt_flen + opt->opt_nflen` to compute header sizes/headroom decisions. (lines 1448-1466, especially 1463-1465) - `net/ipv6/ip6_output.c:__ip6_make_skb()`: - Calls `ipv6_push_frag_opts()` if `opt->opt_flen` is non-zero. (lines 1930-1934) - `net/ipv6/exthdrs.c:ipv6_push_frag_opts()` / `ipv6_push_exthdr()`: - Push size comes from `ipv6_optlen(opt->dst1opt)` (based on the pointed-to header). (lines 1179-1185 and 1206-1211) 1. `opt_flen` is a 16-bit accumulator: - `include/net/ipv6.h:298` defines `__u16 opt_flen; /* after fragment hdr */`. 2. `ip6_datagram_send_ctl()` accepts *repeated* `IPV6_DSTOPTS` cmsgs and increments `opt_flen` each time: - In `net/ipv6/datagram.c:909-933`, for `IPV6_DSTOPTS`: - It computes `len = ((hdr->hdrlen + 1) << 3);` - It checks `CAP_NET_RAW` using `ns_capable(net->user_ns, CAP_NET_RAW)`. (line 922) - Then it does: - `opt->opt_flen += len;` (line 927) - `opt->dst1opt = hdr;` (line 928) There is no duplicate rejection here (unlike the legacy `IPV6_2292DSTOPTS` path which rejects duplicates at `net/ipv6/datagram.c:901-904`). If enough large `IPV6_DSTOPTS` cmsgs are provided, `opt_flen` wraps while `dst1opt` still points to a large (2048-byte) destination-options header. In the attached PoC (`poc.c`): - 32 cmsgs with `hdrlen=255` => `len = (255+1)*8 = 2048` - 1 cmsg with `hdrlen=0` => `len = 8` - Total increment: `32*2048 + 8 = 65544`, so `(__u16)opt_flen == 8` - The last cmsg is 2048 bytes, so `dst1opt` points to a 2048-byte header. 3. The transmit path sizes headers using the wrapped `opt_flen`: - In `net/ipv6/ip6_output.c:1463-1465`: - `headersize = sizeof(struct ipv6hdr) + (opt ? opt->opt_flen + opt->opt_nflen : 0) + ...;` With wrapped `opt_flen`, `headersize`/headroom decisions underestimate what will be pushed later. 4. When building the final skb, the actual push length comes from `dst1opt` and is not limited by wrapped `opt_flen`: - In `net/ipv6/ip6_output.c:1930-1934`: - `if (opt->opt_flen) proto = ipv6_push_frag_opts(skb, opt, proto);` - In `net/ipv6/exthdrs.c:1206-1211`, `ipv6_push_frag_opts()` pushes `dst1opt` via `ipv6_push_exthdr()`. - In `net/ipv6/exthdrs.c:1179-1184`, `ipv6_push_exthdr()` does: - `skb_push(skb, ipv6_optlen(opt));` - `memcpy(h, opt, ipv6_optlen(opt));` With insufficient headroom, `skb_push()` underflows and triggers `skb_under_panic()` -> `BUG()`: - `net/core/skbuff.c:2669-2675` (`skb_push()` calls `skb_under_panic()`) - `net/core/skbuff.c:207-214` (`skb_panic()` ends in `BUG()`) - The `IPV6_DSTOPTS` cmsg path requires `CAP_NET_RAW` in the target netns user namespace (`ns_capable(net->user_ns, CAP_NET_RAW)`). - Root (or any task with `CAP_NET_RAW`) can trigger this without user namespaces. - An unprivileged `uid=1000` user can trigger this if unprivileged user namespaces are enabled and it can create a userns+netns to obtain namespaced `CAP_NET_RAW` (the attached PoC does this). - Local denial of service: kernel BUG/panic (system crash). - Reproducible with a small userspace PoC. This patch does not reject duplicated options, as this might break some user applications. Instead, it makes sure to adjust opt_flen and opt_nflen to correctly reflect the size of the current option headers, preventing the overflows and the potential for panics. This applies to IPV6_DSTOPTS, IPV6_HOPOPTS, and IPV6_RTHDR. Specifically: When a new IPV6_DSTOPTS is processed, the length of the old opt->dst1opt is subtracted from opt->opt_flen before adding the new length. When a new IPV6_HOPOPTS is processed, the length of the old opt->dst0opt is subtracted from opt->opt_nflen. When a new Routing Header (IPV6_RTHDR or IPV6_2292RTHDR) is processed, the length of the old opt->srcrt is subtracted from opt->opt_nflen. In the special case within IPV6_2292RTHDR handling where dst1opt is moved to dst0opt, the length of the old opt->dst0opt is subtracted from opt->opt_nflen before the new one is added. Fixes: 333fad5364d6 ("[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).") Reported-by: Yiming Qian Closes: https://lore.kernel.org/netdev/CAL_bE8JNzawgr5OX5m+3jnQDHry2XxhQT5=jThW1zDPtUikRYA@mail.gmail.com/ Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260401154721.3740056-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/datagram.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index c564b68a0562..993e2d76fc1f 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -763,6 +763,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, { struct in6_pktinfo *src_info; struct cmsghdr *cmsg; + struct ipv6_rt_hdr *orthdr; struct ipv6_rt_hdr *rthdr; struct ipv6_opt_hdr *hdr; struct ipv6_txoptions *opt = ipc6->opt; @@ -924,9 +925,13 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, goto exit_f; } if (cmsg->cmsg_type == IPV6_DSTOPTS) { + if (opt->dst1opt) + opt->opt_flen -= ipv6_optlen(opt->dst1opt); opt->opt_flen += len; opt->dst1opt = hdr; } else { + if (opt->dst0opt) + opt->opt_nflen -= ipv6_optlen(opt->dst0opt); opt->opt_nflen += len; opt->dst0opt = hdr; } @@ -969,12 +974,17 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, goto exit_f; } + orthdr = opt->srcrt; + if (orthdr) + opt->opt_nflen -= ((orthdr->hdrlen + 1) << 3); opt->opt_nflen += len; opt->srcrt = rthdr; if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) { int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3); + if (opt->dst0opt) + opt->opt_nflen -= ipv6_optlen(opt->dst0opt); opt->opt_nflen += dsthdrlen; opt->dst0opt = opt->dst1opt; opt->dst1opt = NULL; From ec7067e661193403a7a00980bda8612db5954142 Mon Sep 17 00:00:00 2001 From: Dimitri Daskalakis Date: Wed, 1 Apr 2026 09:28:48 -0700 Subject: [PATCH 684/712] eth: fbnic: Increase FBNIC_QUEUE_SIZE_MIN to 64 On systems with 64K pages, RX queues will be wedged if users set the descriptor count to the current minimum (16). Fbnic fragments large pages into 4K chunks, and scales down the ring size accordingly. With 64K pages and 16 descriptors, the ring size mask is 0 and will never be filled. 32 descriptors is another special case that wedges the RX rings. Internally, the rings track pages for the head/tail pointers, not page fragments. So with 32 descriptors, there's only 1 usable page as one ring slot is kept empty to disambiguate between an empty/full ring. As a result, the head pointer never advances and the HW stalls after consuming 16 page fragments. Fixes: 0cb4c0a13723 ("eth: fbnic: Implement Rx queue alloc/start/stop/free") Signed-off-by: Dimitri Daskalakis Link: https://patch.msgid.link/20260401162848.2335350-1-dimitri.daskalakis1@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/meta/fbnic/fbnic_txrx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h index 980965274079..e03c9d2c38dc 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h @@ -38,7 +38,7 @@ struct fbnic_net; #define FBNIC_MAX_XDPQS 128u /* These apply to TWQs, TCQ, RCQ */ -#define FBNIC_QUEUE_SIZE_MIN 16u +#define FBNIC_QUEUE_SIZE_MIN 64u #define FBNIC_QUEUE_SIZE_MAX SZ_64K #define FBNIC_TXQ_SIZE_DEFAULT 1024 From b0db1accbc7395657c2b79db59fa9fae0d6656f3 Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Thu, 2 Apr 2026 17:29:22 +0800 Subject: [PATCH 685/712] bpf: reject direct access to nullable PTR_TO_BUF pointers check_mem_access() matches PTR_TO_BUF via base_type() which strips PTR_MAYBE_NULL, allowing direct dereference without a null check. Map iterator ctx->key and ctx->value are PTR_TO_BUF | PTR_MAYBE_NULL. On stop callbacks these are NULL, causing a kernel NULL dereference. Add a type_may_be_null() guard to the PTR_TO_BUF branch, matching the existing PTR_TO_BTF_ID pattern. Fixes: 20b2aff4bc15 ("bpf: Introduce MEM_RDONLY flag") Signed-off-by: Qi Tang Acked-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260402092923.38357-2-tpluszz77@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a3388cb8fcbd..df04dccfc540 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7905,7 +7905,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn } else if (reg->type == CONST_PTR_TO_MAP) { err = check_ptr_to_map_access(env, regs, regno, off, size, t, value_regno); - } else if (base_type(reg->type) == PTR_TO_BUF) { + } else if (base_type(reg->type) == PTR_TO_BUF && + !type_may_be_null(reg->type)) { bool rdonly_mem = type_is_rdonly_mem(reg->type); u32 *max_access; From eb7024bfcc5f68ed11ed9dd4891a3073c15f04a8 Mon Sep 17 00:00:00 2001 From: Varun R Mallya Date: Thu, 2 Apr 2026 00:41:25 +0530 Subject: [PATCH 686/712] bpf: Reject sleepable kprobe_multi programs at attach time kprobe.multi programs run in atomic/RCU context and cannot sleep. However, bpf_kprobe_multi_link_attach() did not validate whether the program being attached had the sleepable flag set, allowing sleepable helpers such as bpf_copy_from_user() to be invoked from a non-sleepable context. This causes a "sleeping function called from invalid context" splat: BUG: sleeping function called from invalid context at ./include/linux/uaccess.h:169 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1787, name: sudo preempt_count: 1, expected: 0 RCU nest depth: 2, expected: 0 Fix this by rejecting sleepable programs early in bpf_kprobe_multi_link_attach(), before any further processing. Fixes: 0dcac2725406 ("bpf: Add multi kprobe link") Signed-off-by: Varun R Mallya Acked-by: Kumar Kartikeya Dwivedi Acked-by: Leon Hwang Acked-by: Jiri Olsa Link: https://lore.kernel.org/r/20260401191126.440683-1-varunrmallya@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/trace/bpf_trace.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 0b040a417442..af7079aa0f36 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2752,6 +2752,10 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr if (!is_kprobe_multi(prog)) return -EINVAL; + /* kprobe_multi is not allowed to be sleepable. */ + if (prog->sleepable) + return -EINVAL; + /* Writing to context is not allowed for kprobes. */ if (prog->aux->kprobe_write_ctx) return -EINVAL; From 179ee84a89114b854ac2dd1d293633a7f6c8dac1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 1 Apr 2026 00:20:19 +0200 Subject: [PATCH 687/712] bpf: Fix incorrect pruning due to atomic fetch precision tracking When backtrack_insn encounters a BPF_STX instruction with BPF_ATOMIC and BPF_FETCH, the src register (or r0 for BPF_CMPXCHG) also acts as a destination, thus receiving the old value from the memory location. The current backtracking logic does not account for this. It treats atomic fetch operations the same as regular stores where the src register is only an input. This leads the backtrack_insn to fail to propagate precision to the stack location, which is then not marked as precise! Later, the verifier's path pruning can incorrectly consider two states equivalent when they differ in terms of stack state. Meaning, two branches can be treated as equivalent and thus get pruned when they should not be seen as such. Fix it as follows: Extend the BPF_LDX handling in backtrack_insn to also cover atomic fetch operations via is_atomic_fetch_insn() helper. When the fetch dst register is being tracked for precision, clear it, and propagate precision over to the stack slot. For non-stack memory, the precision walk stops at the atomic instruction, same as regular BPF_LDX. This covers all fetch variants. Before: 0: (b7) r1 = 8 ; R1=8 1: (7b) *(u64 *)(r10 -8) = r1 ; R1=8 R10=fp0 fp-8=8 2: (b7) r2 = 0 ; R2=0 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) ; R2=8 R10=fp0 fp-8=mmmmmmmm 4: (bf) r3 = r10 ; R3=fp0 R10=fp0 5: (0f) r3 += r2 mark_precise: frame0: last_idx 5 first_idx 0 subseq_idx -1 mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10 mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) mark_precise: frame0: regs=r2 stack= before 2: (b7) r2 = 0 6: R2=8 R3=fp8 6: (b7) r0 = 0 ; R0=0 7: (95) exit After: 0: (b7) r1 = 8 ; R1=8 1: (7b) *(u64 *)(r10 -8) = r1 ; R1=8 R10=fp0 fp-8=8 2: (b7) r2 = 0 ; R2=0 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) ; R2=8 R10=fp0 fp-8=mmmmmmmm 4: (bf) r3 = r10 ; R3=fp0 R10=fp0 5: (0f) r3 += r2 mark_precise: frame0: last_idx 5 first_idx 0 subseq_idx -1 mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10 mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0 mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1 mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8 6: R2=8 R3=fp8 6: (b7) r0 = 0 ; R0=0 7: (95) exit Fixes: 5ffa25502b5a ("bpf: Add instructions for atomic_[cmp]xchg") Fixes: 5ca419f2864a ("bpf: Add BPF_FETCH field / create atomic_fetch_add instruction") Reported-by: STAR Labs SG Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260331222020.401848-1-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index df04dccfc540..e3814152b52f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -617,6 +617,13 @@ static bool is_atomic_load_insn(const struct bpf_insn *insn) insn->imm == BPF_LOAD_ACQ; } +static bool is_atomic_fetch_insn(const struct bpf_insn *insn) +{ + return BPF_CLASS(insn->code) == BPF_STX && + BPF_MODE(insn->code) == BPF_ATOMIC && + (insn->imm & BPF_FETCH); +} + static int __get_spi(s32 off) { return (-off - 1) / BPF_REG_SIZE; @@ -4447,10 +4454,24 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, * dreg still needs precision before this insn */ } - } else if (class == BPF_LDX || is_atomic_load_insn(insn)) { - if (!bt_is_reg_set(bt, dreg)) + } else if (class == BPF_LDX || + is_atomic_load_insn(insn) || + is_atomic_fetch_insn(insn)) { + u32 load_reg = dreg; + + /* + * Atomic fetch operation writes the old value into + * a register (sreg or r0) and if it was tracked for + * precision, propagate to the stack slot like we do + * in regular ldx. + */ + if (is_atomic_fetch_insn(insn)) + load_reg = insn->imm == BPF_CMPXCHG ? + BPF_REG_0 : sreg; + + if (!bt_is_reg_set(bt, load_reg)) return 0; - bt_clear_reg(bt, dreg); + bt_clear_reg(bt, load_reg); /* scalars can only be spilled into stack w/o losing precision. * Load from any other memory can be zero extended. From e1b5687a862a43429f1d9f69065b3bbc7780a97a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 1 Apr 2026 00:20:20 +0200 Subject: [PATCH 688/712] selftests/bpf: Add more precision tracking tests for atomics Add verifier precision tracking tests for BPF atomic fetch operations. Validate that backtrack_insn correctly propagates precision from the fetch dst_reg to the stack slot for {fetch_add,xchg,cmpxchg} atomics. For the first two src_reg gets the old memory value, and for the last one r0. The fetched register is used for pointer arithmetic to trigger backtracking. Also add coverage for fetch_{or,and,xor} flavors which exercises the bitwise atomic fetch variants going through the same insn->imm & BPF_FETCH check but with different imm values. Add dual-precision regression tests for fetch_add and cmpxchg where both the fetched value and a reread of the same stack slot are tracked for precision. After the atomic operation, the stack slot is STACK_MISC, so the ldx does not set INSN_F_STACK_ACCESS. These tests verify that stack precision propagates solely through the atomic fetch's load side. Add map-based tests for fetch_add and cmpxchg which validate that non- stack atomic fetch completes precision tracking without falling back to mark_all_scalars_precise. Lastly, add 32-bit variants for {fetch_add, cmpxchg} on map values to cover the second valid atomic operand size. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_precision [...] + /etc/rcS.d/S50-startup ./test_progs -t verifier_precision [ 1.697105] bpf_testmod: loading out-of-tree module taints kernel. [ 1.700220] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel [ 1.777043] tsc: Refined TSC clocksource calibration: 3407.986 MHz [ 1.777619] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x311fc6d7268, max_idle_ns: 440795260133 ns [ 1.778658] clocksource: Switched to clocksource tsc #633/1 verifier_precision/bpf_neg:OK #633/2 verifier_precision/bpf_end_to_le:OK #633/3 verifier_precision/bpf_end_to_be:OK #633/4 verifier_precision/bpf_end_bswap:OK #633/5 verifier_precision/bpf_load_acquire:OK #633/6 verifier_precision/bpf_store_release:OK #633/7 verifier_precision/state_loop_first_last_equal:OK #633/8 verifier_precision/bpf_cond_op_r10:OK #633/9 verifier_precision/bpf_cond_op_not_r10:OK #633/10 verifier_precision/bpf_atomic_fetch_add_precision:OK #633/11 verifier_precision/bpf_atomic_xchg_precision:OK #633/12 verifier_precision/bpf_atomic_fetch_or_precision:OK #633/13 verifier_precision/bpf_atomic_fetch_and_precision:OK #633/14 verifier_precision/bpf_atomic_fetch_xor_precision:OK #633/15 verifier_precision/bpf_atomic_cmpxchg_precision:OK #633/16 verifier_precision/bpf_atomic_fetch_add_dual_precision:OK #633/17 verifier_precision/bpf_atomic_cmpxchg_dual_precision:OK #633/18 verifier_precision/bpf_atomic_fetch_add_map_precision:OK #633/19 verifier_precision/bpf_atomic_cmpxchg_map_precision:OK #633/20 verifier_precision/bpf_atomic_fetch_add_32bit_precision:OK #633/21 verifier_precision/bpf_atomic_cmpxchg_32bit_precision:OK #633/22 verifier_precision/bpf_neg_2:OK #633/23 verifier_precision/bpf_neg_3:OK #633/24 verifier_precision/bpf_neg_4:OK #633/25 verifier_precision/bpf_neg_5:OK #633 verifier_precision:OK Summary: 1/25 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260331222020.401848-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/verifier_precision.c | 341 ++++++++++++++++++ 1 file changed, 341 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_precision.c b/tools/testing/selftests/bpf/progs/verifier_precision.c index 1fe090cd6744..4794903aec8e 100644 --- a/tools/testing/selftests/bpf/progs/verifier_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_precision.c @@ -5,6 +5,13 @@ #include "../../../include/linux/filter.h" #include "bpf_misc.h" +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} precision_map SEC(".maps"); + SEC("?raw_tp") __success __log_level(2) __msg("mark_precise: frame0: regs=r2 stack= before 3: (bf) r1 = r10") @@ -301,4 +308,338 @@ __naked int bpf_neg_5(void) ::: __clobber_all); } +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_add_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_add_insn];" /* r2 = atomic_fetch_add(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_xchg((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_xchg_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[xchg_insn];" /* r2 = atomic_xchg(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(xchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_or((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_or_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_or_insn];" /* r2 = atomic_fetch_or(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_or_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_OR | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_and((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_and_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_and_insn];" /* r2 = atomic_fetch_and(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_and_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_AND | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_xor((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_xor_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_xor_insn];" /* r2 = atomic_fetch_xor(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_xor_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_XOR | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0 stack= before 5: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r0 stack= before 4: (db) r0 = atomic64_cmpxchg((u64 *)(r10 -8), r0, r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 3: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r0 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_cmpxchg_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r0 = 0;" + "r2 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u64 *)(r10 - 8), r0, r2) */ + "r3 = r10;" + "r3 += r0;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +/* Regression test for dual precision: Both the fetched value (r2) and + * a reread of the same stack slot (r3) are tracked for precision. After + * the atomic operation, the stack slot is STACK_MISC. Thus, the ldx at + * insn 4 does NOT set INSN_F_STACK_ACCESS. Precision for the stack slot + * propagates solely through the atomic fetch's load side (insn 3). + */ +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2,r3 stack= before 4: (79) r3 = *(u64 *)(r10 -8)") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_add_dual_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_add_insn];" /* r2 = atomic_fetch_add(*(u64 *)(r10 - 8), r2) */ + "r3 = *(u64 *)(r10 - 8);" + "r4 = r2;" + "r4 += r3;" + "r4 &= 7;" + "r5 = r10;" + "r5 += r4;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0,r3 stack= before 5: (79) r3 = *(u64 *)(r10 -8)") +__msg("mark_precise: frame0: regs=r0 stack= before 4: (db) r0 = atomic64_cmpxchg((u64 *)(r10 -8), r0, r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 3: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r0 = 8") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_cmpxchg_dual_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r0 = 8;" + "r2 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u64 *)(r10 - 8), r0, r2) */ + "r3 = *(u64 *)(r10 - 8);" + "r4 = r0;" + "r4 += r3;" + "r4 &= 7;" + "r5 = r10;" + "r5 += r4;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r1 stack= before 10: (57) r1 &= 7") +__msg("mark_precise: frame0: regs=r1 stack= before 9: (db) r1 = atomic64_fetch_add((u64 *)(r0 +0), r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_fetch_add_map_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r1 = 0;" + ".8byte %[fetch_add_insn];" /* r1 = atomic_fetch_add(*(u64 *)(r0 + 0), r1) */ + "r1 &= 7;" + "r2 = r10;" + "r2 += r1;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_0, BPF_REG_1, 0)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0 stack= before 12: (57) r0 &= 7") +__msg("mark_precise: frame0: regs=r0 stack= before 11: (db) r0 = atomic64_cmpxchg((u64 *)(r6 +0), r0, r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_cmpxchg_map_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r6 = r0;" + "r0 = 0;" + "r1 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u64 *)(r6 + 0), r0, r1) */ + "r0 &= 7;" + "r2 = r10;" + "r2 += r0;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_6, BPF_REG_1, 0)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r1 stack= before 10: (57) r1 &= 7") +__msg("mark_precise: frame0: regs=r1 stack= before 9: (c3) r1 = atomic_fetch_add((u32 *)(r0 +0), r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_fetch_add_32bit_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r1 = 0;" + ".8byte %[fetch_add_insn];" /* r1 = atomic_fetch_add(*(u32 *)(r0 + 0), r1) */ + "r1 &= 7;" + "r2 = r10;" + "r2 += r1;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_W, BPF_ADD | BPF_FETCH, BPF_REG_0, BPF_REG_1, 0)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0 stack= before 12: (57) r0 &= 7") +__msg("mark_precise: frame0: regs=r0 stack= before 11: (c3) r0 = atomic_cmpxchg((u32 *)(r6 +0), r0, r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_cmpxchg_32bit_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r6 = r0;" + "r0 = 0;" + "r1 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u32 *)(r6 + 0), r0, r1) */ + "r0 &= 7;" + "r2 = r10;" + "r2 += r0;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_6, BPF_REG_1, 0)) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; From a4983968fa5b3179ab090407d325a71cdc96874e Mon Sep 17 00:00:00 2001 From: Ionut Nechita Date: Mon, 23 Mar 2026 23:13:43 +0200 Subject: [PATCH 689/712] drm/amd/display: Wire up dcn10_dio_construct() for all pre-DCN401 generations Description: - Commit b82f0759346617b2 ("drm/amd/display: Migrate DIO registers access from hwseq to dio component") moved DIO_MEM_PWR_CTRL register access behind the new dio abstraction layer but only created the dio object for DCN 4.01. On all other generations (DCN 10/20/21/201/30/301/302/303/ 31/314/315/316/32/321/35/351/36), the dio pointer is NULL, causing the register write to be silently skipped. This results in AFMT HDMI memory not being powered on during init_hw, which can cause HDMI audio failures and display issues on affected hardware including Renoir/Cezanne (DCN 2.1) APUs that use dcn10_init_hw. Call dcn10_dio_construct() in each older DCN generation's resource.c to create the dio object, following the same pattern as DCN 4.01. This ensures the dio pointer is non-NULL and the mem_pwr_ctrl callback works through the dio abstraction for all DCN generations. Fixes: b82f07593466 ("drm/amd/display: Migrate DIO registers access from hwseq to dio component.") Reviewed-by: Ivan Lipski Signed-off-by: Ionut Nechita Signed-off-by: Alex Deucher --- .../dc/resource/dcn10/dcn10_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn20/dcn20_resource.c | 42 ++++++++++++++++++ .../dc/resource/dcn201/dcn201_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn21/dcn21_resource.c | 34 +++++++++++++++ .../dc/resource/dcn30/dcn30_resource.c | 42 ++++++++++++++++++ .../dc/resource/dcn301/dcn301_resource.c | 42 ++++++++++++++++++ .../dc/resource/dcn302/dcn302_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn303/dcn303_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn31/dcn31_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn314/dcn314_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn315/dcn315_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn316/dcn316_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn32/dcn32_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn321/dcn321_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn35/dcn35_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn351/dcn351_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn36/dcn36_resource.c | 43 +++++++++++++++++++ 17 files changed, 699 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c index bbe185e15eb6..4663456a736a 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c @@ -71,6 +71,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_i2c.h" +#include "dio/dcn10/dcn10_dio.h" #ifndef mmDP0_DP_DPHY_INTERNAL_CTRL #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x210f @@ -444,6 +445,33 @@ static const struct dcn_hubbub_mask hubbub_mask = { HUBBUB_MASK_SH_LIST_DCN10(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn10_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static int map_transmitter_id_to_phy_instance( enum transmitter transmitter) { @@ -917,6 +945,11 @@ static void dcn10_resource_destruct(struct dcn10_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.opps[i] != NULL) pool->base.opps[i]->funcs->opp_destroy(&pool->base.opps[i]); @@ -1653,6 +1686,14 @@ static bool dcn10_resource_construct( goto fail; } + /* DIO */ + pool->base.dio = dcn10_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto fail; + } + if (!resource_construct(num_virtual_links, dc, &pool->base, &res_create_funcs)) goto fail; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c index 8b555187ac75..74e8d229c9dd 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c @@ -82,6 +82,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_i2c.h" +#include "dio/dcn10/dcn10_dio.h" #include "vm_helper.h" #include "link_enc_cfg.h" @@ -550,6 +551,33 @@ static const struct dcn_hubbub_mask hubbub_mask = { HUBBUB_MASK_SH_LIST_DCN20(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn20_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + #define vmid_regs(id)\ [id] = {\ DCN20_VMID_REG_LIST(id)\ @@ -1104,6 +1132,12 @@ static void dcn20_resource_destruct(struct dcn20_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn20_dpp_destroy(&pool->base.dpps[i]); @@ -2692,6 +2726,14 @@ static bool dcn20_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn20_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + for (i = 0; i < pool->base.res_cap->num_dsc; i++) { pool->base.dscs[i] = dcn20_dsc_create(ctx, i); if (pool->base.dscs[i] == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c index 4ea76e46ab15..e289be70efb5 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c @@ -56,6 +56,7 @@ #include "dce/dce_aux.h" #include "dce/dce_i2c.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "cyan_skillfish_ip_offset.h" @@ -755,6 +756,33 @@ static struct hubbub *dcn201_hubbub_create(struct dc_context *ctx) return &hubbub->base; } +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn201_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct timing_generator *dcn201_timing_generator_create( struct dc_context *ctx, uint32_t instance) @@ -930,6 +958,11 @@ static void dcn201_resource_destruct(struct dcn201_resource_pool *pool) pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn201_dpp_destroy(&pool->base.dpps[i]); @@ -1276,6 +1309,14 @@ static bool dcn201_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn201_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + if (!resource_construct(num_virtual_links, dc, &pool->base, &res_create_funcs)) goto create_fail; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c index 0f4307f8f3dd..4333baac96ad 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c @@ -84,6 +84,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_i2c.h" +#include "dio/dcn10/dcn10_dio.h" #include "dcn21_resource.h" #include "vm_helper.h" #include "dcn20/dcn20_vmid.h" @@ -329,6 +330,25 @@ static const struct dcn_hubbub_mask hubbub_mask = { HUBBUB_MASK_SH_LIST_DCN21(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +static const struct dcn_dio_shift dio_shift = { 0 }; + +static const struct dcn_dio_mask dio_mask = { 0 }; + +static struct dio *dcn21_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} #define vmid_regs(id)\ [id] = {\ @@ -677,6 +697,12 @@ static void dcn21_resource_destruct(struct dcn21_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn20_dpp_destroy(&pool->base.dpps[i]); @@ -1654,6 +1680,14 @@ static bool dcn21_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn21_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + for (i = 0; i < pool->base.res_cap->num_dsc; i++) { pool->base.dscs[i] = dcn21_dsc_create(ctx, i); if (pool->base.dscs[i] == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 2fa86b9587ed..87b7b4ee04c6 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -60,6 +60,7 @@ #include "dml/display_mode_vba.h" #include "dcn30/dcn30_dccg.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "link_service.h" #include "dce/dce_panel_cntl.h" @@ -886,6 +887,33 @@ static struct hubbub *dcn30_hubbub_create(struct dc_context *ctx) return &hubbub3->base; } +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn30_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct timing_generator *dcn30_timing_generator_create( struct dc_context *ctx, uint32_t instance) @@ -1095,6 +1123,12 @@ static void dcn30_resource_destruct(struct dcn30_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn30_dpp_destroy(&pool->base.dpps[i]); @@ -2464,6 +2498,14 @@ static bool dcn30_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn30_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn30_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c index 7842bee57e63..6bb1c62124bb 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c @@ -59,6 +59,7 @@ #include "dml/display_mode_vba.h" #include "dcn301/dcn301_dccg.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "dcn30/dcn30_dio_stream_encoder.h" #include "dcn301/dcn301_dio_link_encoder.h" #include "dcn301/dcn301_panel_cntl.h" @@ -843,6 +844,33 @@ static struct hubbub *dcn301_hubbub_create(struct dc_context *ctx) return &hubbub3->base; } +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn301_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct timing_generator *dcn301_timing_generator_create( struct dc_context *ctx, uint32_t instance) { @@ -1066,6 +1094,12 @@ static void dcn301_destruct(struct dcn301_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn301_dpp_destroy(&pool->base.dpps[i]); @@ -1582,6 +1616,14 @@ static bool dcn301_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn301_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + j = 0; /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index 1874d5d6b782..d02aafd06fd4 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -46,6 +46,7 @@ #include "dml/dcn30/dcn30_fpu.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "link_service.h" @@ -253,6 +254,33 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn302_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn302_hubbub_create(struct dc_context *ctx) { int i; @@ -1022,6 +1050,11 @@ static void dcn302_resource_destruct(struct resource_pool *pool) pool->hubbub = NULL; } + if (pool->dio != NULL) { + kfree(TO_DCN10_DIO(pool->dio)); + pool->dio = NULL; + } + for (i = 0; i < pool->pipe_count; i++) { if (pool->dpps[i] != NULL) { kfree(TO_DCN20_DPP(pool->dpps[i])); @@ -1372,6 +1405,14 @@ static bool dcn302_resource_construct( goto create_fail; } + /* DIO */ + pool->dio = dcn302_dio_create(ctx); + if (pool->dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->pipe_count; i++) { pool->hubps[i] = dcn302_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index d52201cb359f..30b1403112c6 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -46,6 +46,7 @@ #include "dml/dcn30/dcn30_fpu.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "link_service.h" @@ -249,6 +250,33 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn303_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn303_hubbub_create(struct dc_context *ctx) { int i; @@ -966,6 +994,11 @@ static void dcn303_resource_destruct(struct resource_pool *pool) pool->hubbub = NULL; } + if (pool->dio != NULL) { + kfree(TO_DCN10_DIO(pool->dio)); + pool->dio = NULL; + } + for (i = 0; i < pool->pipe_count; i++) { if (pool->dpps[i] != NULL) { kfree(TO_DCN20_DPP(pool->dpps[i])); @@ -1304,6 +1337,14 @@ static bool dcn303_resource_construct( goto create_fail; } + /* DIO */ + pool->dio = dcn303_dio_create(ctx); + if (pool->dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->pipe_count; i++) { pool->hubps[i] = dcn303_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 2055f1f8af65..4e9c041c707a 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -64,6 +64,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -810,6 +811,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn31 = { .num_timing_generator = 4, .num_opp = 4, @@ -1021,6 +1037,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn31_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1396,6 +1424,10 @@ static void dcn31_resource_destruct(struct dcn31_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -2063,6 +2095,14 @@ static bool dcn31_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn31_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index 1939f720ba29..e26a6427916a 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -66,6 +66,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -822,6 +823,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn314 = { .num_timing_generator = 4, .num_opp = 4, @@ -1079,6 +1095,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn314_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1455,6 +1483,10 @@ static void dcn314_resource_destruct(struct dcn314_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -1987,6 +2019,14 @@ static bool dcn314_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn314_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index e8377c190f63..131a6cd4c735 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -63,6 +63,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -809,6 +810,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn31 = { .num_timing_generator = 4, .num_opp = 4, @@ -1020,6 +1036,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn315_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1397,6 +1425,10 @@ static void dcn315_resource_destruct(struct dcn315_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -2012,6 +2044,14 @@ static bool dcn315_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn315_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index 045ce01bd74e..c8c0ce6efcfd 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -63,6 +63,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -804,6 +805,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn31 = { .num_timing_generator = 4, .num_opp = 4, @@ -1013,6 +1029,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn316_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1392,6 +1420,10 @@ static void dcn316_resource_destruct(struct dcn316_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -1887,6 +1919,14 @@ static bool dcn316_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn316_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index c7fd604024d6..c3a6ae14de18 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -66,6 +66,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dml/display_mode_vba.h" #include "dcn32/dcn32_dccg.h" #include "dcn10/dcn10_resource.h" @@ -643,6 +644,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn32 = { .num_timing_generator = 4, .num_opp = 4, @@ -833,6 +847,22 @@ static struct clock_source *dcn32_clock_source_create( return NULL; } +static struct dio *dcn32_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn32_hubbub_create(struct dc_context *ctx) { int i; @@ -1494,6 +1524,11 @@ static void dcn32_resource_destruct(struct dcn32_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + if (pool->base.oem_device != NULL) { struct dc *dc = pool->base.oem_device->ctx->dc; @@ -2373,6 +2408,14 @@ static bool dcn32_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn32_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs, TGs, ABMs */ for (i = 0, j = 0; i < pool->base.res_cap->num_timing_generator; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index c1582c27ac87..990aec7eb3d0 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -69,6 +69,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dml/display_mode_vba.h" #include "dcn32/dcn32_dccg.h" #include "dcn10/dcn10_resource.h" @@ -639,6 +640,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn321 = { .num_timing_generator = 4, .num_opp = 4, @@ -827,6 +841,22 @@ static struct clock_source *dcn321_clock_source_create( return NULL; } +static struct dio *dcn321_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn321_hubbub_create(struct dc_context *ctx) { int i; @@ -1474,6 +1504,11 @@ static void dcn321_resource_destruct(struct dcn321_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + if (pool->base.oem_device != NULL) { struct dc *dc = pool->base.oem_device->ctx->dc; @@ -1872,6 +1907,14 @@ static bool dcn321_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn321_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs, TGs, ABMs */ for (i = 0, j = 0; i < pool->base.res_cap->num_timing_generator; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 3494a40cea99..598b2f25881d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -71,6 +71,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" #include "dcn35/dcn35_dccg.h" @@ -664,6 +665,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn35 = { .num_timing_generator = 4, .num_opp = 4, @@ -973,6 +987,22 @@ static struct mpc *dcn35_mpc_create( return &mpc30->base; } +static struct dio *dcn35_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn35_hubbub_create(struct dc_context *ctx) { int i; @@ -1563,6 +1593,11 @@ static void dcn35_resource_destruct(struct dcn35_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } } static struct hubp *dcn35_hubp_create( @@ -2033,6 +2068,14 @@ static bool dcn35_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn35_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn35_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index 080bc7f24ffa..7e15d07df7a3 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -50,6 +50,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" #include "dcn35/dcn35_dccg.h" @@ -644,6 +645,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn351 = { .num_timing_generator = 4, .num_opp = 4, @@ -953,6 +967,22 @@ static struct mpc *dcn35_mpc_create( return &mpc30->base; } +static struct dio *dcn351_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn35_hubbub_create(struct dc_context *ctx) { int i; @@ -1543,6 +1573,11 @@ static void dcn351_resource_destruct(struct dcn351_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } } static struct hubp *dcn35_hubp_create( @@ -2005,6 +2040,14 @@ static bool dcn351_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn351_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn35_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index af51ac4ea59e..83fee2ca61bf 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -50,6 +50,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" #include "dcn35/dcn35_dccg.h" @@ -651,6 +652,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn36 = { .num_timing_generator = 4, .num_opp = 4, @@ -960,6 +974,22 @@ static struct mpc *dcn35_mpc_create( return &mpc30->base; } +static struct dio *dcn36_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn35_hubbub_create(struct dc_context *ctx) { int i; @@ -1550,6 +1580,11 @@ static void dcn36_resource_destruct(struct dcn36_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } } static struct hubp *dcn35_hubp_create( @@ -2012,6 +2047,14 @@ static bool dcn36_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn36_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn35_hubp_create(ctx, i); From 0c4a59df370bea245695c00aaae6ae75747139bd Mon Sep 17 00:00:00 2001 From: Changwoo Min Date: Thu, 2 Apr 2026 11:31:50 +0900 Subject: [PATCH 690/712] sched_ext: Fix is_bpf_migration_disabled() false negative on non-PREEMPT_RCU Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for trampoline.c"), the BPF prolog (__bpf_prog_enter) calls migrate_disable() only when CONFIG_PREEMPT_RCU is enabled, via rcu_read_lock_dont_migrate(). Without CONFIG_PREEMPT_RCU, the prolog never touches migration_disabled, so migration_disabled == 1 always means the task is truly migration-disabled regardless of whether it is the current task. The old unconditional p == current check was a false negative in this case, potentially allowing a migration-disabled task to be dispatched to a remote CPU and triggering scx_error in task_can_run_on_remote_rq(). Only apply the p == current disambiguation when CONFIG_PREEMPT_RCU is enabled, where the ambiguity with the BPF prolog still exists. Fixes: 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for trampoline.c") Cc: stable@vger.kernel.org # v6.18+ Link: https://lore.kernel.org/lkml/20250821090609.42508-8-dongml2@chinatelecom.cn/ Signed-off-by: Changwoo Min Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext_idle.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/kernel/sched/ext_idle.c b/kernel/sched/ext_idle.c index 0ae93cd64004..44c3a50c542c 100644 --- a/kernel/sched/ext_idle.c +++ b/kernel/sched/ext_idle.c @@ -860,25 +860,32 @@ static bool check_builtin_idle_enabled(struct scx_sched *sch) * code. * * We can't simply check whether @p->migration_disabled is set in a - * sched_ext callback, because migration is always disabled for the current - * task while running BPF code. + * sched_ext callback, because the BPF prolog (__bpf_prog_enter) may disable + * migration for the current task while running BPF code. * - * The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) respectively - * disable and re-enable migration. For this reason, the current task - * inside a sched_ext callback is always a migration-disabled task. + * Since the BPF prolog calls migrate_disable() only when CONFIG_PREEMPT_RCU + * is enabled (via rcu_read_lock_dont_migrate()), migration_disabled == 1 for + * the current task is ambiguous only in that case: it could be from the BPF + * prolog rather than a real migrate_disable() call. * - * Therefore, when @p->migration_disabled == 1, check whether @p is the - * current task or not: if it is, then migration was not disabled before - * entering the callback, otherwise migration was disabled. + * Without CONFIG_PREEMPT_RCU, the BPF prolog never calls migrate_disable(), + * so migration_disabled == 1 always means the task is truly + * migration-disabled. + * + * Therefore, when migration_disabled == 1 and CONFIG_PREEMPT_RCU is enabled, + * check whether @p is the current task or not: if it is, then migration was + * not disabled before entering the callback, otherwise migration was disabled. * * Returns true if @p is migration-disabled, false otherwise. */ static bool is_bpf_migration_disabled(const struct task_struct *p) { - if (p->migration_disabled == 1) - return p != current; - else - return p->migration_disabled; + if (p->migration_disabled == 1) { + if (IS_ENABLED(CONFIG_PREEMPT_RCU)) + return p != current; + return true; + } + return p->migration_disabled; } static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p, From f2b1cbef153636fa498324b47e822e8b4d1774aa Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 3 Apr 2026 08:20:16 +0200 Subject: [PATCH 691/712] Documentation: minor updates to the security contacts This clarifies the fact that the bug reporters must use a valid e-mail address to send their report, and that the security team assists developers working on a fix but doesn't always produce fixes on its own. Cc: Eric Dumazet Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260403062018.31080-2-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index c0cf93e11565..da7937fd59df 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -8,6 +8,10 @@ like to know when a security bug is found so that it can be fixed and disclosed as quickly as possible. Please report security bugs to the Linux kernel security team. +Reports are to be sent over e-mail exclusively. Please use a working e-mail +address, preferably the same that you want to appear in ``Reported-by`` tags +if any. If unsure, send your report to yourself first. + The security team and maintainers almost always require additional information beyond what was initially provided in a report and rely on active and efficient collaboration with the reporter to perform further @@ -27,11 +31,9 @@ made public. The Linux kernel security team can be contacted by email at . This is a private list of security officers -who will help verify the bug report and develop and release a fix. -If you already have a fix, please include it with your report, as -that can speed up the process considerably. It is possible that the -security team will bring in extra help from area maintainers to -understand and fix the security vulnerability. +who will help verify the bug report and assist developers working on a fix. +It is possible that the security team will bring in extra help from area +maintainers to understand and fix the security vulnerability. Please send **plain text** emails without attachments where possible. It is much harder to have a context-quoted discussion about a complex From a72b832a482372001a158c8014d116b053089b5d Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 3 Apr 2026 08:20:17 +0200 Subject: [PATCH 692/712] Documentation: explain how to find maintainers addresses for security reports These days, 80% of the work done by the security team consists in locating the affected subsystem in a report, running get_maintainers on it, forwarding the report to these persons and responding to the reporter with them in Cc. This is a huge and unneeded overhead that we must try to lower for a better overall efficiency. This patch adds a complete section explaining how to figure the list of recipients to send the report to. Cc: Eric Dumazet Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260403062018.31080-3-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 76 ++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index da7937fd59df..ac97fc78fecd 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -5,8 +5,75 @@ Security bugs Linux kernel developers take security very seriously. As such, we'd like to know when a security bug is found so that it can be fixed and -disclosed as quickly as possible. Please report security bugs to the -Linux kernel security team. +disclosed as quickly as possible. + +Identifying contacts +-------------------- + +The most effective way to report a security bug is to send it directly to the +affected subsystem's maintainers and Cc: the Linux kernel security team. Do +not send it to a public list at this stage, unless you have good reasons to +consider the issue as being public or trivial to discover (e.g. result of a +widely available automated vulnerability scanning tool that can be repeated by +anyone). + +If you're sending a report for issues affecting multiple parts in the kernel, +even if they're fairly similar issues, please send individual messages (think +that maintainers will not all work on the issues at the same time). The only +exception is when an issue concerns closely related parts maintained by the +exact same subset of maintainers, and these parts are expected to be fixed all +at once by the same commit, then it may be acceptable to report them at once. + +One difficulty for most first-time reporters is to figure the right list of +recipients to send a report to. In the Linux kernel, all official maintainers +are trusted, so the consequences of accidentally including the wrong maintainer +are essentially a bit more noise for that person, i.e. nothing dramatic. As +such, a suitable method to figure the list of maintainers (which kernel +security officers use) is to rely on the get_maintainers.pl script, tuned to +only report maintainers. This script, when passed a file name, will look for +its path in the MAINTAINERS file to figure a hierarchical list of relevant +maintainers. Calling it a first time with the finest level of filtering will +most of the time return a short list of this specific file's maintainers:: + + $ ./scripts/get_maintainer.pl --no-l --no-r --pattern-depth 1 \ + drivers/example.c + Developer One (maintainer:example driver) + Developer Two (maintainer:example driver) + +These two maintainers should then receive the message. If the command does not +return anything, it means the affected file is part of a wider subsystem, so we +should be less specific:: + + $ ./scripts/get_maintainer.pl --no-l --no-r drivers/example.c + Developer One (maintainer:example subsystem) + Developer Two (maintainer:example subsystem) + Developer Three (maintainer:example subsystem [GENERAL]) + Developer Four (maintainer:example subsystem [GENERAL]) + +Here, picking the first, most specific ones, is sufficient. When the list is +long, it is possible to produce a comma-delimited e-mail address list on a +single line suitable for use in the To: field of a mailer like this:: + + $ ./scripts/get_maintainer.pl --no-tree --no-l --no-r --no-n --m \ + --no-git-fallback --no-substatus --no-rolestats --no-multiline \ + --pattern-depth 1 drivers/example.c + dev1@example.com, dev2@example.org + +or this for the wider list:: + + $ ./scripts/get_maintainer.pl --no-tree --no-l --no-r --no-n --m \ + --no-git-fallback --no-substatus --no-rolestats --no-multiline \ + drivers/example.c + dev1@example.com, dev2@example.org, dev3@example.com, dev4@example.org + +If at this point you're still facing difficulties spotting the right +maintainers, **and only in this case**, it's possible to send your report to +the Linux kernel security team only. Your message will be triaged, and you +will receive instructions about whom to contact, if needed. Your message may +equally be forwarded as-is to the relevant maintainers. + +Sending the report +------------------ Reports are to be sent over e-mail exclusively. Please use a working e-mail address, preferably the same that you want to appear in ``Reported-by`` tags @@ -29,6 +96,7 @@ information is helpful. Any exploit code is very helpful and will not be released without consent from the reporter unless it has already been made public. +The report must be sent to maintainers, with the security team in ``Cc:``. The Linux kernel security team can be contacted by email at . This is a private list of security officers who will help verify the bug report and assist developers working on a fix. @@ -44,7 +112,9 @@ reproduction steps, and follow it with a proposed fix, all in plain text. Markdown, HTML and RST formatted reports are particularly frowned upon since they're quite hard to read for humans and encourage to use dedicated viewers, sometimes online, which by definition is not acceptable for a confidential -security report. +security report. Note that some mailers tend to mangle formatting of plain +text by default, please consult Documentation/process/email-clients.rst for +more info. Disclosure and embargoed information ------------------------------------ From 496fa1befba1e8ff149af5120cd9c9616bb05120 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 3 Apr 2026 08:20:18 +0200 Subject: [PATCH 693/712] Documentation: clarify the mandatory and desirable info for security reports A significant part of the effort of the security team consists in begging reporters for patch proposals, or asking them to provide them in regular format, and most of the time they're willing to provide this, they just didn't know that it would help. So let's add a section detailing the required and desirable contents in a security report to help reporters write more actionable reports which do not require round trips. Cc: Eric Dumazet Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260403062018.31080-4-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 66 ++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index ac97fc78fecd..0b1f6d8e3cbe 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -7,6 +7,65 @@ Linux kernel developers take security very seriously. As such, we'd like to know when a security bug is found so that it can be fixed and disclosed as quickly as possible. +Preparing your report +--------------------- + +Like with any bug report, a security bug report requires a lot of analysis work +from the developers, so the more information you can share about the issue, the +better. Please review the procedure outlined in +Documentation/admin-guide/reporting-issues.rst if you are unclear about what +information is helpful. The following information are absolutely necessary in +**any** security bug report: + + * **affected kernel version range**: with no version indication, your report + will not be processed. A significant part of reports are for bugs that + have already been fixed, so it is extremely important that vulnerabilities + are verified on recent versions (development tree or latest stable + version), at least by verifying that the code has not changed since the + version where it was detected. + + * **description of the problem**: a detailed description of the problem, with + traces showing its manifestation, and why you consider that the observed + behavior as a problem in the kernel, is necessary. + + * **reproducer**: developers will need to be able to reproduce the problem to + consider a fix as effective. This includes both a way to trigger the issue + and a way to confirm it happens. A reproducer with low complexity + dependencies will be needed (source code, shell script, sequence of + instructions, file-system image etc). Binary-only executables are not + accepted. Working exploits are extremely helpful and will not be released + without consent from the reporter, unless they are already public. By + definition if an issue cannot be reproduced, it is not exploitable, thus it + is not a security bug. + + * **conditions**: if the bug depends on certain configuration options, + sysctls, permissions, timing, code modifications etc, these should be + indicated. + +In addition, the following information are highly desirable: + + * **suspected location of the bug**: the file names and functions where the + bug is suspected to be present are very important, at least to help forward + the report to the appropriate maintainers. When not possible (for example, + "system freezes each time I run this command"), the security team will help + identify the source of the bug. + + * **a proposed fix**: bug reporters who have analyzed the cause of a bug in + the source code almost always have an accurate idea on how to fix it, + because they spent a long time studying it and its implications. Proposing + a tested fix will save maintainers a lot of time, even if the fix ends up + not being the right one, because it helps understand the bug. When + proposing a tested fix, please always format it in a way that can be + immediately merged (see Documentation/process/submitting-patches.rst). + This will save some back-and-forth exchanges if it is accepted, and you + will be credited for finding and fixing this issue. Note that in this case + only a ``Signed-off-by:`` tag is needed, without ``Reported-by:` when the + reporter and author are the same. + + * **mitigations**: very often during a bug analysis, some ways of mitigating + the issue appear. It is useful to share them, as they can be helpful to + keep end users protected during the time it takes them to apply the fix. + Identifying contacts -------------------- @@ -89,13 +148,6 @@ run additional tests. Reports where the reporter does not respond promptly or cannot effectively discuss their findings may be abandoned if the communication does not quickly improve. -As it is with any bug, the more information provided the easier it -will be to diagnose and fix. Please review the procedure outlined in -'Documentation/admin-guide/reporting-issues.rst' if you are unclear about what -information is helpful. Any exploit code is very helpful and will not -be released without consent from the reporter unless it has already been -made public. - The report must be sent to maintainers, with the security team in ``Cc:``. The Linux kernel security team can be contacted by email at . This is a private list of security officers From 7e0ffb72de8aa3b25989c2d980e81b829c577010 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Fri, 3 Apr 2026 08:57:20 +0200 Subject: [PATCH 694/712] sched_ext: Fix stale direct dispatch state in ddsp_dsq_id @p->scx.ddsp_dsq_id can be left set (non-SCX_DSQ_INVALID) triggering a spurious warning in mark_direct_dispatch() when the next wakeup's ops.select_cpu() calls scx_bpf_dsq_insert(), such as: WARNING: kernel/sched/ext.c:1273 at scx_dsq_insert_commit+0xcd/0x140 The root cause is that ddsp_dsq_id was only cleared in dispatch_enqueue(), which is not reached in all paths that consume or cancel a direct dispatch verdict. Fix it by clearing it at the right places: - direct_dispatch(): cache the direct dispatch state in local variables and clear it before dispatch_enqueue() on the synchronous path. For the deferred path, the direct dispatch state must remain set until process_ddsp_deferred_locals() consumes them. - process_ddsp_deferred_locals(): cache the dispatch state in local variables and clear it before calling dispatch_to_local_dsq(), which may migrate the task to another rq. - do_enqueue_task(): clear the dispatch state on the enqueue path (local/global/bypass fallbacks), where the direct dispatch verdict is ignored. - dequeue_task_scx(): clear the dispatch state after dispatch_dequeue() to handle both the deferred dispatch cancellation and the holding_cpu race, covering all cases where a pending direct dispatch is cancelled. - scx_disable_task(): clear the direct dispatch state when transitioning a task out of the current scheduler. Waking tasks may have had the direct dispatch state set by the outgoing scheduler's ops.select_cpu() and then been queued on a wake_list via ttwu_queue_wakelist(), when SCX_OPS_ALLOW_QUEUED_WAKEUP is set. Such tasks are not on the runqueue and are not iterated by scx_bypass(), so their direct dispatch state won't be cleared. Without this clear, any subsequent SCX scheduler that tries to direct dispatch the task will trigger the WARN_ON_ONCE() in mark_direct_dispatch(). Fixes: 5b26f7b920f7 ("sched_ext: Allow SCX_DSQ_LOCAL_ON for direct dispatches") Cc: stable@vger.kernel.org # v6.12+ Cc: Daniel Hodges Cc: Patrick Somaru Signed-off-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext.c | 49 +++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index d5bdcdb3f700..064eaa76be4b 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -1109,15 +1109,6 @@ static void dispatch_enqueue(struct scx_sched *sch, struct scx_dispatch_q *dsq, dsq_mod_nr(dsq, 1); p->scx.dsq = dsq; - /* - * scx.ddsp_dsq_id and scx.ddsp_enq_flags are only relevant on the - * direct dispatch path, but we clear them here because the direct - * dispatch verdict may be overridden on the enqueue path during e.g. - * bypass. - */ - p->scx.ddsp_dsq_id = SCX_DSQ_INVALID; - p->scx.ddsp_enq_flags = 0; - /* * We're transitioning out of QUEUEING or DISPATCHING. store_release to * match waiters' load_acquire. @@ -1283,12 +1274,34 @@ static void mark_direct_dispatch(struct scx_sched *sch, p->scx.ddsp_enq_flags = enq_flags; } +/* + * Clear @p direct dispatch state when leaving the scheduler. + * + * Direct dispatch state must be cleared in the following cases: + * - direct_dispatch(): cleared on the synchronous enqueue path, deferred + * dispatch keeps the state until consumed + * - process_ddsp_deferred_locals(): cleared after consuming deferred state, + * - do_enqueue_task(): cleared on enqueue fallbacks where the dispatch + * verdict is ignored (local/global/bypass) + * - dequeue_task_scx(): cleared after dispatch_dequeue(), covering deferred + * cancellation and holding_cpu races + * - scx_disable_task(): cleared for queued wakeup tasks, which are excluded by + * the scx_bypass() loop, so that stale state is not reused by a subsequent + * scheduler instance + */ +static inline void clear_direct_dispatch(struct task_struct *p) +{ + p->scx.ddsp_dsq_id = SCX_DSQ_INVALID; + p->scx.ddsp_enq_flags = 0; +} + static void direct_dispatch(struct scx_sched *sch, struct task_struct *p, u64 enq_flags) { struct rq *rq = task_rq(p); struct scx_dispatch_q *dsq = find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, p); + u64 ddsp_enq_flags; touch_core_sched_dispatch(rq, p); @@ -1329,8 +1342,10 @@ static void direct_dispatch(struct scx_sched *sch, struct task_struct *p, return; } - dispatch_enqueue(sch, dsq, p, - p->scx.ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS); + ddsp_enq_flags = p->scx.ddsp_enq_flags; + clear_direct_dispatch(p); + + dispatch_enqueue(sch, dsq, p, ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS); } static bool scx_rq_online(struct rq *rq) @@ -1439,6 +1454,7 @@ static void do_enqueue_task(struct rq *rq, struct task_struct *p, u64 enq_flags, */ touch_core_sched(rq, p); refill_task_slice_dfl(sch, p); + clear_direct_dispatch(p); dispatch_enqueue(sch, dsq, p, enq_flags); } @@ -1610,6 +1626,7 @@ static bool dequeue_task_scx(struct rq *rq, struct task_struct *p, int deq_flags sub_nr_running(rq, 1); dispatch_dequeue(rq, p); + clear_direct_dispatch(p); return true; } @@ -2293,13 +2310,15 @@ static void process_ddsp_deferred_locals(struct rq *rq) struct task_struct, scx.dsq_list.node))) { struct scx_sched *sch = scx_root; struct scx_dispatch_q *dsq; + u64 dsq_id = p->scx.ddsp_dsq_id; + u64 enq_flags = p->scx.ddsp_enq_flags; list_del_init(&p->scx.dsq_list.node); + clear_direct_dispatch(p); - dsq = find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, p); + dsq = find_dsq_for_dispatch(sch, rq, dsq_id, p); if (!WARN_ON_ONCE(dsq->id != SCX_DSQ_LOCAL)) - dispatch_to_local_dsq(sch, rq, dsq, p, - p->scx.ddsp_enq_flags); + dispatch_to_local_dsq(sch, rq, dsq, p, enq_flags); } } @@ -3015,6 +3034,8 @@ static void scx_disable_task(struct task_struct *p) lockdep_assert_rq_held(rq); WARN_ON_ONCE(scx_get_task_state(p) != SCX_TASK_ENABLED); + clear_direct_dispatch(p); + if (SCX_HAS_OP(sch, disable)) SCX_CALL_OP_TASK(sch, SCX_KF_REST, disable, rq, p); scx_set_task_state(p, SCX_TASK_READY); From e2b0ae529db4766584e77647cefe3ec15c3d842e Mon Sep 17 00:00:00 2001 From: Zoltan Illes Date: Fri, 3 Apr 2026 22:03:42 -0700 Subject: [PATCH 695/712] Input: xpad - add support for Razer Wolverine V3 Pro Add device IDs for the Razer Wolverine V3 Pro controller in both wired (0x0a57) and wireless 2.4 GHz dongle (0x0a59) modes. The controller uses the Xbox 360 protocol (vendor-specific class, subclass 93, protocol 1) on interface 0 with an identical 20-byte input report layout, so no additional processing is needed. Signed-off-by: Zoltan Illes Link: https://patch.msgid.link/20260329220031.1325509-1-137647604+ZlordHUN@users.noreply.github.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 363d50949386..655226fe493d 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -313,6 +313,8 @@ static const struct xpad_device { { 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE }, { 0x1532, 0x0a29, "Razer Wolverine V2", 0, XTYPE_XBOXONE }, + { 0x1532, 0x0a57, "Razer Wolverine V3 Pro (Wired)", 0, XTYPE_XBOX360 }, + { 0x1532, 0x0a59, "Razer Wolverine V3 Pro (2.4 GHz Dongle)", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 }, From 0d9363a764d9d601a05591f9695cea8b429e9be3 Mon Sep 17 00:00:00 2001 From: Shengyu Qu Date: Fri, 3 Apr 2026 22:07:28 -0700 Subject: [PATCH 696/712] Input: xpad - add support for BETOP BTP-KP50B/C controller's wireless mode BETOP's BTP-KP50B and BTP-KP50C controller's wireless dongles are both working as standard Xbox 360 controllers. Add USB device IDs for them to xpad driver. Signed-off-by: Shengyu Qu Link: https://patch.msgid.link/TY4PR01MB14432B4B298EA186E5F86C46B9855A@TY4PR01MB14432.jpnprd01.prod.outlook.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 655226fe493d..627e8950e451 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -362,6 +362,8 @@ static const struct xpad_device { { 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 }, { 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 }, { 0x1ee9, 0x1590, "ZOTAC Gaming Zone", 0, XTYPE_XBOX360 }, + { 0x20bc, 0x5134, "BETOP BTP-KP50B Xinput Dongle", 0, XTYPE_XBOX360 }, + { 0x20bc, 0x514a, "BETOP BTP-KP50C Xinput Dongle", 0, XTYPE_XBOX360 }, { 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE }, { 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE }, { 0x20d6, 0x2064, "PowerA Wired Controller for Xbox", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, @@ -564,6 +566,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x1a86), /* Nanjing Qinheng Microelectronics (WCH) */ XPAD_XBOX360_VENDOR(0x1bad), /* Harmonix Rock Band guitar and drums */ XPAD_XBOX360_VENDOR(0x1ee9), /* ZOTAC Technology Limited */ + XPAD_XBOX360_VENDOR(0x20bc), /* BETOP wireless dongles */ XPAD_XBOX360_VENDOR(0x20d6), /* PowerA controllers */ XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA controllers */ XPAD_XBOX360_VENDOR(0x2345), /* Machenike Controllers */ From f387e2e2b9d302688dbdceebe9aade221c90f09e Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 4 Apr 2026 10:20:33 +0200 Subject: [PATCH 697/712] Documentation: fix two typos in latest update to the security report howto In previous patch "Documentation: clarify the mandatory and desirable info for security reports" I left two typos that I didn't detect in local checks. One is "get_maintainers.pl" (no 's' in the script name), and the other one is a missing closing quote after "Reported-by", which didn't have effect here but I don't know if it can break rendering elsewhere (e.g. on the public HTML page). Better fix it before it gets merged. Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260404082033.5160-1-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index 0b1f6d8e3cbe..27b028e85861 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -59,7 +59,7 @@ In addition, the following information are highly desirable: immediately merged (see Documentation/process/submitting-patches.rst). This will save some back-and-forth exchanges if it is accepted, and you will be credited for finding and fixing this issue. Note that in this case - only a ``Signed-off-by:`` tag is needed, without ``Reported-by:` when the + only a ``Signed-off-by:`` tag is needed, without ``Reported-by:`` when the reporter and author are the same. * **mitigations**: very often during a bug analysis, some ways of mitigating @@ -88,7 +88,7 @@ recipients to send a report to. In the Linux kernel, all official maintainers are trusted, so the consequences of accidentally including the wrong maintainer are essentially a bit more noise for that person, i.e. nothing dramatic. As such, a suitable method to figure the list of maintainers (which kernel -security officers use) is to rely on the get_maintainers.pl script, tuned to +security officers use) is to rely on the get_maintainer.pl script, tuned to only report maintainers. This script, when passed a file name, will look for its path in the MAINTAINERS file to figure a hierarchical list of relevant maintainers. Calling it a first time with the finest level of filtering will From 834911eb8eef2501485d819b4eabebadc25c3497 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 23 Mar 2026 17:43:47 -0600 Subject: [PATCH 698/712] riscv: kgdb: fix several debug register assignment bugs Fix several bugs in the RISC-V kgdb implementation: - The element of dbg_reg_def[] that is supposed to pertain to the S1 register embeds instead the struct pt_regs offset of the A1 register. Fix this to use the S1 register offset in struct pt_regs. - The sleeping_thread_to_gdb_regs() function copies the value of the S10 register into the gdb_regs[] array element meant for the S9 register, and copies the value of the S11 register into the array element meant for the S10 register. It also neglects to copy the value of the S11 register. Fix all of these issues. Fixes: fe89bd2be8667 ("riscv: Add KGDB support") Cc: Vincent Chen Link: https://patch.msgid.link/fde376f8-bcfd-bfe4-e467-07d8f7608d05@kernel.org Signed-off-by: Paul Walmsley --- arch/riscv/kernel/kgdb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/kgdb.c b/arch/riscv/kernel/kgdb.c index 15fec5d1e6de..0bf629204c76 100644 --- a/arch/riscv/kernel/kgdb.c +++ b/arch/riscv/kernel/kgdb.c @@ -175,7 +175,7 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { {DBG_REG_T1, GDB_SIZEOF_REG, offsetof(struct pt_regs, t1)}, {DBG_REG_T2, GDB_SIZEOF_REG, offsetof(struct pt_regs, t2)}, {DBG_REG_FP, GDB_SIZEOF_REG, offsetof(struct pt_regs, s0)}, - {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)}, + {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, s1)}, {DBG_REG_A0, GDB_SIZEOF_REG, offsetof(struct pt_regs, a0)}, {DBG_REG_A1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)}, {DBG_REG_A2, GDB_SIZEOF_REG, offsetof(struct pt_regs, a2)}, @@ -244,8 +244,9 @@ sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task) gdb_regs[DBG_REG_S6_OFF] = task->thread.s[6]; gdb_regs[DBG_REG_S7_OFF] = task->thread.s[7]; gdb_regs[DBG_REG_S8_OFF] = task->thread.s[8]; - gdb_regs[DBG_REG_S9_OFF] = task->thread.s[10]; - gdb_regs[DBG_REG_S10_OFF] = task->thread.s[11]; + gdb_regs[DBG_REG_S9_OFF] = task->thread.s[9]; + gdb_regs[DBG_REG_S10_OFF] = task->thread.s[10]; + gdb_regs[DBG_REG_S11_OFF] = task->thread.s[11]; gdb_regs[DBG_REG_EPC_OFF] = task->thread.ra; } From 6b60a128c2f43180664a614830f3c529497e0394 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Mon, 23 Mar 2026 17:43:47 -0600 Subject: [PATCH 699/712] riscv: patch: Avoid early phys_to_page() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to commit 8d09e2d569f6 ("arm64: patching: avoid early page_to_phys()"), avoid using phys_to_page() for the kernel address case in patch_map(). Since this is called from apply_boot_alternatives() in setup_arch(), and commit 4267739cabb8 ("arch, mm: consolidate initialization of SPARSE memory model") has moved sparse_init() to after setup_arch(), phys_to_page() is not available there yet, and it panics on boot with SPARSEMEM on RV32, which does not use SPARSEMEM_VMEMMAP. Reported-by: Thomas Weißschuh Closes: https://lore.kernel.org/r/20260223144108-dcace0b9-02e8-4b67-a7ce-f263bed36f26@linutronix.de/ Fixes: 4267739cabb8 ("arch, mm: consolidate initialization of SPARSE memory model") Suggested-by: Mike Rapoport Signed-off-by: Vivian Wang Acked-by: Mike Rapoport (Microsoft) Tested-by: Thomas Weißschuh Link: https://patch.msgid.link/20260310-riscv-sparsemem-alternatives-fix-v1-1-659d5dd257e2@iscas.ac.cn [pjw@kernel.org: fix the subject line to align with the patch description] Signed-off-by: Paul Walmsley --- arch/riscv/kernel/patch.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c index db13c9ddf9e3..16b243376f36 100644 --- a/arch/riscv/kernel/patch.c +++ b/arch/riscv/kernel/patch.c @@ -42,19 +42,20 @@ static inline bool is_kernel_exittext(uintptr_t addr) static __always_inline void *patch_map(void *addr, const unsigned int fixmap) { uintptr_t uintaddr = (uintptr_t) addr; - struct page *page; + phys_addr_t phys; - if (core_kernel_text(uintaddr) || is_kernel_exittext(uintaddr)) - page = phys_to_page(__pa_symbol(addr)); - else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) - page = vmalloc_to_page(addr); - else + if (core_kernel_text(uintaddr) || is_kernel_exittext(uintaddr)) { + phys = __pa_symbol(addr); + } else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) { + struct page *page = vmalloc_to_page(addr); + + BUG_ON(!page); + phys = page_to_phys(page) + offset_in_page(addr); + } else { return addr; + } - BUG_ON(!page); - - return (void *)set_fixmap_offset(fixmap, page_to_phys(page) + - offset_in_page(addr)); + return (void *)set_fixmap_offset(fixmap, phys); } static void patch_unmap(int fixmap) From 57f0253bc1538446ee46a4550fe85d91235fb678 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sat, 21 Feb 2026 10:37:31 +0800 Subject: [PATCH 700/712] riscv: make runtime const not usable by modules Similar as commit 284922f4c563 ("x86: uaccess: don't use runtime-const rewriting in modules") does, make riscv's runtime const not usable by modules too, to "make sure this doesn't get forgotten the next time somebody wants to do runtime constant optimizations". The reason is well explained in the above commit: "The runtime-const infrastructure was never designed to handle the modular case, because the constant fixup is only done at boot time for core kernel code." Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260221023731.3476-1-jszhang@kernel.org Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/runtime-const.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/include/asm/runtime-const.h b/arch/riscv/include/asm/runtime-const.h index d766e2b9e6df..900db0a103d0 100644 --- a/arch/riscv/include/asm/runtime-const.h +++ b/arch/riscv/include/asm/runtime-const.h @@ -2,6 +2,10 @@ #ifndef _ASM_RISCV_RUNTIME_CONST_H #define _ASM_RISCV_RUNTIME_CONST_H +#ifdef MODULE + #error "Cannot use runtime-const infrastructure from modules" +#endif + #include #include #include From 3033b2b1e3949274f33a140e2a97571b5a307298 Mon Sep 17 00:00:00 2001 From: Zishun Yi Date: Mon, 23 Mar 2026 00:00:22 +0800 Subject: [PATCH 701/712] riscv: Reset pmm when PR_TAGGED_ADDR_ENABLE is not set In set_tagged_addr_ctrl(), when PR_TAGGED_ADDR_ENABLE is not set, pmlen is correctly set to 0, but it forgets to reset pmm. This results in the CPU pmm state not corresponding to the software pmlen state. Fix this by resetting pmm along with pmlen. Fixes: 2e1743085887 ("riscv: Add support for the tagged address ABI") Signed-off-by: Zishun Yi Reviewed-by: Samuel Holland Link: https://patch.msgid.link/20260322160022.21908-1-vulab@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/process.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index aacb23978f93..5957effab57c 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -347,8 +347,10 @@ long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) if (arg & PR_TAGGED_ADDR_ENABLE && (tagged_addr_disabled || !pmlen)) return -EINVAL; - if (!(arg & PR_TAGGED_ADDR_ENABLE)) + if (!(arg & PR_TAGGED_ADDR_ENABLE)) { pmlen = PMLEN_0; + pmm = ENVCFG_PMM_PMLEN_0; + } if (mmap_write_lock_killable(mm)) return -EINTR; From 87ad7cc9aa7f0a202189640c5015aa985e7e8f3b Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Thu, 2 Apr 2026 17:18:03 -0600 Subject: [PATCH 702/712] riscv: use _BITUL macro rather than BIT() in ptrace uapi and kselftests Fix the build of non-kernel code that includes the RISC-V ptrace uapi header, and the RISC-V validate_v_ptrace.c kselftest, by using the _BITUL() macro rather than BIT(). BIT() is not available outside the kernel. Based on patches and comments from Charlie Jenkins, Michael Neuling, and Andreas Schwab. Fixes: 30eb191c895b ("selftests: riscv: verify ptrace rejects invalid vector csr inputs") Fixes: 2af7c9cf021c ("riscv/ptrace: expose riscv CFI status and state via ptrace and in core files") Cc: Andreas Schwab Cc: Michael Neuling Cc: Charlie Jenkins Link: https://patch.msgid.link/20260330024248.449292-1-mikey@neuling.org Link: https://lore.kernel.org/linux-riscv/20260309-fix_selftests-v2-1-9d5a553a531e@gmail.com/ Link: https://lore.kernel.org/linux-riscv/20260309-fix_selftests-v2-3-9d5a553a531e@gmail.com/ Signed-off-by: Paul Walmsley --- arch/riscv/include/uapi/asm/ptrace.h | 13 +++++++------ .../selftests/riscv/vector/validate_v_ptrace.c | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 18988a5f1a63..70a74adad914 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -9,6 +9,7 @@ #ifndef __ASSEMBLER__ #include +#include #define PTRACE_GETFDPIC 33 @@ -138,12 +139,12 @@ struct __sc_riscv_cfi_state { #define PTRACE_CFI_SS_LOCK_BIT 4 #define PTRACE_CFI_SS_PTR_BIT 5 -#define PTRACE_CFI_LP_EN_STATE BIT(PTRACE_CFI_LP_EN_BIT) -#define PTRACE_CFI_LP_LOCK_STATE BIT(PTRACE_CFI_LP_LOCK_BIT) -#define PTRACE_CFI_ELP_STATE BIT(PTRACE_CFI_ELP_BIT) -#define PTRACE_CFI_SS_EN_STATE BIT(PTRACE_CFI_SS_EN_BIT) -#define PTRACE_CFI_SS_LOCK_STATE BIT(PTRACE_CFI_SS_LOCK_BIT) -#define PTRACE_CFI_SS_PTR_STATE BIT(PTRACE_CFI_SS_PTR_BIT) +#define PTRACE_CFI_LP_EN_STATE _BITUL(PTRACE_CFI_LP_EN_BIT) +#define PTRACE_CFI_LP_LOCK_STATE _BITUL(PTRACE_CFI_LP_LOCK_BIT) +#define PTRACE_CFI_ELP_STATE _BITUL(PTRACE_CFI_ELP_BIT) +#define PTRACE_CFI_SS_EN_STATE _BITUL(PTRACE_CFI_SS_EN_BIT) +#define PTRACE_CFI_SS_LOCK_STATE _BITUL(PTRACE_CFI_SS_LOCK_BIT) +#define PTRACE_CFI_SS_PTR_STATE _BITUL(PTRACE_CFI_SS_PTR_BIT) #define PRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_LP_EN_STATE | \ PTRACE_CFI_LP_LOCK_STATE | \ diff --git a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c index 3589549f7228..7ae6fede496f 100644 --- a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c +++ b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c @@ -346,8 +346,8 @@ FIXTURE_TEARDOWN(v_csr_invalid) { } -#define VECTOR_1_0 BIT(0) -#define XTHEAD_VECTOR_0_7 BIT(1) +#define VECTOR_1_0 _BITUL(0) +#define XTHEAD_VECTOR_0_7 _BITUL(1) #define vector_test(x) ((x) & VECTOR_1_0) #define xthead_test(x) ((x) & XTHEAD_VECTOR_0_7) From 511361fe7a8856e2f415010942808c237a1b8061 Mon Sep 17 00:00:00 2001 From: Charlie Jenkins Date: Mon, 9 Mar 2026 18:52:11 -0700 Subject: [PATCH 703/712] selftests: riscv: Add braces around EXPECT_EQ() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EXPECT_EQ() expands to multiple lines, breaking up one-line if statements. This issue was not present in the patch on the mailing list but was instead introduced by the maintainer when attempting to fix up checkpatch warnings. Add braces around EXPECT_EQ() to avoid the error even though checkpatch suggests them to be removed: validate_v_ptrace.c:626:17: error: ‘else’ without a previous ‘if’ Fixes: 3789d5eecd5a ("selftests: riscv: verify syscalls discard vector context") Fixes: 30eb191c895b ("selftests: riscv: verify ptrace rejects invalid vector csr inputs") Fixes: 849f05ae1ea6 ("selftests: riscv: verify ptrace accepts valid vector csr values") Signed-off-by: Charlie Jenkins Reviewed-and-tested-by: Sergey Matyukevich Link: https://patch.msgid.link/20260309-fix_selftests-v2-2-9d5a553a531e@gmail.com Signed-off-by: Paul Walmsley --- .../selftests/riscv/vector/validate_v_ptrace.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c index 7ae6fede496f..74b6f6bcf067 100644 --- a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c +++ b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c @@ -290,10 +290,11 @@ TEST(ptrace_v_syscall_clobbering) /* verify initial vsetvli settings */ - if (is_xtheadvector_supported()) + if (is_xtheadvector_supported()) { EXPECT_EQ(5UL, regset_data->vtype); - else + } else { EXPECT_EQ(9UL, regset_data->vtype); + } EXPECT_EQ(regset_data->vlenb, regset_data->vl); EXPECT_EQ(vlenb, regset_data->vlenb); @@ -619,10 +620,11 @@ TEST_F(v_csr_invalid, ptrace_v_invalid_values) /* verify initial vsetvli settings */ - if (is_xtheadvector_supported()) + if (is_xtheadvector_supported()) { EXPECT_EQ(5UL, regset_data->vtype); - else + } else { EXPECT_EQ(9UL, regset_data->vtype); + } EXPECT_EQ(regset_data->vlenb, regset_data->vl); EXPECT_EQ(vlenb, regset_data->vlenb); @@ -827,10 +829,11 @@ TEST_F(v_csr_valid, ptrace_v_valid_values) /* verify initial vsetvli settings */ - if (is_xtheadvector_supported()) + if (is_xtheadvector_supported()) { EXPECT_EQ(5UL, regset_data->vtype); - else + } else { EXPECT_EQ(9UL, regset_data->vtype); + } EXPECT_EQ(regset_data->vlenb, regset_data->vl); EXPECT_EQ(vlenb, regset_data->vlenb); From 9156585280f161fc1c3552cf1860559edb2bb7e3 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 3 Mar 2026 11:46:05 +0530 Subject: [PATCH 704/712] ACPI: RIMT: Add dependency between iommu and devices EPROBE_DEFER ensures IOMMU devices are probed before the devices that depend on them. During shutdown, however, the IOMMU may be removed first, leading to issues. To avoid this, a device link is added which enforces the correct removal order. Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT") Signed-off-by: Sunil V L Link: https://patch.msgid.link/20260303061605.722949-1-sunilvl@oss.qualcomm.com Signed-off-by: Paul Walmsley --- drivers/acpi/riscv/rimt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c index 229c4a0d47a3..906282b0e63c 100644 --- a/drivers/acpi/riscv/rimt.c +++ b/drivers/acpi/riscv/rimt.c @@ -263,6 +263,13 @@ static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 if (!rimt_fwnode) return -EPROBE_DEFER; + /* + * EPROBE_DEFER ensures IOMMU is probed before the devices that + * depend on them. During shutdown, however, the IOMMU may be removed + * first, leading to issues. To avoid this, a device link is added + * which enforces the correct removal order. + */ + device_link_add(dev, rimt_fwnode->dev, DL_FLAG_AUTOREMOVE_CONSUMER); return acpi_iommu_fwspec_init(dev, deviceid, rimt_fwnode); } From 5401b9adebc9e5f68df58226f51493ef0e6ceb4d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 5 Apr 2026 12:42:25 -0700 Subject: [PATCH 705/712] i915: don't use a vma that didn't match the context VM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In eb_lookup_vma(), the code checks that the context vm matches before incrementing the i915 vma usage count, but for the non-matching case it didn't clear the non-matching vma pointer, so it would then mistakenly be returned, causing potential UaF and refcount issues. Reported-by: Yassine Mounir Suggested-by: Ville Syrjälä Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index e7918f896a26..942f4eed817f 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -898,6 +898,8 @@ static struct i915_vma *eb_lookup_vma(struct i915_execbuffer *eb, u32 handle) vma = radix_tree_lookup(&eb->gem_context->handles_vma, handle); if (likely(vma && vma->vm == vm)) vma = i915_vma_tryget(vma); + else + vma = NULL; rcu_read_unlock(); if (likely(vma)) return vma; From 591cd656a1bf5ea94a222af5ef2ee76df029c1d2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 5 Apr 2026 15:26:23 -0700 Subject: [PATCH 706/712] Linux 7.0-rc7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6b1d9fb1a6b4..4f54c5685638 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 0 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Baby Opossum Posse # *DOCUMENTATION* From 2232ba9c7931d5c1061f7f4e897b944ea39c3aa9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Feb 2026 12:06:18 +1000 Subject: [PATCH 707/712] mm: add gpu active/reclaim per-node stat counters (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While discussing memcg intergration with gpu memory allocations, it was pointed out that there was no numa/system counters for GPU memory allocations. With more integrated memory GPU server systems turning up, and more requirements for memory tracking it seems we should start closing the gap. Add two counters to track GPU per-node system memory allocations. The first is currently allocated to GPU objects, and the second is for memory that is stored in GPU page pools that can be reclaimed, by the shrinker. Cc: Christian Koenig Cc: Matthew Brost Cc: Johannes Weiner Cc: linux-mm@kvack.org Cc: Andrew Morton Acked-by: Zi Yan Acked-by: Shakeel Butt Acked-by: Andrew Morton Acked-by: Christian König Signed-off-by: Dave Airlie --- Documentation/filesystems/proc.rst | 8 ++++++++ drivers/base/node.c | 5 +++++ fs/proc/meminfo.c | 6 ++++++ include/linux/mmzone.h | 2 ++ mm/show_mem.c | 6 +++++- mm/vmstat.c | 2 ++ 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index b0c0d1b45b99..3acfdf785465 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -1089,6 +1089,8 @@ Example output. You may not have all of these fields. CmaFree: 0 kB Unaccepted: 0 kB Balloon: 0 kB + GPUActive: 0 kB + GPUReclaim: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 @@ -1269,6 +1271,12 @@ Unaccepted Memory that has not been accepted by the guest Balloon Memory returned to Host by VM Balloon Drivers +GPUActive + System memory allocated to active GPU objects +GPUReclaim + System memory stored in GPU pools for reuse. This memory is not + counted in GPUActive. It is shrinker reclaimable memory kept in a reuse + pool because it has non-standard page table attributes, like WC or UC. HugePages_Total, HugePages_Free, HugePages_Rsvd, HugePages_Surp, Hugepagesize, Hugetlb See Documentation/admin-guide/mm/hugetlbpage.rst. DirectMap4k, DirectMap2M, DirectMap1G diff --git a/drivers/base/node.c b/drivers/base/node.c index d7647d077b66..126f66aa2c3e 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -523,6 +523,8 @@ static ssize_t node_read_meminfo(struct device *dev, #ifdef CONFIG_UNACCEPTED_MEMORY "Node %d Unaccepted: %8lu kB\n" #endif + "Node %d GPUActive: %8lu kB\n" + "Node %d GPUReclaim: %8lu kB\n" , nid, K(node_page_state(pgdat, NR_FILE_DIRTY)), nid, K(node_page_state(pgdat, NR_WRITEBACK)), @@ -556,6 +558,9 @@ static ssize_t node_read_meminfo(struct device *dev, , nid, K(sum_zone_node_page_state(nid, NR_UNACCEPTED)) #endif + , + nid, K(node_page_state(pgdat, NR_GPU_ACTIVE)), + nid, K(node_page_state(pgdat, NR_GPU_RECLAIM)) ); len += hugetlb_report_node_meminfo(buf, len, nid); return len; diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index a458f1e112fd..65ba49ec3a63 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -163,6 +163,12 @@ static int meminfo_proc_show(struct seq_file *m, void *v) show_val_kb(m, "Balloon: ", global_node_page_state(NR_BALLOON_PAGES)); + show_val_kb(m, "GPUActive: ", + global_node_page_state(NR_GPU_ACTIVE)); + + show_val_kb(m, "GPUReclaim: ", + global_node_page_state(NR_GPU_RECLAIM)); + hugetlb_report_meminfo(m); arch_report_meminfo(m); diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 3e51190a55e4..841b40031833 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -260,6 +260,8 @@ enum node_stat_item { #endif NR_BALLOON_PAGES, NR_KERNEL_FILE_PAGES, + NR_GPU_ACTIVE, /* Pages assigned to GPU objects */ + NR_GPU_RECLAIM, /* Pages in shrinkable GPU pools */ NR_VM_NODE_STAT_ITEMS }; diff --git a/mm/show_mem.c b/mm/show_mem.c index 24078ac3e6bc..43aca5a2ac99 100644 --- a/mm/show_mem.c +++ b/mm/show_mem.c @@ -254,6 +254,8 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z " sec_pagetables:%lukB" " all_unreclaimable? %s" " Balloon:%lukB" + " gpu_active:%lukB" + " gpu_reclaim:%lukB" "\n", pgdat->node_id, K(node_page_state(pgdat, NR_ACTIVE_ANON)), @@ -279,7 +281,9 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z K(node_page_state(pgdat, NR_PAGETABLE)), K(node_page_state(pgdat, NR_SECONDARY_PAGETABLE)), str_yes_no(kswapd_test_hopeless(pgdat)), - K(node_page_state(pgdat, NR_BALLOON_PAGES))); + K(node_page_state(pgdat, NR_BALLOON_PAGES)), + K(node_page_state(pgdat, NR_GPU_ACTIVE)), + K(node_page_state(pgdat, NR_GPU_RECLAIM))); } for_each_populated_zone(zone) { diff --git a/mm/vmstat.c b/mm/vmstat.c index 86b14b0f77b5..ac9affbe48b7 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1281,6 +1281,8 @@ const char * const vmstat_text[] = { #endif [I(NR_BALLOON_PAGES)] = "nr_balloon_pages", [I(NR_KERNEL_FILE_PAGES)] = "nr_kernel_file_pages", + [I(NR_GPU_ACTIVE)] = "nr_gpu_active", + [I(NR_GPU_RECLAIM)] = "nr_gpu_reclaim", #undef I /* system-wide enum vm_stat_item counters */ From ae80122f3896c88884841520c983a6e8551da448 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Feb 2026 12:06:19 +1000 Subject: [PATCH 708/712] drm/ttm: use gpu mm stats to track gpu memory allocations. (v4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses the newly introduced per-node gpu tracking stats, to track GPU memory allocated via TTM and reclaimable memory in the TTM page pools. These stats will be useful later for system information and later when mem cgroups are integrated. Cc: Christian Koenig Cc: Matthew Brost Cc: Johannes Weiner Cc: linux-mm@kvack.org Cc: Andrew Morton Acked-by: Christian König Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_pool.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index aa41099c5ecf..55d23b9a76a4 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -159,8 +159,10 @@ static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags, if (!ttm_pool_uses_dma_alloc(pool)) { p = alloc_pages_node(pool->nid, gfp_flags, order); - if (p) + if (p) { p->private = order; + mod_lruvec_page_state(p, NR_GPU_ACTIVE, 1 << order); + } return p; } @@ -195,7 +197,7 @@ static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags, /* Reset the caching and pages of size 1 << order */ static void ttm_pool_free_page(struct ttm_pool *pool, enum ttm_caching caching, - unsigned int order, struct page *p) + unsigned int order, struct page *p, bool reclaim) { unsigned long attr = DMA_ATTR_FORCE_CONTIGUOUS; struct ttm_pool_dma *dma; @@ -210,6 +212,8 @@ static void ttm_pool_free_page(struct ttm_pool *pool, enum ttm_caching caching, #endif if (!pool || !ttm_pool_uses_dma_alloc(pool)) { + mod_lruvec_page_state(p, reclaim ? NR_GPU_RECLAIM : NR_GPU_ACTIVE, + -(1 << order)); __free_pages(p, order); return; } @@ -297,6 +301,9 @@ static void ttm_pool_type_give(struct ttm_pool_type *pt, struct page *p) list_add(&p->lru, &pt->pages); spin_unlock(&pt->lock); atomic_long_add(1 << pt->order, &allocated_pages); + + mod_lruvec_page_state(p, NR_GPU_ACTIVE, -num_pages); + mod_lruvec_page_state(p, NR_GPU_RECLAIM, num_pages); } /* Take pages from a specific pool_type, return NULL when nothing available */ @@ -308,6 +315,8 @@ static struct page *ttm_pool_type_take(struct ttm_pool_type *pt) p = list_first_entry_or_null(&pt->pages, typeof(*p), lru); if (p) { atomic_long_sub(1 << pt->order, &allocated_pages); + mod_lruvec_page_state(p, NR_GPU_ACTIVE, (1 << pt->order)); + mod_lruvec_page_state(p, NR_GPU_RECLAIM, -(1 << pt->order)); list_del(&p->lru); } spin_unlock(&pt->lock); @@ -340,7 +349,7 @@ static void ttm_pool_type_fini(struct ttm_pool_type *pt) spin_unlock(&shrinker_lock); while ((p = ttm_pool_type_take(pt))) - ttm_pool_free_page(pt->pool, pt->caching, pt->order, p); + ttm_pool_free_page(pt->pool, pt->caching, pt->order, p, true); } /* Return the pool_type to use for the given caching and order */ @@ -392,7 +401,7 @@ static unsigned int ttm_pool_shrink(void) p = ttm_pool_type_take(pt); if (p) { - ttm_pool_free_page(pt->pool, pt->caching, pt->order, p); + ttm_pool_free_page(pt->pool, pt->caching, pt->order, p, true); num_pages = 1 << pt->order; } else { num_pages = 0; @@ -484,7 +493,7 @@ static pgoff_t ttm_pool_unmap_and_free(struct ttm_pool *pool, struct page *page, if (pt) ttm_pool_type_give(pt, page); else - ttm_pool_free_page(pool, caching, order, page); + ttm_pool_free_page(pool, caching, order, page, false); return nr; } @@ -792,7 +801,7 @@ static int __ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt, return 0; error_free_page: - ttm_pool_free_page(pool, page_caching, order, p); + ttm_pool_free_page(pool, page_caching, order, p, false); error_free_all: if (tt->restore) From 444e2a19d7fd1f08044a68fbd8b37721c6531565 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Feb 2026 12:06:20 +1000 Subject: [PATCH 709/712] ttm/pool: port to list_lru. (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an initial port of the TTM pools for write combined and uncached pages to use the list_lru. This makes the pool's more NUMA aware and avoids needing separate NUMA pools (later commit enables this). Cc: Christian Koenig Cc: Johannes Weiner Cc: Dave Chinner Reviewed-by: Christian König Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/tests/ttm_device_test.c | 2 +- drivers/gpu/drm/ttm/tests/ttm_pool_test.c | 32 +++---- drivers/gpu/drm/ttm/ttm_pool.c | 92 ++++++++++++++------- include/drm/ttm/ttm_pool.h | 7 +- mm/list_lru.c | 1 + 5 files changed, 84 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/ttm/tests/ttm_device_test.c b/drivers/gpu/drm/ttm/tests/ttm_device_test.c index 2d55ad34fe48..db4b4a09a73f 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_device_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_device_test.c @@ -176,7 +176,7 @@ static void ttm_device_init_pools(struct kunit *test) if (ttm_pool_uses_dma_alloc(pool)) KUNIT_ASSERT_FALSE(test, - list_empty(&pt.pages)); + !list_lru_count(&pt.pages)); } } } diff --git a/drivers/gpu/drm/ttm/tests/ttm_pool_test.c b/drivers/gpu/drm/ttm/tests/ttm_pool_test.c index 11c92bd75779..01197014b83f 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_pool_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_pool_test.c @@ -248,7 +248,7 @@ static void ttm_pool_alloc_order_caching_match(struct kunit *test) pool = ttm_pool_pre_populated(test, size, caching); pt = &pool->caching[caching].orders[order]; - KUNIT_ASSERT_FALSE(test, list_empty(&pt->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt->pages)); tt = ttm_tt_kunit_init(test, 0, caching, size); KUNIT_ASSERT_NOT_NULL(test, tt); @@ -256,7 +256,7 @@ static void ttm_pool_alloc_order_caching_match(struct kunit *test) err = ttm_pool_alloc(pool, tt, &simple_ctx); KUNIT_ASSERT_EQ(test, err, 0); - KUNIT_ASSERT_TRUE(test, list_empty(&pt->pages)); + KUNIT_ASSERT_TRUE(test, !list_lru_count(&pt->pages)); ttm_pool_free(pool, tt); ttm_tt_fini(tt); @@ -282,8 +282,8 @@ static void ttm_pool_alloc_caching_mismatch(struct kunit *test) tt = ttm_tt_kunit_init(test, 0, tt_caching, size); KUNIT_ASSERT_NOT_NULL(test, tt); - KUNIT_ASSERT_FALSE(test, list_empty(&pt_pool->pages)); - KUNIT_ASSERT_TRUE(test, list_empty(&pt_tt->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt_pool->pages)); + KUNIT_ASSERT_TRUE(test, !list_lru_count(&pt_tt->pages)); err = ttm_pool_alloc(pool, tt, &simple_ctx); KUNIT_ASSERT_EQ(test, err, 0); @@ -291,8 +291,8 @@ static void ttm_pool_alloc_caching_mismatch(struct kunit *test) ttm_pool_free(pool, tt); ttm_tt_fini(tt); - KUNIT_ASSERT_FALSE(test, list_empty(&pt_pool->pages)); - KUNIT_ASSERT_FALSE(test, list_empty(&pt_tt->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt_pool->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt_tt->pages)); ttm_pool_fini(pool); } @@ -316,8 +316,8 @@ static void ttm_pool_alloc_order_mismatch(struct kunit *test) tt = ttm_tt_kunit_init(test, 0, caching, snd_size); KUNIT_ASSERT_NOT_NULL(test, tt); - KUNIT_ASSERT_FALSE(test, list_empty(&pt_pool->pages)); - KUNIT_ASSERT_TRUE(test, list_empty(&pt_tt->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt_pool->pages)); + KUNIT_ASSERT_TRUE(test, !list_lru_count(&pt_tt->pages)); err = ttm_pool_alloc(pool, tt, &simple_ctx); KUNIT_ASSERT_EQ(test, err, 0); @@ -325,8 +325,8 @@ static void ttm_pool_alloc_order_mismatch(struct kunit *test) ttm_pool_free(pool, tt); ttm_tt_fini(tt); - KUNIT_ASSERT_FALSE(test, list_empty(&pt_pool->pages)); - KUNIT_ASSERT_FALSE(test, list_empty(&pt_tt->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt_pool->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt_tt->pages)); ttm_pool_fini(pool); } @@ -352,12 +352,12 @@ static void ttm_pool_free_dma_alloc(struct kunit *test) ttm_pool_alloc(pool, tt, &simple_ctx); pt = &pool->caching[caching].orders[order]; - KUNIT_ASSERT_TRUE(test, list_empty(&pt->pages)); + KUNIT_ASSERT_TRUE(test, !list_lru_count(&pt->pages)); ttm_pool_free(pool, tt); ttm_tt_fini(tt); - KUNIT_ASSERT_FALSE(test, list_empty(&pt->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt->pages)); ttm_pool_fini(pool); } @@ -383,12 +383,12 @@ static void ttm_pool_free_no_dma_alloc(struct kunit *test) ttm_pool_alloc(pool, tt, &simple_ctx); pt = &pool->caching[caching].orders[order]; - KUNIT_ASSERT_TRUE(test, list_is_singular(&pt->pages)); + KUNIT_ASSERT_TRUE(test, list_lru_count(&pt->pages) == 1); ttm_pool_free(pool, tt); ttm_tt_fini(tt); - KUNIT_ASSERT_TRUE(test, list_is_singular(&pt->pages)); + KUNIT_ASSERT_TRUE(test, list_lru_count(&pt->pages) == 1); ttm_pool_fini(pool); } @@ -404,11 +404,11 @@ static void ttm_pool_fini_basic(struct kunit *test) pool = ttm_pool_pre_populated(test, size, caching); pt = &pool->caching[caching].orders[order]; - KUNIT_ASSERT_FALSE(test, list_empty(&pt->pages)); + KUNIT_ASSERT_FALSE(test, !list_lru_count(&pt->pages)); ttm_pool_fini(pool); - KUNIT_ASSERT_TRUE(test, list_empty(&pt->pages)); + KUNIT_ASSERT_TRUE(test, !list_lru_count(&pt->pages)); } static struct kunit_case ttm_pool_test_cases[] = { diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 55d23b9a76a4..b2e3d48e237b 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -132,6 +132,16 @@ static struct list_head shrinker_list; static struct shrinker *mm_shrinker; static DECLARE_RWSEM(pool_shrink_rwsem); +static int ttm_pool_nid(struct ttm_pool *pool) +{ + int nid = NUMA_NO_NODE; + if (pool) + nid = pool->nid; + if (nid == NUMA_NO_NODE) + nid = numa_node_id(); + return nid; +} + /* Allocate pages of size 1 << order with the given gfp_flags */ static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags, unsigned int order) @@ -297,30 +307,41 @@ static void ttm_pool_type_give(struct ttm_pool_type *pt, struct page *p) clear_page(page_address(p + i)); } - spin_lock(&pt->lock); - list_add(&p->lru, &pt->pages); - spin_unlock(&pt->lock); + INIT_LIST_HEAD(&p->lru); + rcu_read_lock(); + list_lru_add(&pt->pages, &p->lru, page_to_nid(p), NULL); + rcu_read_unlock(); atomic_long_add(1 << pt->order, &allocated_pages); mod_lruvec_page_state(p, NR_GPU_ACTIVE, -num_pages); mod_lruvec_page_state(p, NR_GPU_RECLAIM, num_pages); } -/* Take pages from a specific pool_type, return NULL when nothing available */ -static struct page *ttm_pool_type_take(struct ttm_pool_type *pt) +static enum lru_status take_one_from_lru(struct list_head *item, + struct list_lru_one *list, + void *cb_arg) { - struct page *p; + struct page **out_page = cb_arg; + struct page *p = container_of(item, struct page, lru); + list_lru_isolate(list, item); - spin_lock(&pt->lock); - p = list_first_entry_or_null(&pt->pages, typeof(*p), lru); - if (p) { + *out_page = p; + return LRU_REMOVED; +} + +/* Take pages from a specific pool_type, return NULL when nothing available */ +static struct page *ttm_pool_type_take(struct ttm_pool_type *pt, int nid) +{ + int ret; + struct page *p = NULL; + unsigned long nr_to_walk = 1; + + ret = list_lru_walk_node(&pt->pages, nid, take_one_from_lru, (void *)&p, &nr_to_walk); + if (ret == 1 && p) { atomic_long_sub(1 << pt->order, &allocated_pages); mod_lruvec_page_state(p, NR_GPU_ACTIVE, (1 << pt->order)); mod_lruvec_page_state(p, NR_GPU_RECLAIM, -(1 << pt->order)); - list_del(&p->lru); } - spin_unlock(&pt->lock); - return p; } @@ -331,25 +352,47 @@ static void ttm_pool_type_init(struct ttm_pool_type *pt, struct ttm_pool *pool, pt->pool = pool; pt->caching = caching; pt->order = order; - spin_lock_init(&pt->lock); - INIT_LIST_HEAD(&pt->pages); + list_lru_init(&pt->pages); spin_lock(&shrinker_lock); list_add_tail(&pt->shrinker_list, &shrinker_list); spin_unlock(&shrinker_lock); } +static enum lru_status pool_move_to_dispose_list(struct list_head *item, + struct list_lru_one *list, + void *cb_arg) +{ + struct list_head *dispose = cb_arg; + + list_lru_isolate_move(list, item, dispose); + + return LRU_REMOVED; +} + +static void ttm_pool_dispose_list(struct ttm_pool_type *pt, + struct list_head *dispose) +{ + while (!list_empty(dispose)) { + struct page *p; + p = list_first_entry(dispose, struct page, lru); + list_del_init(&p->lru); + atomic_long_sub(1 << pt->order, &allocated_pages); + ttm_pool_free_page(pt->pool, pt->caching, pt->order, p, true); + } +} + /* Remove a pool_type from the global shrinker list and free all pages */ static void ttm_pool_type_fini(struct ttm_pool_type *pt) { - struct page *p; + LIST_HEAD(dispose); spin_lock(&shrinker_lock); list_del(&pt->shrinker_list); spin_unlock(&shrinker_lock); - while ((p = ttm_pool_type_take(pt))) - ttm_pool_free_page(pt->pool, pt->caching, pt->order, p, true); + list_lru_walk(&pt->pages, pool_move_to_dispose_list, &dispose, LONG_MAX); + ttm_pool_dispose_list(pt, &dispose); } /* Return the pool_type to use for the given caching and order */ @@ -399,7 +442,7 @@ static unsigned int ttm_pool_shrink(void) list_move_tail(&pt->shrinker_list, &shrinker_list); spin_unlock(&shrinker_lock); - p = ttm_pool_type_take(pt); + p = ttm_pool_type_take(pt, ttm_pool_nid(pt->pool)); if (p) { ttm_pool_free_page(pt->pool, pt->caching, pt->order, p, true); num_pages = 1 << pt->order; @@ -756,7 +799,7 @@ static int __ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt, p = NULL; pt = ttm_pool_select_type(pool, page_caching, order); if (pt && allow_pools) - p = ttm_pool_type_take(pt); + p = ttm_pool_type_take(pt, ttm_pool_nid(pool)); /* * If that fails or previously failed, allocate from system. * Note that this also disallows additional pool allocations using @@ -1185,16 +1228,7 @@ static unsigned long ttm_pool_shrinker_count(struct shrinker *shrink, /* Count the number of pages available in a pool_type */ static unsigned int ttm_pool_type_count(struct ttm_pool_type *pt) { - unsigned int count = 0; - struct page *p; - - spin_lock(&pt->lock); - /* Only used for debugfs, the overhead doesn't matter */ - list_for_each_entry(p, &pt->pages, lru) - ++count; - spin_unlock(&pt->lock); - - return count; + return list_lru_count(&pt->pages); } /* Print a nice header for the order */ diff --git a/include/drm/ttm/ttm_pool.h b/include/drm/ttm/ttm_pool.h index 233581670e78..26ee592e1994 100644 --- a/include/drm/ttm/ttm_pool.h +++ b/include/drm/ttm/ttm_pool.h @@ -29,6 +29,7 @@ #include #include #include +#include #include struct device; @@ -45,8 +46,7 @@ struct ttm_tt; * @order: the allocation order our pages have * @caching: the caching type our pages have * @shrinker_list: our place on the global shrinker list - * @lock: protection of the page list - * @pages: the list of pages in the pool + * @pages: the lru_list of pages in the pool */ struct ttm_pool_type { struct ttm_pool *pool; @@ -55,8 +55,7 @@ struct ttm_pool_type { struct list_head shrinker_list; - spinlock_t lock; - struct list_head pages; + struct list_lru pages; }; /** diff --git a/mm/list_lru.c b/mm/list_lru.c index 26463ae29c64..dd29bcf8eb5f 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -179,6 +179,7 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid, unlock_list_lru(l, false); return false; } +EXPORT_SYMBOL_GPL(list_lru_add); bool list_lru_add_obj(struct list_lru *lru, struct list_head *item) { From c21066ef697bbecc49dd7888bf8f4e7c684a8ef9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Feb 2026 12:06:21 +1000 Subject: [PATCH 710/712] ttm/pool: drop numa specific pools The list_lru will now handle numa for us, so no need to keep separate pool types for it. Just consolidate into the global ones. This adds a debugfs change to avoid dumping non-existant orders due to this change. Cc: Christian Koenig Cc: Johannes Weiner Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_pool.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index b2e3d48e237b..f50d9ff08193 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -406,17 +406,11 @@ static struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool, #ifdef CONFIG_X86 switch (caching) { case ttm_write_combined: - if (pool->nid != NUMA_NO_NODE) - return &pool->caching[caching].orders[order]; - if (ttm_pool_uses_dma32(pool)) return &global_dma32_write_combined[order]; return &global_write_combined[order]; case ttm_uncached: - if (pool->nid != NUMA_NO_NODE) - return &pool->caching[caching].orders[order]; - if (ttm_pool_uses_dma32(pool)) return &global_dma32_uncached[order]; @@ -1294,7 +1288,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m) { unsigned int i; - if (!ttm_pool_uses_dma_alloc(pool) && pool->nid == NUMA_NO_NODE) { + if (!ttm_pool_uses_dma_alloc(pool)) { seq_puts(m, "unused\n"); return 0; } @@ -1305,10 +1299,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m) for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) { if (!ttm_pool_select_type(pool, i, 0)) continue; - if (ttm_pool_uses_dma_alloc(pool)) - seq_puts(m, "DMA "); - else - seq_printf(m, "N%d ", pool->nid); + seq_puts(m, "DMA "); switch (i) { case ttm_cached: seq_puts(m, "\t:"); From 0180d6ff34e6460b79c75fc62d1915d115a84597 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Feb 2026 12:06:22 +1000 Subject: [PATCH 711/712] ttm/pool: make pool shrinker NUMA aware (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enable NUMA awareness for the shrinker on the ttm pools. Cc: Christian Koenig Cc: Dave Chinner Reviewed-by: Christian König Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_pool.c | 38 +++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index f50d9ff08193..1bcb60f141f8 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -423,12 +423,12 @@ static struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool, return NULL; } -/* Free pages using the global shrinker list */ -static unsigned int ttm_pool_shrink(void) +/* Free pages using the per-node shrinker list */ +static unsigned int ttm_pool_shrink(int nid, unsigned long num_to_free) { + LIST_HEAD(dispose); struct ttm_pool_type *pt; unsigned int num_pages; - struct page *p; down_read(&pool_shrink_rwsem); spin_lock(&shrinker_lock); @@ -436,13 +436,10 @@ static unsigned int ttm_pool_shrink(void) list_move_tail(&pt->shrinker_list, &shrinker_list); spin_unlock(&shrinker_lock); - p = ttm_pool_type_take(pt, ttm_pool_nid(pt->pool)); - if (p) { - ttm_pool_free_page(pt->pool, pt->caching, pt->order, p, true); - num_pages = 1 << pt->order; - } else { - num_pages = 0; - } + num_pages = list_lru_walk_node(&pt->pages, nid, pool_move_to_dispose_list, &dispose, &num_to_free); + num_pages *= 1 << pt->order; + + ttm_pool_dispose_list(pt, &dispose); up_read(&pool_shrink_rwsem); return num_pages; @@ -794,6 +791,7 @@ static int __ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt, pt = ttm_pool_select_type(pool, page_caching, order); if (pt && allow_pools) p = ttm_pool_type_take(pt, ttm_pool_nid(pool)); + /* * If that fails or previously failed, allocate from system. * Note that this also disallows additional pool allocations using @@ -944,8 +942,10 @@ void ttm_pool_free(struct ttm_pool *pool, struct ttm_tt *tt) { ttm_pool_free_range(pool, tt, tt->caching, 0, tt->num_pages); - while (atomic_long_read(&allocated_pages) > page_pool_size) - ttm_pool_shrink(); + while (atomic_long_read(&allocated_pages) > page_pool_size) { + unsigned long diff = atomic_long_read(&allocated_pages) - page_pool_size; + ttm_pool_shrink(ttm_pool_nid(pool), diff); + } } EXPORT_SYMBOL(ttm_pool_free); @@ -1200,7 +1200,7 @@ static unsigned long ttm_pool_shrinker_scan(struct shrinker *shrink, unsigned long num_freed = 0; do - num_freed += ttm_pool_shrink(); + num_freed += ttm_pool_shrink(sc->nid, sc->nr_to_scan); while (num_freed < sc->nr_to_scan && atomic_long_read(&allocated_pages)); @@ -1328,11 +1328,15 @@ static int ttm_pool_debugfs_shrink_show(struct seq_file *m, void *data) .nr_to_scan = TTM_SHRINKER_BATCH, }; unsigned long count; + int nid; fs_reclaim_acquire(GFP_KERNEL); - count = ttm_pool_shrinker_count(mm_shrinker, &sc); - seq_printf(m, "%lu/%lu\n", count, - ttm_pool_shrinker_scan(mm_shrinker, &sc)); + for_each_node(nid) { + sc.nid = nid; + count = ttm_pool_shrinker_count(mm_shrinker, &sc); + seq_printf(m, "%d: %lu/%lu\n", nid, count, + ttm_pool_shrinker_scan(mm_shrinker, &sc)); + } fs_reclaim_release(GFP_KERNEL); return 0; @@ -1380,7 +1384,7 @@ int ttm_pool_mgr_init(unsigned long num_pages) #endif #endif - mm_shrinker = shrinker_alloc(0, "drm-ttm_pool"); + mm_shrinker = shrinker_alloc(SHRINKER_NUMA_AWARE, "drm-ttm_pool"); if (!mm_shrinker) return -ENOMEM; From 4516432284e1b2ad9e70de8067f779c9c1072189 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 24 Feb 2026 12:06:23 +1000 Subject: [PATCH 712/712] ttm/pool: track allocated_pages per numa node. This gets the memory sizes from the nodes and stores the limit as 50% of those. I think eventually we should drop the limits once we have memcg aware shrinking, but this should be more NUMA friendly, and I think seems like what people would prefer to happen on NUMA aware systems. Cc: Christian Koenig Signed-off-by: Dave Airlie --- drivers/gpu/drm/ttm/ttm_pool.c | 62 ++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 1bcb60f141f8..26a3689e5fd9 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -116,10 +116,11 @@ struct ttm_pool_tt_restore { static unsigned long page_pool_size; -MODULE_PARM_DESC(page_pool_size, "Number of pages in the WC/UC/DMA pool"); +MODULE_PARM_DESC(page_pool_size, "Number of pages in the WC/UC/DMA pool per NUMA node"); module_param(page_pool_size, ulong, 0644); -static atomic_long_t allocated_pages; +static unsigned long pool_node_limit[MAX_NUMNODES]; +static atomic_long_t allocated_pages[MAX_NUMNODES]; static struct ttm_pool_type global_write_combined[NR_PAGE_ORDERS]; static struct ttm_pool_type global_uncached[NR_PAGE_ORDERS]; @@ -299,6 +300,7 @@ static void ttm_pool_unmap(struct ttm_pool *pool, dma_addr_t dma_addr, static void ttm_pool_type_give(struct ttm_pool_type *pt, struct page *p) { unsigned int i, num_pages = 1 << pt->order; + int nid = page_to_nid(p); for (i = 0; i < num_pages; ++i) { if (PageHighMem(p)) @@ -309,10 +311,10 @@ static void ttm_pool_type_give(struct ttm_pool_type *pt, struct page *p) INIT_LIST_HEAD(&p->lru); rcu_read_lock(); - list_lru_add(&pt->pages, &p->lru, page_to_nid(p), NULL); + list_lru_add(&pt->pages, &p->lru, nid, NULL); rcu_read_unlock(); - atomic_long_add(1 << pt->order, &allocated_pages); + atomic_long_add(num_pages, &allocated_pages[nid]); mod_lruvec_page_state(p, NR_GPU_ACTIVE, -num_pages); mod_lruvec_page_state(p, NR_GPU_RECLAIM, num_pages); } @@ -338,7 +340,7 @@ static struct page *ttm_pool_type_take(struct ttm_pool_type *pt, int nid) ret = list_lru_walk_node(&pt->pages, nid, take_one_from_lru, (void *)&p, &nr_to_walk); if (ret == 1 && p) { - atomic_long_sub(1 << pt->order, &allocated_pages); + atomic_long_sub(1 << pt->order, &allocated_pages[nid]); mod_lruvec_page_state(p, NR_GPU_ACTIVE, (1 << pt->order)); mod_lruvec_page_state(p, NR_GPU_RECLAIM, -(1 << pt->order)); } @@ -377,7 +379,7 @@ static void ttm_pool_dispose_list(struct ttm_pool_type *pt, struct page *p; p = list_first_entry(dispose, struct page, lru); list_del_init(&p->lru); - atomic_long_sub(1 << pt->order, &allocated_pages); + atomic_long_sub(1 << pt->order, &allocated_pages[page_to_nid(p)]); ttm_pool_free_page(pt->pool, pt->caching, pt->order, p, true); } } @@ -940,11 +942,13 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, */ void ttm_pool_free(struct ttm_pool *pool, struct ttm_tt *tt) { + int nid = ttm_pool_nid(pool); + ttm_pool_free_range(pool, tt, tt->caching, 0, tt->num_pages); - while (atomic_long_read(&allocated_pages) > page_pool_size) { - unsigned long diff = atomic_long_read(&allocated_pages) - page_pool_size; - ttm_pool_shrink(ttm_pool_nid(pool), diff); + while (atomic_long_read(&allocated_pages[nid]) > pool_node_limit[nid]) { + unsigned long diff = atomic_long_read(&allocated_pages[nid]) - pool_node_limit[nid]; + ttm_pool_shrink(nid, diff); } } EXPORT_SYMBOL(ttm_pool_free); @@ -1202,7 +1206,7 @@ static unsigned long ttm_pool_shrinker_scan(struct shrinker *shrink, do num_freed += ttm_pool_shrink(sc->nid, sc->nr_to_scan); while (num_freed < sc->nr_to_scan && - atomic_long_read(&allocated_pages)); + atomic_long_read(&allocated_pages[sc->nid])); sc->nr_scanned = num_freed; @@ -1213,7 +1217,7 @@ static unsigned long ttm_pool_shrinker_scan(struct shrinker *shrink, static unsigned long ttm_pool_shrinker_count(struct shrinker *shrink, struct shrink_control *sc) { - unsigned long num_pages = atomic_long_read(&allocated_pages); + unsigned long num_pages = atomic_long_read(&allocated_pages[sc->nid]); return num_pages ? num_pages : SHRINK_EMPTY; } @@ -1250,8 +1254,12 @@ static void ttm_pool_debugfs_orders(struct ttm_pool_type *pt, /* Dump the total amount of allocated pages */ static void ttm_pool_debugfs_footer(struct seq_file *m) { - seq_printf(m, "\ntotal\t: %8lu of %8lu\n", - atomic_long_read(&allocated_pages), page_pool_size); + int nid; + + for_each_node(nid) { + seq_printf(m, "\ntotal node%d\t: %8lu of %8lu\n", nid, + atomic_long_read(&allocated_pages[nid]), pool_node_limit[nid]); + } } /* Dump the information for the global pools */ @@ -1345,6 +1353,23 @@ DEFINE_SHOW_ATTRIBUTE(ttm_pool_debugfs_shrink); #endif +static inline u64 ttm_get_node_memory_size(int nid) +{ + /* + * This is directly using si_meminfo_node implementation as the + * function is not exported. + */ + int zone_type; + u64 managed_pages = 0; + + pg_data_t *pgdat = NODE_DATA(nid); + + for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) + managed_pages += + zone_managed_pages(&pgdat->node_zones[zone_type]); + return managed_pages * PAGE_SIZE; +} + /** * ttm_pool_mgr_init - Initialize globals * @@ -1356,8 +1381,15 @@ int ttm_pool_mgr_init(unsigned long num_pages) { unsigned int i; - if (!page_pool_size) - page_pool_size = num_pages; + int nid; + for_each_node(nid) { + if (!page_pool_size) { + u64 node_size = ttm_get_node_memory_size(nid); + pool_node_limit[nid] = (node_size >> PAGE_SHIFT) / 2; + } else { + pool_node_limit[nid] = page_pool_size; + } + } spin_lock_init(&shrinker_lock); INIT_LIST_HEAD(&shrinker_list);